Search code examples
meteormeteor-blazespacebarsflow-router

rendering alternate components under the same flow:router URL


How can I render two different views in a one page app without changing URLs. I'm using meteor with the default blaze as well as the flow:router package. Right now I have it set up like this: routes.js..

FlowRouter.route("/", {
  name: "App.home",
  action() {
    BlazeLayout.render("App_body", {
      main: "App_home",
      mainContent: "calendar"
    });
  }
});

FlowRouter.route("/list", {
  name: "App.list",
  action() {
    BlazeLayout.render("App_body", { main: "App_home", mainContent: "list" });
  }
});

but this way I'm using the url /list and i dont want that. I would like to simply render an alternate component template in the same url. I'm very new to coding so forgive me if this is obvious. Essentially I just want two different view styles: a list and a calendar. So I would like a way to set it up so that a spacebars template can be rendered if a certain button is clicked, and a different one can be rendered instead if another button is clicked.

Thanks so much for any help, i've been at this for a couple of days :)


Solution

  • You can handle this within a single template like so:

    FlowRouter.route('/', {
      name: 'App.home',
      action() {
      BlazeLayout.render('App_body', { main: 'App_home', mainContent: 'ListOrCalendar' });
    }
    

    And then the ListOrCalendar template would look like this:

    {{#if displayList}}
      {{> List}}
    {{else}}
      {{> Calendar}}
    {{/if}}
    <button>Switch</button>
    

    You would set up a ReactiveVar in the ListOrCalendar template:

    Template.ListOrCalendar.onCreated(function() {
      const instance = this;
      instance.displayList = new ReactiveVar(true);
    });
    

    See ReactiveVar explanation here (ignore Session)

    Then you would have a helper which returns the value of your ReactiveVar:

    Template.ListOrCalendar.helpers({
      displayList() {
        const instance = Template.instance();
        return instance.displayList.get();
      }
    });
    

    Finally, you would hook up an event to change the value of displayList to switch between templates:

    Template.ListOrCalendar.events({
      "click button"(event, instance) {
        const displayListCurrent = instance.displayList.get();
        const displayListNew = !displayListCurrent;
        instance.displayList.set(displayListNew);
        // or, more concisely, instance.displayList.set(!instance.displayList.get());
      }
    });
    

    So, in summary:

    • When the template is created, your ReactiveVar is true
      • so your displayList returns true
      • so the #if displayList condition in the template is satisfied
      • and so the List template is displayed
    • When the button is clicked
      • The ReactiveVar is set to false
      • so the displayList helper returns false
      • so the #if displayList condition in the template is not satisfied and it goes to the else statement
      • and so, finally, the Calendar template is displayed
    • When the button is clicked again, the ReactiveVar is toggled back to true, and on we go as above

    This might seem daunting or over-complicated, but there's nothing fancy going on here at all. You'll get used to it pretty quickly