Tabbed UI with Routing


#1

I’ve been using Ember.js in production for 2 months now. One of my requirements is to design a tabbed user interface with twitter bootstrap v3. when you want to redirect to a new route, a new Tab Item should be rendered in the main outlet. of course, each tab pane will have its own controller/view/template etc. objects.

  1. What is the best way to implement this design? I’ve spent some time browsing the web on the subject but haven’t been able to find any useful information, yet.
  2. How do I do garbage collection? suppose we’ve implemented a solution and routes are being dynamically rendered into a Tab Panel. when i close a tab pane, what happens to its controller/view etc. objects? do i have to dispose them and if so, how.

Thanks in advance :thought_balloon:


#2

Simple example that shows the basics of how you might go about implementing this.

Non-singleton controllers (i.e. item controllers within array controllers) and views are disposed of automatically when they are no longer needed. Singleton controllers are long-lived objects that stay in memory (i.e. in the DI container).


#3

Hi, thanks for your answer. I cloned your repository and gave it a try. I’m not sure if this is what I’m looking for. You have an array in your application controller which holds the tab collection. you’ve defined a route in which you extract the selected tab’s ID and then render the appropriate template.

What i need is different. say we have a navbar with links to our application routes. the user clicks on the Messages menu item which is linked to the /#/messages/ route. the route object is executed (App.MessagesRoute) and at this point i need to add a new Tab Item to my tab control and render the corresponding controller/template/view.

I hope I’ve explained my requirement well. perhaps I could incorporate your sample app and achieve my desired result, am I missing something here?

Thanks again


#4

Updated the example, the tabs are now dynamic.

Of course this merely illustrates one possible approach. You’ll have to adapt it to your application.


#5

I have approached this same issue and solved it by using different routes for each tab and just faking the TWBS tabs. Basically, I markup the tabs the way the TWBS CSS expects and make one active based on the route. I never bother to initialize the JS, because I don’t need it.

Ember already provides itself as a mechanism for swapping content in-and-out based on the URL; the TWBS tabbing JS is redundant.


#6

Hi lfridael, thank you for your example, but I’m still confused on how to implement my use-case. I’ll write about the problem below this topic.


#7

Hi atsjj. the problem is that if i use outlets to render my templates then only one template can be visible at a time. but i need to render each route separately and keep them open until closed by the user. suppose we have the following routes:

  • Home
  • Profile
  • Messages

When you go to the Profile route the route object will be executed, you’ll resolve your model(s) and setup the controller etc. then render the template. if i include an outlet in my tab panel markup (the tab-content div) then switching between routes will invalidate the contents of this outlet. but i need to keep the rendered content in the DOM. Is this even possible in Ember.js? visiting each route should add a new item to the Tab Items and render its content in the tab-content div, how can i achieve this in Ember? I hope I’ve explained my scenario clearly.

Thanks


#8

I’ve done this in a very hacky way before, where I override the renderTemplate method in my route and manually wire up my templates to some named outlets with some already resolved model data.

The blessed way to do this kind of thing in Ember is with nested resources.

I’m not sure I’m going to be able to solve this one, but I sure am interested!


#9

Hey @atsjj I’ve implemented a sample and everything’s working the way i expect. I’ll post the complete code here. It’s probably a better idea to create a demo repo on GitHub, I’m on it. stay tuned! :coffee:


#10

Hey guys, I’ve created a demo repo on GitHub. The code is self explanatory but if you needed any help please let me know. Currently the Tabbed UI works fine, you can load modules dynamically with their own context (controller, view etc.) and you can close open tabs which disposes related objects automatically. @atsjj @lfridael There’s a problem in this sample project and I would really appreciate it if you guys could give it a try and help me out. The problem is the fact that controller properties return null after the views have been rendered. I’m assuming this is caused by the render-module helper since it observes .on(“init”).

Thanks, I’m waiting to hear from you guys :speech_balloon:


#11

Note: this current implementation works fine except the fact that controllers don’t get instantiated properly. this is because the render-module helper passes a model to the {{render}} helper since you can only call the {{render}} helper once without a model, hence the setupController hook won’t work. I fixed this issue by taking the following steps:

  • Remove the #each loop in application.hbs where we call the render-module helper and instead introduce a named outlet (i.e. {{outlet “tab-content”}})
  • Update your view classes and add the necessary attributes like role=tabpanel etc.
  • Update your route objects and override the renderTemplate hook, specifying the tab-content outlet as the container.

Now everything would work as you expect. each route will be rendered inside the tab-content section of your bs-tabpanel and the context is properly set up (controller, view etc.) but the catch is that, only one template will be rendered into this outlet at a time. Its no biggy and actually its a better design because the DOM would be much more lightweight. but i wanted to keep my rendered views in the DOM, just like the implementation that I’ve uploaded to the demo repo ember-tabbed-ui. If anyone could help me out figure a way to properly initialize my controller in that sample It’d be great!


#12

I’m actually a little surprised that this topic got little attention over the past few days. anyone able to help here :question:


#13

I’m sorry to have neglected this for so long, but this is on my list. Will update again soon!


#14

Hey @atsjj
It’s alright I came up with a solution and its been working really well for the past several weeks.
In my current implementation, only one template can be rendered and kept alive in the DOM. you get route transitions when you switch between the tabs, and if a route has been loaded before the model hook is skipped.
It works just fine, but only one template can be rendered a time and kept in the DOM. I’m still looking for a good design to come up with a modular application. in which you can render different components in different parts of the page, whenever you need to, and keep them alive in the DOM, with their model and context properly initialized and set up.

I’ll definitely update this post when i resolve this challenge!
P.S I’ll update my github repo in a day or two with my current implementation. its still pretty good. If it gets developed into a plugin for ember and ember-cli, even better!


#15

Bump +1

I am also looking for dynamic link+tabbed interface. My application is admin controller and admin can assign links to each users so my links will be loaded from database. And each link will open its own tab


#16

Bump +1 - this would be a really good starter-kit, especially when implemented using all the Ember 2.0 guidelines.


#17

I also have this requirement in our project. Here is my approach to solve it: (using bootstrap for markup)

  • a nav-pills for tabs display
  • put the {{outlet}} inside tab-content .tab-panel.active
<div class="row">
  <div class="col-md-2">
    <ul class="nav nav-pills nav-stacked">
      {{#each model.categories as |category|}}
        {{#link-to 'shops.show.category' category tagName="li"}}
          <a href="#">{{category.name}}</a>
        {{/link-to}}
      {{/each}}
    </ul>
  </div>
  <div class="col-md-10">
    <div class="tab-content">
      <div role="tabpanel" class="tab-pane active">
        {{outlet}}
      </div>
    </div>
  </div>
</div>

Here is how it looks:

Here is the repo for it


#18

I have really liked the example you have implemented. I am going to use the concepts for a tabbed UI instead of nav-pills. Thanks for sharing