Getting model data from controller - ember.js

In Ember if I have a model that is a list of users, if in the UsersController I do:
users = this.get('model');
users.map(function(user) {
user.name
});
should it not resolve the promise and return the user records? I'm confused on why this is not working for me, or how to get the model data the correct way. Thank you in advance.

The model promise is resolved by the router. Ember, by default, sets the controller's content property as the route's model unless you override the route's setupController() method. Your issue lies in the formatting of the map function.
It seems like you're using an array controller, because the model is an array, so do the following:
App.UsersController = Em.ArrayController.extend({
users: function() {
return this.map(function(user) {
return user.get('name');
});
}.property('#each.user'),
});
You can make this code even more streamlined by using Em.Array's mapBy() method, as follows:
App.UsersController = Em.ArrayController.extend({
users: function() {
return this.mapBy('name');
}.property('#each.user'),
});
If you're using the list of users in your template you can do this easily with a {{#each users}} helper. However, if you're using this list for other properties in the controller, be sure to to use the right observer to watch for items being added to the array:
someOtherProperty: function() {
var users = this.get('users');
// Do stuff with users array here...
}.observes('users.[]')
See setting up a route's model and setting up a controller if you're unfamiliar with how the models stuff works.

Related

How is Sorting achieved in an Ember model without using Array Controller?

Every google result is about an ArrayController sorting. Need a sorting mechanism without using ArrayController.
There is a model where there are sort params. Like say 'sortOrder' as one of the properties in the model (which will be from a back end).
Will be rendering this model using #each but this should do the iteration based on the sortOrder property and not the model's ID property.
In Ember 2.0 SortableMixin is deprecated and is on its way out too.
In the Controller (not the ArrayController) you may define a new computed property like SortedUsers1,2,3 below:
export default Ember.Controller.extend({
sortProps: ['lastName'],
sortedUsers1: Ember.computed.sort('model', 'sortProps'),
sortedUsers2: Ember.computed.sort('content', 'sortProps'),
sortedUsers3: Ember.computed('content', function(){
return this.get('content').sortBy('lastName');
})
});
The assumption above is that the model itself is an array of users with lastName as one of user properties. Dependency on 'model' and 'content' look equivalent to me. All three computed properties above produce the same sorted list.
Note that you cannot replace 'sortProps' argument with 'lastName' in sortedUsers1,2 - it won't work.
To change sorting order modify sortProps to
sortProps: ['lastName:desc']
Also if your template is in users/index folder then your controller must be there as well. The controller in users/ would not do, even if the route loading model is in users/.
In the template the usage is as expected:
<ul>
{{#each sortedUsers1 as |user|}}
<li>{{user.lastName}}</li>
{{/each}}
</ul>
Here is how I manually sort (using ember compare)
import Ember from "ember";
import { attr, Model } from "ember-cli-simple-store/model";
var compare = Ember.compare, get = Ember.get;
var Foo = Model.extend({
orderedThings: function() {
var things = this.get("things");
return things.toArray().sort(function(a, b) {
return compare(get(a, "something"), get(b, "something"));
});
}.property("things.#each.something")
});
You just need to include a SortableMixin to either controller or component and then specify the sortAscending and sortProperties property.
Em.Controller.extend(Em.SortableMixin, {
sortAscending: true,
sortProperties: ['val']
});
Here is a working demo.
In situations like that, I use Ember.ArrayProxy with a Ember.SortableMixin directly.
An ArrayProxy wraps any other object that implements Ember.Array
and/or Ember.MutableArray, forwarding all requests. This makes it very
useful for a number of binding use cases or other cases where being
able to swap out the underlying array is useful.
So for example, I may have a controller property as such:
sortedItems: function(){
var items = Ember.ArrayProxy.extend(Ember.SortableMixin).create({content: this.get('someCollection')});
items.set('sortProperties', ['propNameToSortOn']);
return items;
}.property()
Like so: JSBin

How to access model from controller?

I can't seem to get the model from inside the controller, even though the controller seems to have a model property set. The following:
export default Ember.ObjectController.extend({
init: function() {
this._super();
console.log(this.get('model'));
console.log(this.model);
console.log(this);
}
}
prints out:
Any ideas?
So it turns out that when I examine the model by setting a break point it is empty. I assume the console shows content because it updates the model once the content arrives.
In init() the model is unreachable:
init: function() {
this._super();
console.log(this.get('model')); // null
}
Same for any method .on('init'):
onInit: function() {
console.log(this.get('model')); // null
}.on('init'),
But the model is accessible to actions (I'm assuming because the model has been set up by the time the action is called):
someAction: function() {
console.log(this.get('model')); // model object as expected
}
So to answer my question, this.get('model') can be used to access the model from the controller, but just not in init() or .on('init') methods.
The an Ember.ObjectController is a proxy for the model. So the model can be referenced using this as you found in your example. So then in a template {{this.aModelAttr}} or just {{aModelAttr}}. Like you question suggests it's a bit confusing.
The ObjectController is being deprecated as of Ember 1.11.0. So to simplify use Ember.Controller and in your controller you can reference the model by this.get('model')
Then in the template use {{model.aModelAttr}}
To give the model a domain specific ie: books or user name use
export default Ember.Controller.extend({
domainSpecificName: Ember.computed.alias('model')
}
Then in your templates you can use {{domainSpecificName.aModelAttr}}

Ember data - computed property not firing when model is updated with createRecord

For reasons beyond the scope of this question, I have to populate an Ember data model named Activity in my SearchRoute using Ember.$.getJSON in the model hook like this:
App.SearchRoute = Ember.Route.extend({
model: function (params) {
// Create a promise to return to the model hook. The promise will return a DS.RecordArray.
var modelPromise = new Ember.RSVP.Promise(function (resolve, reject) {
// Make the AJAX call to retrieve activities that match the search criteria
Ember.$.getJSON('/services/activities?query=' + params.q).then(function (data) {
data.activities.forEach(function (activity) {
// If the current activity does not already exist in the store...
if (!this.store.hasRecordForId('activity', activity.id)) {
// add the activity to the store
this.store.createRecord('activity', {
id: activity.id,
title: activity.propertyBag.title
});
}
}.bind(this));
resolve(this.store.all('activity', { query: params.q }));
}.bind(this));
}.bind(this));
// Return the DS.RecordArray as the model for the search route
return modelPromise;
}
});
Then, in my SearchController I do some model sorting and filtering before returning the results from a computed property that is bound to a template that displays the results, like this:
App.SearchController = Ember.ArrayController.extend({
filteredActivities: function () {
var model = this.get('model');
// complete various model sorting and filtering operations
return model;
}.property('model')
});
Here's the template:
<script type="text/x-handlebars" data-template-name="activities">
{{#each item in filteredActivities}}
{{item.title}}
{{/each}}
</script>
Every time a search is executed, the model hook in the SearchRoute is refreshed, the AJAX request is made, and the store is updated with new Activity records, if necessary.
The problem is, even if I do create new records in the store using createRecord and return the new store query results to my model hook, the filteredActivities property does not get fired and the template does not update.
I would think that because I'm returning a newly updated DS.RecordArray to the model hook, that Ember would consider my model as having changed and fire any computed properties watching for changes to the model, but I must be missing something.
Does anybody have any ideas?
Sorry for the long post, and thank you so much for taking the time to consider my issue!
Don't use createRecord. Use push.
http://guides.emberjs.com/v1.10.0/models/pushing-records-into-the-store/
Do you try model.[] or model.#each.propertyNameToObserve in computed property filteredActivities?
Examples with #each: http://guides.emberjs.com/v1.10.0/object-model/computed-properties-and-aggregate-data/,
http://guides.emberjs.com/v1.10.0/controllers/representing-multiple-models-with-arraycontroller/

Ember: Accessing a data store in an Ember component

I have component that I want to provide data too. I am using Ember-CLI if that helps.
The component is a map that I am loading onto the page that I than want to place markers on. I used a component so I could use the didInsertElement method to get access to the element once it is ready.
export default Ember.Component.extend({
componentMap: '',
didInsertElement: function() {
navigator.geolocation.getCurrentPosition(position => {
//Initialize map...
this.populateMap();
});
},
populateMap: function() {
//Get store
var store = this.get('parentView.targetObject.store');
console.log(store);
//Search Store
var data = store.find('restaurant');
//Where is the data?!
data.map(item => {
console.log(item.get('name'));
});
}
});
I am having an issues getting the data from a store. I have seen a couple methods, here shows two different methods. First being the this.get('parentView.targetObject.store') or this.get('targetObject.store'). I have also tried the {{component store=store}} method, but that was not working for me either. This might have to do with a fundamental lack of understanding of data flow in an ember app.
I am using Ember CLI and I am wondering if it has anything to do with the context of this inside modules?
If I am way off base as to how I should do this, please let em know!
UPDATE: Adding route, controller and template for context.
Route
export default Ember.Route.extend({
model: function() {
return this.store.find('restaurant');
}
});
Controller
export default Ember.Controller.extend({
actions: {
add: function() {
var $addForm = $('.add-form');
$addForm.show();
}
}
});
Template (index.hbs, which is output in application.hbs {{outlet}})
{{main-map store=store}}
Thanks.
What is happening is as follows:
The model associated with your control is populated as an array of restaurants, not a single map or anything of that sort.
return this.store.find('restaurant'); returns an array of restaurants from the store which ultimately populates the model of your controller.
If you want access to the data contained within your model in your component, you should pass the model as an argument into your component.
So, you can pass the array of restaurants as follows (rename the property as appropriate):
{{main-map data=model}}
Or, if in theory you wanted to display a component for each restaurant:
{{#each restaurant in model}}
{{your-component name=restuarant.name}}
{{/each}}

How to load an object from an id in an Ember View?

I'm trying to build a view to display a user card, from their id. So ideally my calling handlebars would look something like:
<p>{{view App.UserThumb authorId}}
{{comment}}</p>
And then in my UserThumb view, I'd like to be able to load a model, in some sort of setup method or model function, sort of how I'm using controllers:
App.UserThumb = Ember.View.extend({
model: function(view, authorId) {
User.find(authorId, function(user) { view.set('content', user); } );
}
}
Can anyone help me understand the 'right' or at least a workable way to do this? Worst case I can go and create the objects first, but I'd like to just keep the id around for a bit first, unless that is just totally at odds with the philosphy of Ember.
This should do
{{#each id in listOfIds}}
{{view App.UserThumb idBinding="id"}}
{{/each}}
App.UserThumb = Ember.View.extend({
didInsertElement: function() {
var authorId = this.get('id');
User.find(authorId, function(user) { view.set('content', user); } );
}
}
Only after the view is inserted the didInsertElement hook gets executed which gets the required user
The model hook you re using in your View is only available inside a Route. The model hook can be used to setup the model for a controller.
See here the docs for more info on that.
In the spirit of DRY (Dont Repeat Yourself) I'd like to link to this great SO answer that will help setup a functional application, the ember way.
Hope it helps