EmberJS - Computed property referencing firstObject do not update - ember.js

I have a model like this:
App.Conversation = DS.Model.extend({
body : DS.attr('string'),
created_at : DS.attr('date'),
entry : DS.hasMany('Entry', {async: true}),
user : DS.belongsTo('User'),
allEntriesLoaded : DS.attr('boolean'),
entryProxyBody : function() {
return this.get('entry.firstObject.body');
}.property('entry.firstObject.body')
});
As you can see it references its Entry hasMany relationsship in the function entryProxyBody. This reference works great, as calling entryProxyBody do indeed return the body-attribute from the first object in Entry.
However my problem is, that the computed property is not updated, when a new value is added to the Entry-store.
I add a new record like this:
App.NewController = Em.ObjectController.extend({
actions: {
save: function() {
var entry = this.store.createRecord('entry', {body: 'Test', created_at: new Date() });
this.store.find('conversation', this.parentController.get('id')).then(function(conversation) {
conversation.get('entry').pushObject(entry);
entry.save();
});
}
},
});
However.. If I update the first object in Entry directly using Ember Inspector in Chrome, then the computed property changes as it should.
What am I missing? Thank you for your help!

I think you might want to observe for changes for each entry in the entry array.
entryProxyBody:function() {
return this.get('entry.firstObject.body');
}.property('entry.#each.body')

Related

ember data array UI not updating on pushObject

I have a list of product-tag that I fetch for my model.
Route:
model: function() {
return {
product_tags: this.store.find('product-tag', {merchant: merchId})
}
}
I have a component that adds tags to the model, however when after I create the record and push it into the model (as suggested on other posts) my UI still isn't updating.
addTag: function(name) {
tag = this.store.createRecord('product-tag', {
name: name
});
this.model.product_tags.toArray().addObject(tag);
tag.save();
}
//model merchant.js
export default DS.Model.extend({
user_id: DS.attr('number'),
product_tags: DS.hasMany('product-tag', {async: true})
});
//model product-tag.js
export default DS.Model.extend({
merchant: DS.belongsTo('merchant'),
name: DS.attr('string'),
});
What am I doing wrong? Thanks in advance.
You should make it array in the route, so u can use it always afterwards like u want. Your calling toArray() which makes a new Array instance, then your model is not hooked to the array u just made.
model: function() {
return {
product_tags: this.store.query('product-tag', {merchant: merchId}).then(function(pt) {
return pt.toArray();
});
}
}
var x = this.get('model.product_tags') === model's p_t // true
var y = this.get('model.product_tags').toArray() === model's p_t // false
Later on just do
addTag: function(name) {
this.get('store').createRecord('product-tag', {
name: name
}).save().then(function(saved){ 
this.get('model.product_tags').pushObject(saved);
}.bind(this);
}

Explicit reverses in Ember-Data

My model looks like this:
App.Room = DS.Model.extend({
title : DS.attr('string'),
description: DS.attr('string'),
rooms : DS.hasMany('room', {
async : true,
inverse: 'parent'
}),
parent : DS.belongsTo('room')
});
Viewing existing records works, except creating new records.
I tried it like this:
var self = this,
parent = this.get('content'),
input = this.getProperties('title', 'description'),
newRoom = this.store.createRecord('room', {
title : input.title,
description: input.description,
parent : parent
});
parent.get('rooms').then(function (rooms) {
rooms.pushObject(newRoom);
newRoom.save();
parent.save();
self.transitionToRoute('rooms');
});
But get this error:
Assertion Failed: You defined the 'parent' relationship on (subclass of DS.Model),
but multiple possible inverse relationships of type (subclass of DS.Model) were
found on (subclass of DS.Model).
You're relating an object to itself, so it's likely that Ember-Data's inverse guessing algorithm is getting confused. Declare the inverse on both explicitly.
App.Room = DS.Model.extend({
rooms: DS.hasMany('room', {
async: true,
inverse: 'parent'
}),
parent: DS.belongsTo('room', {
inverse: 'rooms'
})
});
Because it's specifically mentioning the parent relationship, I'm assuming Ember-Data is thinking that it might be its own inverse.

Emberjs promiseArray inside route doesn't return properly

I have a controller for showing item.
Users can put the item in their wish list.
(Item has many users, User has many Items.)
So, when user enter the webpage, I want to show a AddToList or RemoveFromList button to the user based on isAddedToList property.
Below is the code.
User Model:
var User = DS.Model.extend({
username: DS.attr('string'),
email: DS.attr('string'),
avatar: DS.attr('string'),
items: DS.hasMany("item", { async: true })
});
export default User;
ItemModel:
var Item = DS.Model.extend({
name: DS.attr("string"),
priceInCent: DS.attr("number"),
salePriceInCent: DS.attr("number"),
brand: DS.belongsTo("brand"),
itemImages: DS.hasMany("itemImage", { async: true }),
users: DS.hasMany("user", { async: true }),
});
export default Item;
ItemRoute:
var ItemRoute = Ember.Route.extend({
model: function(params) {
var userId = this.get("session").get("userId");
return Ember.RSVP.hash({
item: this.store.find('item', params.item_id),
user: this.store.find('user', userId),
});
},
setupController: function(controller, model) {
controller.set('item', model.item);
controller.set('user', model.user);
}
});
export default ItemRoute;
ItemController:
var ItemController = Ember.Controller.extend({
needs: ["current-user", "application"],
currentUser: Ember.computed.alias("controllers.current-user"),
isAddedToList: function() {
var promiseUsers = this.get("item.users"), user = this.get("user");
return promiseUsers.contains(user);
}.property("item"),
actions: {
addToList: function() {
var item = this.get("item"), user = this.get("user");
item.get("users").pushObject(user);
item.set("addedUserIds", [user.get("id")]);
item.save();
},
removeFromList: function() {
var item = this.get("item"), user = this.get("user");
item.get("users").removeObject(user);
item.set("removedUserIds", [user.get("id")]);
item.save();
}
}
});
export default ItemController;
The problem is when I check the length of promiseUsers with
promiseUsers.get("length")
it always returns 0.
but when I try the same with Chrome console, it returns the length properly.
Do I miss something in the route? How to fix the problem?
The problem is you're using your code synchronously, despite it being an asynchronous property.
The first time you attempt to use an async relationship it will begin resolving the relationship, making a callback to the server is necessary. In your case you try to use the users right away, but they are going to be empty the first time, so you're contains will return false. Since you aren't watching the users' collection it will then update, but the computed property won't update since the computed property was just watching item. This is why when you try it from the console it works, because by the time you attempt to use it in the console it's finished resolving the async collection of users.
isAddedToList: function() {
var promiseUsers = this.get("item.users"), user = this.get("user");
return promiseUsers.contains(user);
}.property("user", 'item.users.[]')

Ember.js access model values

I'd like to be able to modify/validate data before actually saving.
Model
App.Post = DS.Model.extend({
title: DS.attr('string'),
author: DS.attr('string'),
date: DS.attr('date', { defaultValue: new Date() }),
excerpt: DS.attr('string'),
body: DS.attr('string')
});
Route
App.PostsNewRoute = Ember.Route.extend({
model: function() {
return this.get('store').createRecord('post');
},
actions: {
doneEditing: function() {
debugger;
this.modelFor('postsNew').save();
this.transitionTo('posts.index');
}
}
});
So, the questions, before the .save() I want to, let's say, validate that the title is not empty or so.
Everything I've tried gets undefined, or [Object object] has no .val() method. I don't know how to get to the values of the model. How can I do that?
And the other thing I have in mind. Is that defaultValue working as intended? I want to set Date() to every new created post. Somehow date is not being recorded since it's not showing.
Thanks.
App.PostsNewRoute = Ember.Route.extend({
model: function() {
return this.get('store').createRecord('post');
},
actions: {
doneEditing: function() {
debugger;
var model = this.modelFor('postsNew');
var title = model.get('title');
model.save();
this.transitionTo('posts.index');
}
}
});

Ember-data model computed value, return value instead of promise

I have ember models called survey, question, and response. surveys have multiple questions, which have multiple responses. Each response has an attribute count.
How do I set a total_response_count computed value in the survey model? In emberjs 1.0.0, the questions are in a DS.PromiseArray (due to the async: true), so when I return the computed value, it shows up in my template as an Object rather than a value.
I can easily access responses from the question model because responses are embedded in question. However, Ember automatically makes promises for the questions referenced by survey because {async: true}.
Survey Model:
App.Survey = DS.Model.extend({
title: DS.attr('string'),
owner_id: DS.belongsTo('user'),
questions: DS.hasMany('question', {async:true}),
total_responses: function() {
var question_cb = function(prevValue, item) {
return prevValue + item.get('total_responses');
};
return this.get('questions').then(function(questions){
return questions.reduce(question_cb, 0);
});
}.property('questions')
});
Question Model:
App.Question = DS.Model.extend({
survey: DS.belongsTo('survey'),
question: DS.attr('string'),
responses: DS.hasMany('response'),
total_responses: function() {
var response_cb = function(prevValue, item) {
return prevValue + item.get('count');
};
return this.get('responses').reduce(response_cb, 0);
}.property('responses')
});
Response Model:
App.Response = DS.Model.extend({
response: DS.attr('string'),
count: DS.attr('number'),
question: DS.belongsTo('question')
});
I'm using ember-1.0.0 and ember-data 1.0 beta-2.
I also asked this question on Github, and got this response from Yehuda Katz:
You can try something like this:
App.Survey = DS.Model.extend({
title: DS.attr(),
owner: DS.belongsTo('user'),
questions: DS.hasMany({ async:true }),
totalResponses: Ember.arrayComputed('questions', {
initialValue: 0,
addedItem: function(accum, item) {
accum += item.get('totalResponses');
},
removedItem: function(accum, item) {
accum -= item.get('totalResponses');
}
})
});
When questions resolves, the addedItem callback in totalResponses will be called once for every item in the resolved array.