Creating Web applications with Ember.js - ember.js

I've just found out about Ember.js, and it looks interesting. I'd like to create a small notes app to learn how to use it.
The basic layout I have in mind is to have categories, and each category can have notes. For the UI, there would be a sidebar with the categories which will be clickable, and the notes for the category will be displayed on the other side.
But I can't quite figure out the whole template/layout system. The template system itself seems simple enough (similar enough to Rails views). But what do you do for layouts? With Rails for example, you can define layouts quite easily and then the individual views are added to that. This seems unclear to me with Ember.js.

Besides the approaches #rharper mentioned, you can also use the outlet helper, wich has been introduced in commit 5a4917b.
You can find an example here:
Handlebars:
<script type="text/x-handlebars" data-template-name="main" >
Main View
{{outlet contentView}}
{{outlet footerView}}
</script>
JavaScript:
App.viewController = Ember.Object.create({
footerView: Ember.View.create({
templateName: 'footer'
}),
contentView: Ember.View.create({
templateName: 'content'
})
});
Ember.View.create({
controllerBinding: 'App.viewController',
templateName: 'main'
}).append();
Ember.run.later(function(){
App.viewController.set('contentView', Ember.View.create({
templateName: 'new-content'
}));
}, 1000);
​

For simple wrapper-style layouts you can use Ember's built-in layout support. It only supports a single {{yield}} so may be too limited for your application.
For something a little more robust take a look at ghempton's Ember Layout. I think you'll find it quite similar to Rails layouts. He has a live example here.
Finally, it's fairly easy to create a hierarchy of views in Ember (instead of or in addition to using layouts). Tom Dale has a good collection of Ember resources and examples here.

Related

Not rendering/removing sidebar based on route

Something I've been experimenting around with Ember for a couple of hours and can't work out. Hopefully it's just a terminology issue that I'm getting stumped on as I read through the Ember docs.
I have an application, that, for the most part, consists of a sidebar/top bar (called wrapper), and a footer.
My basic application.hbs looks like this (I'm using Ember App Kit to provide structure):
{{partial "wrapper"}}
{{outlet}}
{{partial "footer"}}
If this was the state of my application, it would work pretty well. Page content loads in the {{outlet}} fine.
My main issue is how to break out of this template structure in an "Ember" way (and preferably without going all jQuery and removing DOM elements willy-nilly).
I have a few routes that I don't want the wrapper and the footer to show on (they're full page login/forgot password routes, and a couple of minimal interface/no distractions modes).
I experimented with trying to remove the sidebar and footer by making the default template (application.hbs):
{{#if showWrappers}}
{{partial "wrapper"}}
{{/if}}
{{outlet}}
{{#if showWrappers}}
{{partial "footer"}}
{{/if}}
Where showWrappers is in the ApplicationController:
export default Ember.Controller.extend({
showWrappers: function() {
var routes = ['login'],
currentPath = this.get('currentPath'),
show = true;
routes.forEach(function(item) {
var path = new RegExp('^' + item + '*');
if (!Ember.isEmpty(currentPath.match(path))) {
show = false;
}
});
return show;
}.property('currentPath'),
});
Attemping to transition to /login from / using {{link-to}} returns in an error: Uncaught Error: Cannot perform operations on a Metamorph that is not in the DOM presumably because I'm removing things Ember wanted to keep (I am using {{link-to}} and {{bind-attr}} in the sidebar, so there are bindings there).
Aware that I could use actions and jQuery to hide elements of the page and bring them back for the "distraction free" mode, but I'd prefer to learn how to structure templates and use Routes with the renderTemplate hook potentially using this.render (?) to blow away the current DOM and rebuild from a different base (rather than application.hbs).
Thoughts? More than happy to clarify.
I have discovered disconnectOutlet, and have converted my partials into outlets:
{{outlet wrapper}}
{{outlet}}
{{outlet footer}}
Made my ApplicationRoute render to them by default:
export default Ember.Route.extend({
renderTemplate: function() {
this.render();
this.render('wrapper', {
outlet: 'wrapper',
into: 'application'
});
this.render('footer', {
outlet: 'footer',
into: 'application'
});
}
});
and then on the LoginRoute, I just run this.disconnectOutlet for both wrapper and footer, and seems to work pretty well.

Ember: how to use i18n translations in controller code?

I am using Ember i18n in my app. I also want to use the translation strings in the controllers (in most cases in an alert or confirm message). How can this be done ?
See http://jsfiddle.net/cyclomarc/36VS3/2/
Clicking on the button should alert "info" and not "T1005" ...
<script type="text/x-handlebars">
{{t T1005}}<br>
<button {{action 'clickMe' content}}>{{t T1005}} - Click me</button>
</script>
CLDR.defaultLanguage = 'en';
App.ApplicationController = Ember.Controller.extend({
clickMe: function(){
alert('T1005');
}
})
I know that a possible workaround is to no longer use alert and confirm and replace them by for example the bootstrap alternatives. However, I could imagine that in certain cases you will want to do something with the strings in Javascript (e.g. update a certain label via jQuery or so).
Any ideas on how to use the i18n strings in the controllers is helpful. Using an i18n library is only usefull if all aspects of the application can be translated ...
Just found the solution. Just access the string via Ember.I18n.t("T1005");
JSFiddle updated: http://jsfiddle.net/cyclomarc/36VS3/7/
Might be late here, but what about using the Em.I18n.TranslateableProperties mixin as documented here ?
You could do something like :
App.ApplicationController = Ember.Controller.extend(Em.I18n.translateableProperties, {
messageTranslation: 'T1005',
clickMe: function(){
alert(this.get('message'));
}
});
In the template, {{message}} will also hold the translation.
The solution that works to me is the following (using Ember I18n):
App.ApplicationController = Ember.Controller.extend(Em.I18n.translateableProperties, {
messageTranslation: 'T001',
showMessage: function(){
alert(this.get('message'));
}
});
The answer from cyclomarc didn't work for me (it's from 2013, which might be related), but it pointed me in the right direction:
this.container.lookup('service:i18n').t('my.translation.id')

Ember.js how to design different representations of Data (with TodoMVC as an example)?

I would like to know what's the best way of designing the display of different representations of the same data model in Ember.js. To ask my question, I'll use the TodoMVC of Ember.JS, which has 3 representations of todo-data:
any todo, i.e. the entire todo list (TodosIndexRoute)
todos that are still active and incomplete (TodosActiveRoute)
todos that have been completed (TodosCompletedRoute)
Currently, you can see each of the 3 by clicking on the words at the bottom of the list, directing to a different URL each time. Since currently each representation has a route, it makes sense that each representation gets its unique URL. The main page displays the entire todo list (1.).
A. What is the best ember.js design to make the main page display all 3 representations (i.e. under just one URL)?
B. How about the best design that displays all 3 on the main page as well as on separate pages?
Currently I only figured out a clumsy way and made this modified TodoMVC app that shows incomplete and completed lists at the bottom of the page.
In index.html, I added new named lists:
{{#each todosactive itemController="todo"}}
{{ title }},
{{/each}}
In the js router, I copied TodosActiveRoute and TodosCompletedRoute into TodoIndexRoute, which is code duplication, very bad.
Todos.TodosIndexRoute = Ember.Route.extend({
setupController: function () {
var todos = Todos.Todo.find();
this.controllerFor('todos').set('filteredTodos', todos);
var todos_active = Todos.Todo.filter(function (todo) {
if (!todo.get('isCompleted')) {
return true;
}
});
this.controllerFor('todos').set('todosactive', todos_active);
...
});
I feel like I'm missing an elegant way of doing this, but my current ember.js knowledge is very limited. Should I use {{partial}}, {{render}}, render, or something else?
I tried {{ partial }} and {{ render }}, but I can't get them to display any data .
Thanks for helping out.
A) Ember tries to work really closely with urls. This is a good thing since if you want to share a url, the view should be consistent. The url is a powerful tool and each unique url should link to the same unique page. Having one url that links to multiple views isn't great, and certainly not shareable. If you have some time listen to some talks by Tom Dale and Yehuda Katz for an interesting overview of ember and what they're trying to do.
B) You can include different views on one page. Have a look at the guides, most notably on rendering templates and using helpers for more information on including different views under one url.
A) To display all 3 representations in one view, we actually just need the basic model in the single route. The key is for the controller to give you flavors of that model. The other important thing is to use data binding in the handlebars template. You can see the running version here.
In the IndexRoute, add a model that gets the plain todos list:
Todos.TodosIndexRoute = Ember.Route.extend({
model: function(params) {
return Todos.Todo.find();
},
...
Make a TodosListView that doesn't need to have anything:
Todos.TodoListView = Ember.View.extend();
In the controller, add 2 computed properties that returns the desired arrays:
Todos.TodosController = Ember.ArrayController.extend({
...
todosActive: function() {
return this.filterProperty('isCompleted', false);
}.property('#each.isCompleted'),
todosCompleted: function() {
return this.filterProperty('isCompleted', true);
}.property('#each.isCompleted'),
...
Finally, in the HTML template:
<script type="text/x-handlebars" data-template-name="todos">
Active:
{{#view Todos.TodoListView lalaBinding="todosActive"}}
{{#each view.lala}}
{{title}},
{{/each}}
{{/view}}
Completed:
{{#view Todos.TodoListView dataBinding="todosCompleted"}}
{{#each view.data}}
{{title}},
{{/each}}
{{/view}}
</script>
note the dataBinding.
Thanks to the folks on #emberjs irc for helping out.

How do I bind a controller to the view when the controller is create by Ember.js

this question is slightly related to How to display the “content” of an ObjectController?
However, in the provided solution and all other examples I can find the controllers are always created explicitly. The nice thing about Ember.js is that the Route takes care of mostly everything. So I don't want to create the controller but want to bind it to a view:
{{view App.myview controllerBinding="App.applicationController"}}
You can see the complete example in this fiddle. The example is not that great because Ember usually sets the controller of a child view to its parent view.
In the end I need to know, how I can access a controller which is created by Ember from a view.
Thanks for any help!
Update:
I provided the wrong fiddle or it did not save my changes. Here is the link to the right version: http://jsfiddle.net/ncaZz/1/
What should I provide in line 9 in the templates?
From the view you can access the controller with
this.controller
If you need other controllers than your view controller you can use the needs in the viewcontroller:
App.DatasetEditController = Ember.ObjectController.extend({
needs: ['mappingIndex']
});
and then use:
this.controller.mappingIndex
You don't really need to bind to it. You can access the controller from the view by calling it like this.
this.get('controller');
Updated Answer:
You really should not have your click event inside your view. Your actions should either be in your controller or your router.
Your template should become
<span style="background-color: green" {{action doStuff}}>
Click
</span>
and you should have a controller that should have this
App.MyController = Em.Controller.extend({
needs: ['application'],
doStuff: function(){
this.get('controllers.application').foo();
}
});
Also, the MyView and MyController should be capitalized, because when extending these items from ember that are not instances, and the capitalization is required. The view should only really have stuff in the didInsertElement that handles special things like any kind of jquery animations or initializing a date picker. But, the "ember way" is to have action in the router or controller.

How do I setup an Ember View class to be appended to a particular container?

Ember.View has a nice method called .appendTo("#container") which would allow me to specify a container div for the view. However, when I use the router and .connectOutlet method, an instance of my view is created automatically based on convention and is added to the page body element by default. Is there a way to configure the class definition of the view so that upon creation it will be inside my desired #container. Here is my view:
Jimux.BuildsView = Em.View.extend({
templateName: 'builds',
appendTo: '#jimux-header', #this was just a guess and did not work. but does some propery like this exist for the view?
tagName: 'div',
listVisible: true,
...
Another way to ask this question is: how do I tell Ember router to append a view to a particular item in the dom? By default the router appends the view to the body.
And here is the router bit:
# Connect builds controller to builds view
router.get('applicationController').connectOutlet("builds","builds", Jimux.buildsController)
To clarify, I dont want to put my whole Ember app in a container. I have several views in my application, and most of them are fine directly in the body. But there are a couple like the one mentioned in this question, which I want to put inside "#application-header" div.
You can specify the root element for your application object.
window.App = Ember.Application.create({
rootElement: '#ember-app'
});
Edit:
Having re-read your question, I think you should look into named outlets, so you could do something like:
<div id="application-header">
{{outlet builds}}
</div>
{{outlet}}
well..after understanding your question, i remember having same trouble. Also, thing is i didn't find any way to do this even after going through the Ember code. But later i understood that its for good purpose only. I know you already might have come across with handlebars with which we can acheive this. If we give a view a ID to get appended, we are constraining the application and the whole use of ember becomes useless. Ok coming to you question, as far as i know, we can acheive that appending mustache templates in you div element of HTML.
<div id="jimux-header">
{{view Jimux.BuildsView}}
</div>
This way we can use the Jimux.BuildsView where ever you want and as many times possible. The Beauty of Ember you have to say...
Just add rootElement in the application object.
var App = Ember.Application.create({
rootElement: '#container'
});