Change between many UI frameworks


#1

I want to let the user change theme in my app. Is possible in meteor to selectively select what package to load? or dynamically load js/css/less? Example if they opt to used bootstrap I can just load the js/css/less of bootstrap. If they opt to used materialize I can just load the sets of js/css/less of the framework.


Client-side Template lazy loading
#2

I think the issue might be that the class names and html structures vary between frameworks.
So your templates would all need to be updated too to reflect which framework has been selected.


#3

@cstrat I think you are right. I think there will mo conflect because the will have difference class names. I will try to test it :slight_smile: thanks!


#4

If you’re switching stylesheets, then perhaps

{{#if style1}}
  <link href="style1.css" />
{{else}}
  <link href="style2.css" />
{{/if}}

or

<link href="{{selectedStyle}}" />

?


#5

@cstrat @trusktr I think this will work. How about if user1 is login /home route will be the /user1/home.html if user2 is login /home route is /user2/home.html. Would this possible in meteor?


#6

Anything is possible. The question is “What logic do I need to write to make it happen?”. :smile:

Look at https://github.com/meteorhacks/flow-router and http://docs.meteor.com/#/full/template_dynamic.

If you are using flow-router (for example) then in the action of the router you can set a Session variable that specifies the name of a template to render. You can have a pre-made set of template that users can choose from.

If you want to user to provide his own Spacebars template in by filling out a textarea, then that will be more tricky, but I’m sure it’s doable. You’ll have to save the template in a collection, then you can get that content for the logged in user and use http://docs.meteor.com/#/full/blaze_render to render a template. You’ll have to dig into the non-public API source-code to see how Meteor compiles the text of a template into a Blaze.Template object ( http://docs.meteor.com/#/full/blaze_template, it has a link to the source code ).

Here’s a related question I just asked: [SOLVED] How do we take a string of template code and make a new Blaze.Template with it?


#7

@trusktr thanks! I will try to test this :slight_smile:


#8

The answer to Client-side Template lazy loading might be helpful (when it gets answered).


#9

Am I allowed to paste code here? I have a method, ‘ChangeTheme’, defined on my server side that will return the CSS file to the client for the desired them (Cerulean, Cosmo, Cyborg, etc. from Bootswatch (http://bootswatch.com/)).

Meteor.methods({
    'ChangeTheme': function (themeName) {

        var css = null;

        switch (themeName) {
            case 'cerulean':
                css = Assets.getText('themes/cerulean/bootstrap.css');
                break;

            case 'cosmo':
                css = Assets.getText('themes/cosmo/bootstrap.css');
                break;
            :
            : 
        }

        return css;
    }
});

And on the client side I listen for events on the dropdown menu:

Template.mainScreen.events({
    'click .ceruleanTheme': function () {
        Meteor.call('ChangeTheme', 'cerulean', function (err, result) {
            if (result) {
                changeCSS(result);
            } else if (err) {
                alert(err);
            }
        });
    },

    'click .cosmoTheme': function () {
        Meteor.call('ChangeTheme', 'cosmo', function (err, result) {
            if (result) {
                changeCSS(result);
            } else if (err) {
                alert(err);
            }
        });
    },
    : 
    :
});


function changeCSS(css) {

    // delete any existing ones first
    var cssLinks = document.getElementsByTagName('link');
    for (var i = cssLinks.length - 1; i >= 0; --i) {
        if (cssLinks[i] && cssLinks[i].getAttribute('href') != null) {
            cssLinks[i].parentNode.removeChild(cssLinks[i]);
        }
    }

    // remove the previously created stylesheet (if any)
    var cssNode = document.getElementById('dynamicSheet');

    if (cssNode) {
        cssNode.parentNode.removeChild(cssNode);
    }

    // create style and append to document
    cssNode = document.createElement('style');
    cssNode.type = 'text/css';
    cssNode.rel = 'stylesheet';
    cssNode.media = 'screen';
    cssNode.id = 'dynamicSheet';
    cssNode.innerHTML = css;
    document.getElementsByTagName("head")[0].appendChild(cssNode);
}

For a simple setup, that is, with one CSS file the above might be okay. I haven’t used it in a more complicated setting yet but I’m sure I’ll deal with the headache when I get there.

Instead of reloading the page I decided to use the communications channel between the application and server to serve up the CSS file.