I have an Ember app setup, and when visiting /playlist/1, it renders the templates as expected. However, the model data isn't being displayed. I have a playlist.hbs file with an outlet, and a playlist folder with index.hbs inside of that folder with html and handlebars to display data. I have both App.PlaylistIndexController & App.PlaylistIndexRoute defined.
App.Router.map(function() {
this.resource('account', {path: '/account/:accountId'}, function() {
this.route('login');
});
this.resource('playlist', { path: '/playlist/:playlist_id'}, function() {
this.route('edit');
});
});
FWIW, everything was working properly prior to adding the nested route (with my controller and route defined as App.PlaylistController and App.PlaylistRoute respectively)
"playlist":{"id":1,"name":"playlistname"}
Any ideas how to get the playlist data to display properly?
UPDATE:
App.PlaylistIndexRoute = App.AuthenticatedRoute.extend({
setupController: function(controller, model) {
this._super(controller, model);
var online = this.get('store').find('account');
this.controllerFor('playlistViewers').set('model', online);
},
});
<div id="main">
<div id="primary">
<section id="playlist">
<header class="playlist-header">
<h2>Playlist</h2>
<h1>{{name}}</h1>
</header><!--.playlist-header-->
<div class="playlist-content">
<ul>
{{#each song in songs}}
<li {{action 'play' song}} class="show-for-mobile">
<button {{bind-attr class="song.isPlaying:icon-volume-up:icon-play song.isStreaming:icon-adjust"}} ></button>
<div class="song-meta">
<span class="song-name">{{song.name}}</span>
<span class="song-artist">{{song.artist}}</span>
</div><!--.song-meta-->
</li>
{{/each}}
</ul>
</div><!--.playlist-content-->
</section><!--#playlist-->
</div><!--#primary-->
{{partial "sidebar"}}
That's my current playlist/index.hbs file, but even {{name}} displays nothing
Your PlaylistIndexRoute needs to have a model. This can be the model that was loaded by the playlist resource.
App.PlaylistIndexRoute = App.AuthenticatedRoute.extend({
model: function() {
return this.modelFor('playlist');
}
});
Related
I'm building an app with multiple todo lists. The todo list used to correctly show todo model.
But I added function() to stack router to wrap todos router (so that it would correctly render todos template in stack template).
Then todos/index template (which is rendered through the outlet in todos) stopped displaying todo model.
This is my router structure:
Todos.Router.map(function() {
this.resource('stacks', {path: '/stacks'});
this.resource('stack', {path: '/stacks/:stack_id'}, function () {
this.resource('todos', { path: '/todos/:todos_id' }, function () {
// additional child routes will go here later
this.route('active');
this.route('completed');
this.route('new');
});
this.resource('todo', { path: 'todo/:todo_id' });
});
});
Stack template which renders Todo template:
<script type="text/x-handlebars" data-template-name="stack">
<h1>
<label {{action "editStack" on="doubleClick"}}>{{stackTitle}}</label>
{{input
value=stackTitle
action="createStack"}}
<div>{{model.stackTitle}}</div>
</h1>
{{render 'todos' todo}}
</div>
</script>
Then this todo template has an outlet for todo/index:
<script type="text/x-handlebars" data-template-name="todos">
<div class="container-fluid">
<section id="main">
{{outlet 'todos/index'}}
{{outlet modal}}
{{input type="checkbox" id="toggle-all" checked=allAreDone}}
</section>
</div>
</script>
And todo/index template:
<script type="text/x-handlebars" data-template-name="todos/index">
<ul id="todo-list">
<li {{bind-attr class="todo.isCompleted:completed todo.isEditing:editing"}}>
{{#each todo in model itemController="todo"}}
{{#if todo.isEditing}}
{{edit-todo class="edit" value=todo.title focus-out="acceptChanges"
insert-newline="acceptChanges"}}
{{else}}
{{input type="checkbox" checked=todo.isCompleted class="toggle"}}
{{outlet}}
<button {{action 'openModal' 'modal' model}}>
<label {{action "editTodo" on="doubleClick"}}>{{todo.title}}</label>
</button>
{{/if}}
</li>
{{/each}}
</ul>
</script>
Routers:
Todos.TodosRoute = Ember.Route.extend({
model: function() {
return this.store.find('todo');
},
});
Todos.TodosIndexRoute = Ember.Route.extend({
model: function() {
return this.modelFor('todos');
},
});
I've tried using {{partial}} helper in case that was a problem, but it didn't seem to change much.
Maybe I'm missing something in todos/index router that I need to call data through nested templates?
I appreciate your help!
There's a lot going on here but I think the root of your problem is with the way that you're using render in your templates/stack.
Since your routes/todos route is nested inside your routes/stack route, you're going to want to use an outlet inside your templates/stack if you want any of the templates for the nested routes to be rendered.
When you use render 'todos' todo, you're saying to render templates/todos with the todo property from the controllers/stack as the model. This isn't going to use your routes/todos or routes/todos-index to set the model.
What you probably want is for your templates/stack to have an {{outlet}} that either the routes/todos or routes/todo is rendered into when you visit either of those routes.
The link-tohelper return the following error:
Uncaught Error: each doesn't match link-to - 5:10
The template:
<script type="text/x-handlebars" id="actions">
<div class='container-fluid'>
<div class="row"> <!-- -->
<div class="col-md-6 col-lg-4"> <!-- -->
{{#each action in model}}
{{link-to 'action' action}}{{action.id}}{{/link-to}}
{{/each}}
{{outlet}}
</div>
</div>
</div>
</script>
The router:
App.Router.map(function() {
this.resource('application', function() {
this.resource('actions', function() {
this.resource('action', { path: '/:action_id'});
});
});
The route:
App.ActionsRoute = Ember.Route.extend({
model: function() {
return this.store.findAll('action');
//return this.modelFor('user').get('actions');
},
setupController: function (controller, model) {
controller.set('model', model);
},
});
I cannot find what is wrong.
It's a quite small error. When using a block helper such as each or link-to you need to invoke them with a # in front, such as you have done with {{#each}}. Since you lack that on your starting link-to, the parser sees the {{/link-to}} and notices that it currently is working with a each-block and those doesn't match. Just add a # in front of your starting 'link-to' and it should work fine.
{{#each action in model}}
{{#link-to 'action' action}}{{action.id}}{{/link-to}}
{{/each}}
I'm trying to adopt the completed/archived-route-behavior from this TodoMVC-Tutorial.
I'm using Ember 1.7.0, Ember Data 1.0.0-beta.9 and Handlebars 1.3.0.
I want to get all active users on my index route (example.com/organization/users) and all archived users on my archived route (example.com/organization/users/archived).
But the filter doesn't work: archived users are shown on my index route and vice versa, but my console.log output is correct.
Update: Please check this JSBin: http://emberjs.jsbin.com/puxeludeviga/3/
That's how I tried it:
Docket.OrganizationUsersRoute = Docket.AuthenticatedRoute.extend({
model: function() {
return this.store.find('user');
},
renderTemplate: function() {
// render all posts
this.render('organization/users', {
into: 'application'
});
// render toolbar
this.render('organization/toolbar', {
into: 'application',
outlet: 'toolbar'
});
}
});
Docket.OrganizationUsersIndexRoute = Docket.OrganizationUsersRoute.extend({
model: function() {
console.log('index');
return this.store.filter('user', function(user) {
return !user.get('archived');
});
}
});
Docket.OrganizationUsersArchivedRoute = Docket.OrganizationUsersRoute.extend({
model: function() {
console.log('archived');
return this.store.filter('user', function(user) {
return user.get('archived');
});
}
});
And that's my template:
<ul class="entries">
{{#each}}
<li>
<div class="actions">
<button {{action "remove" this}} class="ei-icon-close"></button>
</div>
<div class="link" {{action "edit" this}} data-uk-modal="{target:'#user-modal'}">
<span class="before">{{initial}}</span>{{name}}
</div>
</li>
{{else}}
<li>No users</li>
{{/each}}
</ul>
My (shortened) router.js file:
Docket.Router.map(function() {
// organization route
this.resource('organization', function() {
// user routes
this.resource('organization.users', { path: '/users' }, function() {
this.route('archived');
});
});
});
I think I found your issue. The thing here is that template you're rendering the users into, has this name:
organization/users
and turns out that it matches to you organization.users route, so, it is always rendering the data from that route. You need to change the name of you template, let's say to:
organization/users_list
and then, use that in your routes, it will not have any problems because the name is different to the users route.
something like:
<script type="text/x-handlebars" data-template-name="organization/users_list">
<div class="top">
<h1>Users</h1>
{{#link-to 'organization.users.index' class="button-archive"}}active{{/link-to}} - {{#link-to 'organization.users.archived' class="button-archive"}}archived{{/link-to}}
</div>
<ul class="entries">
{{#each}}
<li>{{name}} - Archived: {{archived}}</li>
{{else}}
<li>No users to show</li>
{{/each}}
</ul>
</script>
and in your routes:
renderTemplate: function() {
// render all posts
this.render('organization/users_list', {
into: 'application'
});
// render toolbar
this.render('organization/toolbar', {
into: 'application',
outlet: 'toolbar'
});
}
I recently decided to look into Ember.js after having spent the last two years with KO. The first thing to notice is that the complexity seems a TAD steeper but I shall prevail :)
Now, I seem to need to hardcode the controller for a certain template which seems weird:
App.IndexRoute = Ember.Route.extend({
renderTemplate: function() {
this.render('todosList', { into: 'application' });
}
});
App.todosController = Ember.ArrayController.create({
content: [App.Todo.create(), App.Todo.create()]
});
<script type="text/x-handlebars" data-template-name="application">
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="todosList">
<ul id="todo-list">
{{#each todo in App.todosController.content}}
<li>
<label {{bindAttr class="todo.isCompleted"}}>{{view Ember.Checkbox checkedBinding="todo.isCompleted"}} {{todo.title}}</label>
<button {{action 'removeTodo' todo target="App.todosController"}}>Ta bort</button>
</li>
{{/each}}
</ul>
{{view Ember.TextField valueBinding="App.todosController.newTodoText"}}
<button {{action 'newTodo' App.todosController.newTodoText target="App.todosController"}}>New todo</button>
</script>
I tried setting controller: 'App.todosController' in the render() call but nothing. The #each in the view accepts nothing else than App.todosController.content which doesn't seem right. Why do I even need to explicitly state that it's the content it should read, isn't that set automatically?
Thankful for any help, Ember seems to have its niceties but in the beginning much is confusing.
Short answer first:
working jsbin: http://jsbin.com/usaluc/8/edit
Longer answer:
You had some misconceptions in your code that I've changed to be more ember-like, this results in this very simple example.
todosList template
<script type="text/x-handlebars" data-template-name="todosList">
<ul id="todo-list">
{{#each todo in controller}}
<li>
<label {{bindAttr class="todo.isCompleted"}}>
{{view Ember.Checkbox checkedBinding="todo.isCompleted"}} {{todo.title}}
</label>
<button {{action 'removeTodo' todo target="controller"}}>Remove toto</button>
</li>
{{/each}}
</ul>
{{view Ember.TextField valueBinding="newTodoText"}}
<button {{action 'newTodo' newTodoText target="controller"}}>New todo</button>
</script>
IndexRoute
When using renderTemplate to make sure the correct controller is used you should define it in the hash you are passing into the render function:
App.IndexRoute = Ember.Route.extend({
renderTemplate: function() {
this.render('todosList', {
into: 'application',
controller: 'todosList'
});
}
});
Route map
Since you dind't post your route map, and furthermore because your are using the IndexRoute renderTemplate hook I assumed your todosList is rendered directly when visiting '/', so just to be concise here a simple router map the renders the todosList template when visiting '/'
App.Router.map(function() {
this.resource('todosList', {path: '/'});
});
TodosListRoute
Now that you have a TodosListRoute in where you want to set to correct controller content, you should hook into the setupController function and do just that:
App.TodosListRoute = Ember.Route.extend({
setupController: function(controller, model) {
var myTodos = [
App.Todo.create({title: 'Drink water', text:'foo'}),
App.Todo.create({title: 'Bring out the trash', text:'bar'})
];
controller.set('content', myTodos);
}
});
TodosListController
The TodosListController looks fairly simple so far only including the two functions newTodo and removeTodo using the title value passed from the action helper in your template:
App.TodosListController = Ember.ArrayController.extend({
newTodo: function(title) {
var todo = App.Todo.create({title: title, text:'foo'});
this.get('content').pushObject(todo);
},
removeTodo: function(todo) {
this.get('content').removeObject(todo);
}
});
Hope it helps.
I've got a list of messages that are provided by a Rails backend. What I need is when the "toggle_visibility" action button is pressed, it would toggle the "publicly_viewable" property. This means, making a corresponding REST call (to effect the database) and changing the state of the corresponding cached message. Here is where I'm at so far.
Here's what I've got so far, that manages to end up on the debug console:
# app.js
App.Store = DS.Store.extend({
revision: 12,
adapter: DS.RESTAdapter.extend({
url: 'http://localhost:3000'
})
});
App.Message = DS.Model.extend({
body: DS.attr('string'),
mobile_number: DS.attr('string'),
publicly_viewable: DS.attr('boolean'),
created_at: DS.attr('date')
});
App.Router.map(function() {
this.resource('messages');
});
App.MessagesRoute = Ember.Route.extend({
model: function() { return App.Message.find() }
});
App.MessagesController = Ember.ArrayController.extend({
toggle_visibility: function(){
debugger;
}
});
# index.html
{{#each model}}
<button class="close" {{action toggle_visibility this}}><i class="icon-eye-close"></i></button>
<p class="message_body lead">{{body}}</p>
<small class="source_number">from {{mobile_number}}, received {{date created_at}}</small>
{{/each}}
I've been spending the past few hours reading through the Ember Guides and while I've gotten an idea on what the different classes there are, I still can't visualize clearly how to go about it. Particularly, I'm not sure if this should be a route concern or a controller, and I know that if ever it was a controller responsibility, I know that it should be on an ObjectController but I've been having trouble making it work.
You can use ArrayController#itemController and define a controller for the individual record in your ModelArray. Then you have to specify in the Array Controller the Object Controller responsible for a single object, which you have to reference as well in Handlebars. You can do something like this:
JS:
App.MessageController = Ember.ObjectController.extend({
visibilityClass: function() {
var visibility = this.get('model.publiclyViewable');
return 'toggle-visibility mdi-action-visibility%#'.fmt(
visibility ? '':'-off'
);
}.property('model.publiclyViewable'),
actions: {
toggleVisibility: function() {
var model = this.get('model');
model.toggleProperty('publiclyViewable');
model.save();
}
}
});
Handlebars:
<script type="text/x-handlebars" data-template-name="messages">
<!--
At this point the {{each}} helper will know how to lookup for
the controller simply by it's name
-->
{{#each model itemController="message"}}
<div class="panel panel-primary">
<div class="panel-heading">
<div class="pull-left">
<h3 class="panel-title">{{title}}</h3>
</div>
<div class="pull-right">
<a {{action 'toggleVisibility'}}>
<i class={{visibilityClass}} style="color: #FFF"></i>
</a>
</div>
</div>
<div class="panel-body">
{{body}}
</div>
<div class="panel-footer">
<i class="mdi-communication-quick-contacts-dialer"></i> {{mobileNumber}}
<i class="mdi-notification-event-note"></i> {{createdAt}}
</div>
</div>
{{/each}}
</script>
(see fiddle)
Note: Updated to Ember 1.11.x-beta and changed the code a little bit