I want to show some blocks of HTML based on the existence of an outlet. I tried this code but it doesn't work. Am I missing something?
{{#if outlet}}
<table class="col-sm-9">
some content
</table>
<table class="col-sm-3">
{{outlet}}
</table>
{{else}}
<table class="col-sm-12">
some content
</table>
{{/if}}
EDIT:
My scenario is if I'm on route customers then outlet is hidden, else if I'm on route customers.create then outlet is show. Is there any simple way to do it without touch router.js file?
Have you tried naming the outlet and then render the content of a particular outlet in the router.
Please see http://emberjs.com/guides/routing/rendering-a-template/
something like this , which is mentioned in the guide.
App.PostRoute = App.Route.extend({
renderTemplate: function() {
this.render('favoritePost', { // the template to render
into: 'posts', // the template to render into
outlet: 'posts', // the name of the outlet in that template
controller: 'blogPost' // the controller to use for the template
});
this.render('comments', {
into: 'favoritePost',
outlet: 'comment',
controller: 'blogPost'
});
}
});
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.
I am trying to make a list of items that when clicked, shows an edit form inline with the list item.
So far this is what I've tried:
router.js.coffee
App.Router.map ()->
#resource 'friend_management', ->
#resource 'groups', ->
#resource 'group', path: '/:group_id', ->
#route 'edit'
templates/groups.hbs
<div id="group-list">
{{#each}}
{{#view Dfw.GroupView}}
{{link-to name 'group.edit' this class=cssClass tagName='div'}}
{{outlet groupEditOutletName}}
{{/view}}
{{/each}}
</div>
<!-- it works fine if there is one outlet rendered here,
but I would prefer the outlet for the edit form rendered inline -->
templates/group/edit.hbs
{{input type='text' value=name placeholder="Group name"}}
<button {{action update}}>Update Group</button>
routes/group/edit.js.coffee
App.GroupEditRoute = Ember.Route.extend
renderTemplate: (controller, model)->
#render('group/edit', outlet: "group#{model.id}", into: 'groups')
I get the impression that Ember.js doesn't allow for dynamic named outlets, but does anyone know a work around?
One option you have would be to place the editing UI that you want to be placed inline as part of the actual view. This would remove the ability to have the action still be router based, so you wouldn't be able to have group/edit/ID, but you would be able to edit inline.
Dfw.GroupView = Ember.View.extend({
templateName: 'groupView', // or whatever you call it
isEditing: false,
click: function(evt) {
if (!this.get('isEditing')) {
this.set('isEditing', true);
}
},
actions: {
update: function (e) {
// Update code here
this.set('isEditing', false);
}
});
Then your template for the group view could be something like this:
<div {{bind-attr class=cssClass}}>
<!-- HTML that will be shown even when not editing goes here -->
{{#if view.isEditing}}
{{input type='text' value=name placeholder="Group name"}}
<button {{action view.update}}>Update Group</button>
{{/if}}
</div>
I hope this helps. While it does not use the routing, it seems as though using routing in this case wouldnt make much sense, because you are viewing all of the objects together, instead of viewing one of them and then clicking edit.
Good luck!
After diving into Backbone + Marionette for quite a long time, I've been training with Ember for a week or so. I've been able to play with it and build a blog easily. As long as the app relies on routes it's fine. Now I would like to build an app which only has 2 routes, and relies more on states. I want to define the following nested HTML structure :
<div id="topbar"></div>
<div id="content"></div>
<div id="botbar"></div>
I would like the topbar and botbar to remain the same whatever the content is.
Content could be "pos content"
<div id="pos">
<div id="leftsidebar">...</div>
<div id="cart">...</div>
<div id="buttons">...</div>
<div id="rightsidebar">...</div>
<div id="header">...</div>
</div>
or "params content"
<div id="params">...</div>
So far i've managed to build the 1st level of nesting using the following code :
App = Em.Application.create({});
App.Router.map(function() {
this.route('index', { path: '/' });
this.route('params', { path: '/params' });
});
App.IndexRoute = Em.Route.extend({
renderTemplate: function() {
this.render();
this.render('topbar', { outlet: 'topbar' });
this.render('pos', { outlet: 'content' });
this.render('botbar', { outlet: 'botbar' });
}
});
App.ParamsRoute = Em.Route.extend({
renderTemplate: function() {
this.render();
this.render('topbar', { outlet: 'topbar' });
this.render('params', { outlet: 'content' });
this.render('botbar', { outlet: 'botbar' });
}
});
I see in chrome Ember extension that the routes are defined, and my controllers (TopbarController, BotbarController and PosController/ParamsController depending on the route) seems to be instanciated.
Now what I would like is going deeper, and have a controller per sub-outlet (leftsidebar, cart, etc.) but I have no idea how to build the second level of nesting.
The documentation doesn't seem to help me and google is not my friend on this :( Could you guys help me out ? Thank you.
outlets in ember are contextual to the currently active route.
If you have the following application template
<div id="topbar">
{{ outlet 'topbar' }}
</div>
<div id="content">
{{ outlet }}
</div>
<div id="botbar">
{{ outlet 'botbar' }}
</div>
You could clean up your repeatitive code by moving it into the ApplicationRoute
App.ApplicationRoute = Ember.Route.extend({
renderTemplate: function(){
this._super.apply(this, arguments);
this.render('topbar',{outlet: 'topbar', into: 'application'})
this.render('botbar',{outlet: 'botbar', into: 'application'})
}
})
Now you can have the following index template
<div id="index">
<div id="left">
{{ outlet "left" }}
</div>
<div id="content">
{{ outlet }}
</div>
<div id="right">
{{ outlet 'right' }}
</div>
</div>
You need to clarify which context the outlet is located in by rendering "into" it.
App.IndexRoute = Ember.Route.extend({
renderTemplate: function(){
this._super.apply(this, arguments);
this.render('right',{outlet: 'right', into: 'index'})
this.render('left',{outlet: 'left', into: 'index'})
}
})
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');
}
});
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.