2

I have a page whichs layout will look like the following: There is a nested navigation on the left side. In this case it displays:

  • a list of libraries (l1, l2, l3). currently l1 is selected
  • a list of books (b1, b2) present in the library (1)
  • a list of pages (p1, p2, p3) contained in the book

In case a library is selected, the column for displaying the books in the library would automatically be updated. When selecting a book, the pages column would automatically be updated with all the pages in that book etc...

In addition to that there is a main view, which displays additional details for the currently selected entity. In this case page 2 (p2):

 ________________________________
|*l1 | b1 | p1 |                 |
|----|----|----|                 |
| l2 |*b2 |*p2 |  Main View      |
|----|----|----|  (p2 details)   |
| l3 |    | p3 |                 |
 --------------------------------

Here is an additional view when just a library is selected. It shows the selected library, all the books in that library in the sidebar and the details about the library in the main view.

 ________________________________
|*l1 | b1 |                      |
|----|----|                      |
| l2 | b2 |       Main View      |
|----|----|       (l1 details)   |
| l3 |    |                      |
 --------------------------------

or when just a book is selected:

 ________________________________
|*l1 | b1 | p1 |                 |
|----|----|----|                 |
| l2 |*b2 | p2 |  Main View      |
|----|----|----|  (b2 details)   |
| l3 |    | p3 |                 |
 --------------------------------

So, there are mainly two jobs when for instance a library is selected:

  • Display all the books in the library (navigation)
  • Display details about the selected library (main)

The navigation on its own works fine when using nested routes the following way:

Router.map(function() {
  this.resource('libraries', { path: '/libraries' }, function() {
    this.resource('books', { path: ':library'}, function() {
      this.resource('pages', { path: ':book'});
    });
  });
});

Within the pages route I just get all the books for the selected library and render it into the sidebar outlet:

export default Ember.Route.extend({
  model: function (params) {
    return this.store.find('book', {song: params.library});
  },
  renderTemplate: function() {
    this.render({ outlet: 'sidebar-books' });
  }
});

However, by now I am stuck when trying to render the details about the currently selected library, book or page to the main view AND to the sidebar view.

I found that I can add an additional render call to the renderTemplate function, which lets me render additional content to the main view, however I can't figure out how to retrieve the entity I want to display from the store, as any additional routes won't get called.

What is the recommended why to do something like this?

st-h
  • 2,444
  • 6
  • 37
  • 60

2 Answers2

3

After quite some try and error, loading two models within the same route and rendering both views with that models, seems to do the trick.

This is the books route.

routes/books.js:

export default Ember.Route.extend({
  model: function (params) {
    return Ember.RSVP.hash({
      books: this.store.find('book', {library: params.library}),
      library: this.store.find('library', params.library)
    })
  },
  renderTemplate: function(controller, model) {
    this.render('books',{
      outlet:'sidebar-books'
    });
    this.render('library');
  }
});

In the handlebar template I can now access the library via:

{{library.name}}

and iterate over the books via:

{{#each books}}
  <div class="book">
    <div>{{title}}</div>
  </div>
{{/each}}
st-h
  • 2,444
  • 6
  • 37
  • 60
2

side navigation with dynamic menu Example of rendering a side menu navigation with nested routes closer to what codeySmurf described, although the main issue of loading multiple models has not been tuckled, it has been solved by codeySmurf.

http://emberjs.jsbin.com/begewoza/1/edit

side navigation with static menu

An approach for rendering a sidebar menu along with the content could be something like the following. It is assumed that it is not required to render the parent models along with the children in main view area (eg show the lib1 on top underneath book1 and below that page1 at the same time) and the menu can be defined in template so no nested resources have been used. The menu has been rendered by using the render helper, although named outlets could also be used (named outlets example http://emberjs.jsbin.com/nibikufa/1/edit) .

http://emberjs.jsbin.com/hituxado/1/edit

js

App = Ember.Application.create();

App.Router.map(function() {
  this.route("allLibraries",{path:"/libraries"});
  this.resource("library",{path:"/libraries/:library"});
  this.resource("book",{path:"/libraries/:library/books/:book"});
  this.resource("page",{path:"/libraries/:library/books/:book/pages/:page"});
});


App.IndexRoute = Ember.Route.extend({
  redirect:function(){this.transitionTo("allLibraries");}
});

App.LibraryRoute = Ember.Route.extend({
  model:function(params){
    return {libId:params.library};
  }
});

App.BookRoute = Ember.Route.extend({
  model:function(params){
    return {libId:params.library,bookId:params.book};
  }
});

App.PageRoute = Ember.Route.extend({
  model:function(params){
    return {libId:params.library,bookId:params.book,pageId:params.page};
  }
});

App.MenuView = Ember.View.extend({

});

hbs

<script type="text/x-handlebars">
    <h2> Welcome to Ember.js</h2>
    <div style="float:left">{{render "menu"}}</div>
    <div style="float:left;margin-left:30%">{{outlet}}</div>
  </script>

  <script type="text/x-handlebars" data-template-name="allLibraries">
all libs
  </script>
  <script type="text/x-handlebars" data-template-name="library">
the library {{libId}}
  </script>
  <script type="text/x-handlebars" data-template-name="book">
the book {{libId}} - {{bookId}}
  </script>
  <script type="text/x-handlebars" data-template-name="page">
the page {{libId}} - {{bookId}} - {{pageId}}
  </script>

  <script type="text/x-handlebars" data-template-name="menu">
{{#link-to "allLibraries"}}all libs{{/link-to}}
<br/>
{{#link-to "library" 1}}lib 1
<br/>
{{#link-to "book" 1 1}}book 1
<br/>
{{#link-to "page" 1 1 1}}page1{{/link-to}}
{{/link-to}}
<br/>
{{#link-to "book" 1 2}}book 2
<br/>
{{#link-to "page" 1 2 2}}page2{{/link-to}}
{{/link-to}}

{{/link-to}}
<br/><br/>
{{#link-to "library" 2}}lib 2
<br/>
{{#link-to "book" 2 2}}book 2
<br/>
{{#link-to "page" 2 2 2}}page2 2{{/link-to}}
{{/link-to}}
{{/link-to}}



  </script>

css

a.active{
  background-color:lightgrey;
  border-radius:4px;
  margin:8px;
  padding:4px;
}
melc
  • 11,523
  • 3
  • 36
  • 41
  • Thanks for the detailed answer. Probably my question wasn't accurate enough, but the books column needs to be replaced with the books from one library in case a library is selected. Same for the pages when a specific book is selected. I have updated my question to make this more clear. I have decided to use nested routes to make this possible, but the problem is now that I need to render both the library details as well as all the books in the library when calling a single route. Would this be possible with non nested routes? – st-h Jun 29 '14 at 12:15
  • Hmm, sounds like this might be the route I need to take: http://stackoverflow.com/questions/20521967/emberjs-how-to-load-multiple-models – st-h Jun 29 '14 at 12:18
  • @codeySmurf the thread you mentioned is a very good one, but it is about loading multiple models. The issue you mention is about rendering dynamically the side navigation, right? The solution i provided is simplified showing how to lay things out with a side navigation whose structure is fixed. If you replace the template of the side menu with content of the models retrieved from the routes (i.e. LibraryRoute, BookRoute, PageRoute etc) it will work as you require. – melc Jun 29 '14 at 12:37
  • I am not sure if I understand that correctly. I think I need to load two models for one route: If a library is selected, I need to load the library itself and a list of books associated with that library. I then need to render the list of books in the sidebar (together with the previously acquired list of libraries) and the details about the library in the main view. Probably there is a better way to do this, which I currently just don't really understand... – st-h Jun 29 '14 at 12:46
  • I finally found a solution to this by loading 2 models and rendering them into 2 views. If it isn't too much trouble please let me know what you think about the solution I submitted. However, probably your approach is better, but I just fail to understand. In that case, please let me know. – st-h Jun 29 '14 at 15:06
  • 1
    @codeySmurf great! +1 for that even though I caused some confusion :) . You solution is fine. My approach is not better is just another solution, which i'm afraid is not what you were looking for. I thought you had trouble with combining the routing and rendering part while you actually had this figured out and you mainly required loading multiple models. So i will go ahead and clear things out in case any user lands here looking for side menu functionality. – melc Jun 30 '14 at 06:59
  • I am marking your approach as the solution as it uses the approach that has been described as the one causing less trouble in that question: http://stackoverflow.com/questions/20521967/emberjs-how-to-load-multiple-models – st-h Jun 30 '14 at 18:27
  • `this.resource()` has been deprecated in v2+, is there an updated way of doing the static sidebar? – George L Jan 19 '17 at 17:19