Ember.js: How to access a model's attributes inside Javascript code - javascript

In Ember.js while using ember-data, I can access a model's attributes like this when in a .hbs template:
{{#each model as |person|}}
Welcome, {{person.firstName}} {{person.lastName}}!
{{/each}}
I tried accessing these values in Javascript code (be it inside a controller or a component) using the following methods:
// Method 1
if (person.firstName === 'Jack') {
// Do something when the first name is Jack
}
// Method 2
if (person.get('firstName') === 'Jack') {
// Do something when the first name is Jack
}
But none of these work to get any attributes of the current model. The only value I can get this way is the id of the current model instance.
I have looked far and wide for a solution to this problem and found nothing, so I ask this question:
Is it possible to access the attributes of a model instance inside Javascript code while using Ember.js and ember-data? If so, how can this be done? If not, why can't I do that?
For reference, here is my current Ember.js setup:
DEBUG: Ember : 2.5.1
DEBUG: Ember Data : 2.5.3
DEBUG: jQuery : 2.2.4
DEBUG: Ember Simple Auth : 1.1.0

When you have an object that you're passing into a component, it becomes a property of the component. So you need to get that object via the component's property before accessing any properties on the model object itself.
Assuming you're passing the object into a component like this:
{{person-profile person=person}}
Either of these should work:
// Method 3
if (this.get('person').get('firstName') === 'Jack') {
// Do something when the first name is Jack
}
// Method 4
if (this.get('person.firstName') === 'Jack') {
// Do something when the first name is Jack
}

Related

Ember.js: accessing computed alias from component's javascript

I currently have a model that contains a computed alias, as follows:
model: DS.belongsTo('device-model'),
manufacturerName: Ember.computed.alias('model.manufacturer.name')
I then have a component that is invoked as follows:
{{my-component model=model}}
Now, in the component's Handlebars template, I can easily access the computed property with {{model.manufacturerName}}, however in my-component.js I am having trouble getting it to work. I have tried with:
console.log(this.get('model').get('manufacturerName'))
However, the output is undefined. So far, the only way I can get the manufacturer name from my-component.js is:
this.get('model').get('model')
.then((model) =>{
return model.get('manufacturer')
})
.then((manufacturer) => {
console.log(manufacturer.get('name'))
})
So, I'm wondering what is the Handlebars template doing that I can't do in its javascript counterpart? It seems like the Handlebars template is following through the promise, whereas I have to do it manually when it comes to the component's javascript.
Thank you!
I think the issue is because of your belongsTo relationship. since { async: true} is the default value for relationships in Ember. so it only fetch the related entities when you actually request them. which means your model is not loaded which means your manufacturerName is not loaded since it is an alias of model.manufacturerName.name.

Ember.js: getting model relationships from component's javascript

I am building a component, and I have noticed a behaviour that seems very odd to me. My component is invoked as follows:
{{my-component model=model}}
My model contains a relationship as follows:
type: DS.belongsTo('type')
Now, in my-component.js, if I log to console this.get('model.type.name') (or this.get('model').get('type').get('name')), I get undefined. However, if in my-component.hbs I insert {{model.type.name}}, the value is displayed correctly.
I don't really understand this behaviour: how can I access a model's relationship from within a component's javascript just like I do in the component's Handlebars template?
Thanks!
In ember-data, relationship is treated as Promise so you should use then for the result.
this.get('model').get('type').then((result) =>{
console.log(' Name ', result.get('name'));
});
Refer:
https://guides.emberjs.com/v2.14.0/models/relationships/#toc_relationships-as-promises

Ember template not updating when array in model is modified

I have a simple array of ints that I'm displaying on a page.
This is my model:
var Favorite = Ember.Object.extend({
stuff: Ember.A([1,2,3,4])
});
This is my template:
{{#each model.stuff as |num|}}
<li>{{num}}</li>
{{/each}}
In my controller, modifications to this array generally display on the page (such as pushObject()), but modifying elements directly does not cause the page to update. I have a button on my page linked to this controller action:
actions: {
arrayAdd() {
this.get('model').stuff[0] = 100;
}
}
Clicking the button modifies the underlying array, but doesn't update the page. Is there a way to have ember automatically pick up on that change?
I'm doing this in Ember 1.13
Actually the equivalent of arr[idx] = foo for an ember array is arr.replace(idx, 1, [foo]).
The other thing you could do is to call this.get('model').notifyPropertyChange('stuff') after you manually edited the array.
In Ember, you need to use KVO(key value observer) compliant methods so that it will trigger computed property recalculation and observer and update the template. Always we need to use get for getting the properties and set for setting the values. if you didn't follow ember will throw assertion error sometime.
For array there are KVO methods which is equivalent to standard method.
Standard Method -> Observable Equivalent
pop -> popObject
push -> pushObject
reverse -> reverseObjects
shift -> shiftObject
unshift -> unshiftObject
In your case, you need to update it like the below, reference twiddle
arrayAdd() {
this.get('model.stuff').unshiftObject(100);
}
Note: You can declare array stuff: [1,2,3,4] it's good to initialize it in init method.
I still feel the array indexer should properly update the value,
though
Interestingly in glimmer component, you don't need to use getters/setters.

Issue with creating the instance of the model in EmberJS

I'm writing simple app in EmberJS and trying to do some simple objects creation in the console. I'm trying to create PageModel and then retrieve it from the store (I use localstorage). Unfortunately it doesn't succeeded.
// creates the PageModel instance
page = store.createRecord(App.PageModel)
Class {id: "i1i67", store: Class, container: Container, _changesToSync: Object, _deferredTriggers: Array[0]…}
// tries to retrieve the object from the storage
store.find('page', page.get('id'))
// but doesn't work
Class {__ember1413471577603: null, __nextSuper: undefined, __ember_meta__: Object, constructor: function, _super: function…}
Can anyone is able to help with ?
If your model is called PageModel, then I'd expect the store to call it page_model. It is unusual to call your model ThingModel- more common is Thing. However, this works too:
store.find(App.PageModel, page.get('id'))

Backbone.js and localStorage plugin relation between collection and model -- based on official 'todo' example

Here's the official sample app:
http://documentcloud.github.com/backbone/docs/todos.html
I am confused about the relationship between the collection and its property localStorage = new Store(..)
Shouldn't this be in the model because you can't do a collection.save() anyway?
In addition, I tried implementing something like it, and it doesn't work
var Person = Backbone.Model.extend({
defaults: {
name:'no-name',
age:0
}
});
var Persons = Backbone.Collection.extend({
model: Person,
localStorage: new Store('Persons'),
initialize: function(){
console.log('collection initialized');
}
});
window.people = new Persons();
var p1 = new Person({name:'JC',age:24});
p1.save({text:'hello'}); //<--- Uncaught TypeError: Cannot read property 'localStorage' of undefined
Can anyone help me figure this out?
It's actually the .create() function of a collection that allows the collection to "save" to localStorage.
source-code of todo sample:
createOnEnter: function(e) {
var text = this.input.val();
if (!text || e.keyCode != 13) return;
Todos.create({text: text});
this.input.val('');
},
This then allows the model instance to manipulate it using the .save({attr:value}) function.
Calling modelInstance.save() without a defined localStorage property in the model's constructor function will cause the error:
Uncaught TypeError: Cannot read property 'localStorage' of undefined
However, being that the model is now saved in the localStorage through the collectionInstance.create() method, modelInstance.save({attr:value}) can now be used to modify it.
So, in conclusion, Models only has the save() function which allows persistence, but the Collection object has the create() function that allows persistence.
In order to use these, REST urls within the collection and model must be properly set up or the localStorage plugin must be instantiated within the Constructor Function of either (depending on setup)
I had a similar problem in that I wanted to simply 'save' a collection that I had loaded from LocalStorage. I wrote a save() method on my Collections that simply looped through each model and called model.save().
MyCollection.save = ->
#each (model) ->
model.save()
There's a big drawback to this, however, with regards to Backbone.LocalStorage. You loose all the great benefits of using Collection.set({ models... }); to update your collection (pulling in an online update, or something) with all the add/merge/delete goodness. Removing the model from your collection at runtime doesn't delete it from local storage, and manually identifying unmatched models and destroying them somewhat defeats the purpose of Backbone.Collection.set();
One solution I found was to augment Backbone such that Backbone.Collection.set() uses destroy() instead of remove() on the models it finds to be missing. (see line 705 of BB 1.0.0)
Another solution, which I wound up going with, was to have all models listen for their own 'remove' event and call their own 'destroy' method when it happens. This allows Backbone.Collection.set()'s removals to become permanent.
class Model extends Backbone.Model
initialize: ->
#on 'remove', #destroy
Both of these mean you can't "remove" a model without destroying it permanently, which was fine for me. If you want, you could probably create some special conditions under which this occurs, and manage it that way.

Categories