Ember.js: Loop over data from ArrayController in unassociated View - ember.js

I have a route set up which pulls Account information from a REST endpoint:
Social.Router.map(function() {
this.resource('accounts');
});
Social.AccountsRoute = Ember.Route.extend({
model: function() {
return Social.Account.find();
}
});
Social.Account = DS.Model.extend({
name: DS.attr('string'),
username: DS.attr('string')
});
Social.AccountsController = Ember.ArrayController.extend();
I can loop over that data in my template like so:
<script type="text/x-handlebars" data-template-name="accountItem">
{{#each account in controller}}
<div class="avatar-name">
<p>{{account.name}}</p>
<p>{{account.username}}</p>
</div>
{{/each}}
</script>
I have another template in which I'd like to use the same account data. How would I retrieve the information associated with the Account model from within a View so that I can make it available in the corresponding template?
Social.NewPostView = Ember.View.extend({
tagName: 'div',
accounts: function(){
// return Account data here?
}
});
Update 1
Here's a quick view of my UI
On the left is "live" account data. On the right is static HTML. I'd like to reuse the data from the left on the right. Make sense?

You should take a look to the needs property for controllers. This allow you to specify a list of controllers you will be able to access from the current one.
More information on the emberjs guides
NewPostController
Social.NewPostController = Ember.ObjectController.extend({
needs: ['accounts'],
init: function(){
this._super();
console.log(this.get('controllers.accounts'))
}
});
Then, from your template:
<script type="text/x-handlebars" data-template-name="newPost">
{{#each account in controller.controllers.accounts}}
<div class="avatar-name">
<p>{{account.name}}</p>
<p>{{account.username}}</p>
</div>
{{/each}}
</script>

Related

Inheriting singular controller with render helper

I am trying to render a set of tabs for a set of objects (conversations) using the render helper for each. This is not part of a route as it is a persistent part of the interface. I have run into a problem where only the view with the same name as the model gets the intended controller (i.e. the panel contents and not the tab headers).
I have a Chat model, object controller and array controller (deliberately simplified here):
App.Chat = DS.Model.extend({ });
App.ChatsController = Ember.ArrayController.extend({
needs: 'application',
content: Ember.computed.alias('controllers.application.currentChats'),
});
App.ChatController = Ember.ObjectController.extend({ });
The ArrayController needed the needs/content properties because the chats are loaded in the application controller. I used the currentChats name as other routes may load non-current chats.
App.ApplicationController = Ember.Controller.extend({
init: function(){
this.store.find('chat', {"current": true});
this.set('currentChats', this.store.all('chat'));
}
});
I have no difficulty rendering the chat contents with the appropriate controller (into the 'chat' template). However, the chat tabs are given the default ObjectController, and therefore can't fire actions.
<script type="text/x-handlebars" id="application">
<!--application template-->
{{outlet chats}}
</script>
<script type="text/x-handlebars" id="chats">
<div id="chats">
<ul id="chat-tabs">
{{#each}}
{{render 'chatTab' this}}
{{/each}}
</ul>
{{#each}}
{{render 'chat' this}}
{{/each}}
</div>
</script>
<script type="text/x-handlebars" id="chatTab">
<!--tab template-->
</script>
<script type="text/x-handlebars" id="chat">
<!--chat template-->
</script>
The application router is as follows:
App.ApplicationRoute = Ember.Route.extend({
model: function(){ },
renderTemplate: function(){
this.render('application', { });
this.render('chats', {
into: 'application',
outlet: 'chats',
controller: 'chats'
});
}
});
This seems to come solely down to naming of the templates. The template called 'chat' inherits the correct controller, but chatTab doesn't despite receiving a chat as the model. Is there any way to force the view to inherit the correct controller? Or am I going about this in an idiosyncratic way.
Many thanks for your help to this Ember novice.
Andrew
It goes solely off the name provided to the render. The easiest way is to just create the other controller and extend the chat controller.
App.ChatTabController = App.ChatController.extend();

EmberJS :reload/refresh view after creating new record while using jquery ajax instead of ember-data

I am creating a sample todo app using the jquery to talk to rest api instead of ember-data. The code is given below. I am able to list & add Todos using this code. But once I create a new record I have to refresh the browser to see the new record. how to i make the controller to reload the model?
Here is the Route
App.TodosRoute = Ember.Route.extend({
model: function() {
return Ember.$.getJSON("http://localhost:3000/todos");
}
});
Here is the Controller where I have the action to add new todos
App.TodosController = Ember.ArrayController.extend({
actions: {
addTodo: function(){
var newTodo=this.get('newTodo');
this.set('newTodo', '');
Ember.$.post("http://localhost:3000/todos",{todo: newTodo}));
}
}
});
Here is the template
<script type="text/x-handlebars" data-template-name="todos">
<legend>Todos</legend>
{{input type="text" id="new-todo" value=newTodo placeholder="Todo" action="addTodo" }}
<ul>
{{#each}}
<li><a href='#'>{{todo}}</a></li>
{{/each}}
</ul>
</script>
add new todo in current routes model.
App.TodosController = Ember.ArrayController.extend({
actions: {
addTodo: function(){
var newTodo=this.get('newTodo');
this.set('newTodo', '');
self = this
Ember.$.post("http://localhost:3000/todos",{todo:newTodo}).then(function(response) {
self.addObject(response);
});
}
}
});

Binding model to template with emberjs

I am going to bind model to template with emberjs
<script type="text/x-handlebars" id="dashboard">
<div>
<span>this is user list</span>
<div>
{{render userinfo userinfo}}
</div>
</div>
</script>
<script type="text/x-handlebars" id="_userinfo">
{{#each model}}
<span>{{user}}
{{/each}}
</script>
App.Userinfo= DS.Model.extend({
user: DS.attr("string")
});
App.Userinfo.FIXTURES = [
{user:"user1"},
{user:"user2"},
{user:"user3"}
];
App.UserinfoView= Ember.View.extend({
});
App.UserinfoController = Ember.ObjectController.extend({
});
App.Router.map(function() {
this.resource('dashboard', {path: '/dashboard'}, function() {
});
});
App.DashboardRoute = Ember.Route.extend({
renderTemplate: function() {
this.render('dashboard', { // the template to render
controller: 'dashboard' // the controller to use for the template
});
}
});
App.DashboardController = Ember.ObjectController.extend({
});
When i go to /#/dashboard, Dashboard template is loaded.
In here, I have rendered userinfo.
I'd like to bind Userinfo Model to usersinfo template so that I display all users.
Help me, please.
The short: here a working jsbin.
The long: You hade slightly to much unnecessary going on in your code, basically this does the job:
First of all you had no redirect to your dashboard route, since it's your only route (at least as far I can see from your code) we redirect directly to it after entering the index route
App.IndexRoute = Ember.Route.extend({
redirect: function() {
this.transitionTo('dashboard');
}
});
I've removed the DashboardController since there is nothing to be done.
Then your DashboardRoute was missing the model hook to provide actually data for your dashboard template
App.DashboardRoute = Ember.Route.extend({
model: function(){
return App.Userinfo.find();
}
});
In the router map you don't need to define a path if the URL is the same name as your template name in your case it is the same dashboard
App.Router.map(function() {
this.resource('dashboard');
});
The userinfo model was correct
App.Userinfo= DS.Model.extend({
user: DS.attr("string")
});
But your fixtures where missing the id
App.Userinfo.FIXTURES = [
{id:1, user:"user1"},
{id:2, user:"user2"},
{id:3, user:"user3"}
];
Moreover you where using the render helper with a partial template _userinfo the correct way to render a partial is this
{{partial userinfo}}
As you can see we don't pass any additional parameters to it because the data will be made available trough your model hook. The partial helper uses the context and the data provided in the template it is rendered into, in your case the dashboard template, therefore the model hook is necessary.
Hope it helps.

Ember -- TypeError: arrangedContent.addArrayObserver is not a function

I am trying to build a simple category browser with ember. I have two very simple views. When the user visits / they will see a list of all categories and when they click a category in that list they will be directed to #/10 where 10 is the id.
My problem is that when a user clicks on a category at the / route I am getting the following error
TypeError: arrangedContent.addArrayObserver is not a function
[Break On This Error]
didChange: 'arrangedContentArrayDidChange'
If I refresh the page at the #/10 route the proper api call is made to my backend /api/categories?parent=99. What could I be doing wrong that is throwing this error during the transition? A full example of my code is below.
Templates:
<script type="text/x-handlebars">
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="categories">
{{#each category in controller}}
<p>{{#linkTo 'category' category}}{{ category.name }}{{/linkTo}}</p>
{{/each}}
</script>
<!--this is an array instead of object -->
<script type="text/x-handlebars" data-template-name="category">
{{#each category in controller}}
<p>{{category.name}}</p>
{{/each}}
</script>
Javascript:
var App = Ember.Application.create();
App.Router.map(function(){
this.resource('categories', { path : '/' });
this.resource('category', { path : '/:category_id' });
});
App.CategoriesRoute = Ember.Route.extend({
model: function(){
return App.Category.find();
}
});
//this is causing the error possibly
App.CategoryRoute = Ember.Route.extend({
model: function(params){
return App.Category.find({parent: params.category_id});
}
});
App.CategoryController = Ember.ArrayController.extend();
// Models
App.Store = DS.Store.extend({
revision: 11,
adapter: 'DS.RESTAdapter'
});
DS.RESTAdapter.configure("plurals", {
category: "categories"
});
App.Category = DS.Model.extend({
name: DS.attr('string'),
parent_id: DS.attr('number')
});
Debug info:
DEBUG: Ember.VERSION : 1.0.0-rc.1
DEBUG: Handlebars.VERSION : 1.0.0-rc.3
DEBUG: jQuery.VERSION : 1.9.0
Hint: After writing this i realized that you probably did not get the model hook right. This hook is called when you are entering your app via url. It converts the URL into an appropriate model and transition with this model into the Route. I guess you thought that this model() hook would be called with the arguments of {{#linkTo}}? This is not the case!
This does not work because you are passing a single model to your #linkTo helper in your template. So Ember wants to set this single object as content of your ArrayController. This causes your error. And your model hook returns an array. Rule of Thumb: You should always pass the same data structure to #linkTo, which you are returning in your model hook.
Therefore i would suggest to use an event instead of linkTo and do the following:
<script type="text/x-handlebars" data-template-name="categories">
{{#each category in controller}}
<p {{action 'showParentCategory' category}}>{{category.name}}</p>
{{/each}}
</script>
App.CategoriesRoute = Ember.Route.extend({
model: function(){
return App.Category.find();
},
events: {
showParentCategory : function(parentCategory){
var cats = App.Category.find({parent: parentCategory.get("category_id")});
this.transitionTo("category", cats);
}
}
});
What have i done here?
I created an action called "showParentCategory".
As this is an action with is about routing, i am handling this event in your CategoriesRoute. As you see, events/action handlers are declared in the events property of your route.
I am performing the same logic there as in your model hook and then i am calling manually the transitinTo with the fetched categories.
UPDATE: How to serialize
By implementing serialize, you are telling Ember what to put into your url.
App.CategoryRoute = Ember.Route.extend({
model: function(params){
return App.Category.find({parent: params.category_id});
},
serialize : function(models){
var first = models.objectAt(0);
return {
category_id : first.get("parentId")
}
}
});
If you do #each over a numeric value instead of doing it on an array content in your template, this issue occurs.
I had a numeric value count in my 'poll' model . I was iterating like,
{{#each poll in content.count}}
{{/each}}
I think, we have to use #each only on ember arrays.

How to render a stream view like in twitter of facebook with emberjs arraycontroller?

To render a content of an array with emberjs we usually do the following
<ul>
{{#each controller}}
<li>{{name}} by {{artist}}</li>
{{/each}}
</ul>
How to make a live stream view like we have with twitter (of facebook) where a new stream is added on the top of the streams list ?
On the controller you can set the sortProperties see here to specify on which property the array should be sorted and you can set sortAscending (which is a boolean) to specify which direction the array should be sorted.
When you change the array the view will automatically update.
see this fiddle: http://jsfiddle.net/ZnMFK/2/
or this fiddle: http://jsfiddle.net/KfzFE/ to show the DOM gets updated when the array is changed.
HTML:
<script type="text/x-handlebars" data-template-name="index">
<div class="patient-view extended">
{{#each controller}}
<p>Name: {{name}}</p>
{{/each}}
</div>
</script>
App:
window.App = Em.Application.create();
App.Patient = Em.Object.extend({
order: undefined,
name: undefined
});
App.IndexView = Em.View.extend({
click: function() {
this.get('controller')
.set('sortAscending', !this.get('controller').get('sortAscending'));
}
});
App.IndexController = Em.ArrayController.extend({
sortProperties: ['order'],
sortAscending: false
});
App.IndexRoute = Em.Route.extend({
model: function() {
return Em.A([App.Patient.create({
name: "Bert",
order: 2
}), App.Patient.create({
name: "Ernie",
order: 1
})]);
}
});