How to manually create an Array of ember-data objects? - ember.js

I have the need to override the find on a specific object in my ember.js app. What I have below works great if I was planning to return a "single" ember-data object, but how would I modify this to return an Array of objects (such that I return a AdapterPopulatedRecordArray or RecordArray (not sure the difference to be honest)).
App.Foo = DS.Model.extend({
name: DS.attr('string')
}).reopenClass({
find: function() {
var result = Ember.Object.create({
isLoaded: false
});
$.getJSON('/api/foos/', function(data) {
result.setProperties(data);
result.set('isLoaded', true);
});
return result;
}
});

You can return an array as follows:
App.Foo = DS.Model.extend({
name: DS.attr('string')
}).reopenClass({
records: [],
find: function() {
var self = this;
$.getJSON('/api/foos/', function(response) {
response.forEach(function(record){
self.records.addObject(App.Foo.createRecord(record))
}, this)
});
return this.records;
}
});
This example (adapted from http://trek.github.com/) returns a simple array. To return a record array (or adapter populated record array) will
require more work but it is the same basic pattern. Have a look at this test for detail on what would be required:
https://github.com/emberjs/data/blob/master/packages/ember-data/tests/unit/record_array_test.js

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);
}

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.[]')

Implementing model filter

I have set up the following scaffolding for my Ember application.
window.App = Ember.Application.create({});
App.Router.map(function () {
this.resource('coaches', function() {
this.resource('coach', {path: "/:person_id"});
});
});
App.ApplicationAdapter = DS.FixtureAdapter.extend({});
App.Person = DS.Model.extend({
fname: DS.attr('string')
,lname: DS.attr('string')
,sport: DS.attr('string')
,bio: DS.attr('string')
,coach: DS.attr('boolean')
,athlete: DS.attr('boolean')
});
App.Person.FIXTURES = [
{
id: 10
,fname: 'Jonny'
,lname: 'Batman'
,sport: 'Couch Luge'
,bio: 'Blah, blah, blah'
,coach: true
,athlete: true
}
,{
id: 11
,fname: 'Jimmy'
,lname: 'Falcon'
,sport: 'Cycling'
,bio: 'Yada, yada, yada'
,coach: false
,athlete: true
}
];
I am trying to set up a route to filter the person model and return only coaches. Just to make sure I can access the data, I have simply used a findAll on the person model.
App.CoachesRoute = Ember.Route.extend({
model: function() {
return this.store.findAll('person');
}
});
Now though, I am trying to implement the filter method detailed on the bottom of the Ember.js Models - FAQ page.
App.CoachesRoute = Ember.Route.extend({
model: function() {
var store = this.store;
return store.filter('coaches', { coach: true }, function(coaches) {
return coaches.get('isCoach');
});
}
});
The coaches route is not working at all with the new route implemented and the old one commented out. I am using the Ember Chrome extension and when using the filter route the console responds with, Error while loading route: Error: No model was found for 'coaches'. Apparently the route is not working, specifically the model. No kidding, right? What am I missing in my filter model route?
Thank you in advance for your help.
The error message is spot on- there is no CoachModel. I think you need to do this:
App.CoachesRoute = Ember.Route.extend({
model: function() {
var store = this.store;
return store.filter('person', { coach: true }, function(coaches) {
return coaches.get('isCoach');
});
}
});

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');
}
}
});

How to use DS.Store.registerAdapter

I try to use the 'adapter per type' feature of ember-data. Not sure whether I'm doing something wrong or if it's a bug in ember-data. Basically i thought it would work like this:
Person = DS.Model.extend({
name: DS.attr('string')
});
var personAdapter = DS.Adapter.create();
DS.Store.registerAdapter(Person, personAdapter );
The store always uses the the default adapter and not the registered 'per type adapter'
I wrote a test case to show what I mean:
var get = Ember.get, set = Ember.set;
var Person, store, adapter, personAdapter;
module("DS.Store and DS.Adapter 'adapter per type' integration test", {
setup: function() {
Person = DS.Model.extend({
name: DS.attr('string')
});
adapter = DS.Adapter.create();
personAdapter = DS.Adapter.create();
DS.Store.registerAdapter(Person, personAdapter);
store = DS.Store.create({ adapter: adapter });
},
teardown: function() {
adapter.destroy();
store.destroy();
}
});
test("test function on the per type adapter", function() {
adapter.find = function(store, type, id) {
ok(false, "should call find on the registered 'per type adapter' not on the default one");
};
personAdapter.find = function(store, type, id) {
store.load(Person, {
'id': 1,
'name': "My Name"
});
};
var person = store.find(Person, 1);
equal(person.get('name'), "My Name");
});
While debugging I noticed that the "DS.Mappable._reifyMapping" mixing explicitly checks for DS.Store and stops if the "this" is a DS.Store.
This should work.
var get = Ember.get, set = Ember.set;
var Person, StoreClass, store, adapter, personAdapter;
module("DS.Store and DS.Adapter 'adapter per type' integration test", {
setup: function() {
Person = DS.Model.extend({
name: DS.attr('string')
});
adapter = DS.Adapter.create();
personAdapter = DS.Adapter.create();
StoreClass = DS.Store;
StoreClass.registerAdapter(Person, personAdapter);
store = StoreClass.create({ adapter: adapter });
},
teardown: function() {
adapter.destroy();
store.destroy();
}
});
...
The previous answers here are no longer valid (as of Ember Data 1.0.beta.1).
Per the changelog, you now use the ModelNameAdapter syntax. For example,
App.Person = DS.Model.extend({
...
});
App.CustomAdapter = DS.Adapter.create({
url: 'your/custom/url'
});
App.PersonAdapter = App.CustomAdapter;
There is a bug report for this: Per-type Adapter not respected in case of commit
Edit: removed inappropriate comment.