I'm putting together an app that displays a list of stores (with add/edit/delete options), and clicking on a store name takes you to the list of items in that store (again with add/edit/delete).
The model:
// app/models/shop.js
import DS from 'ember-data';
export default DS.Model.extend({
shopName: DS.attr('string'),
shopDetails: DS.attr('string'),
shopStock: DS.attr('array', {
defaultValue() {
return [];
}
})
});
Basically model should be as:
{
"shopName": "someName",
"shopDetails": "someDetails",
"shopStock": [
{
"name": "foo",
"description": "bar",
"price": "555"
}
]
}
For each shop the route is dynamical:
// app.router.js
Router.map(function() {
this.route('shop', function() {
this.route('stock', { path: '/:shop_id/stock' });
this.route('edit', { path: '/:shop_id/edit' });
});
});
And in the controller I have:
actions: {
saveItem() {
const newItem = {
name: this.get('itemName'),
description: this.get('itemDescription'),
price: this.get('itemPrice')
};
}
}
The question is, how do I push the newItem object into model's shopStock array?
Since you want to create/edit/save/delete the related child records, you should create a new model for the child (shopStock) that belongsTo the parent (shop).
// app/models/shop-stock.js
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string'),
description: DS.attr('string'),
price: DS.attr('string'),
shop: DS.belongsTo('shop')
})
});
Your shop model should also have another field added, shopStocks: DS.hasMany('shop-stock').
When you want to add child records to the parent, you will use the .pushObject() method. See the Model Relationships section of the Guides for more details.
Related
While generating an instance of a staffMember within ember-cli-mirage I'm attempting to create a 'task' and assign it to the key 'tasksCreated' within the staffMember model. My current code is as follows;
It's creating a task, and creating a staffMember but the two have no relationships built between them.
app/models/staff-member.js
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string'),
atWork: DS.attr('boolean'),
avatar: DS.attr(),
tasksCreated: DS.hasMany('task', {
inverse: 'creator'
}),
tasksAssigned: DS.hasMany('task', {
inverse: 'assignee'
})
});
app/models/tasks.js
import DS from 'ember-data';
export default DS.Model.extend({
creator: DS.belongsTo('staff-member', { inverse: null }),
assignee: DS.belongsTo('staff-member', { inverse: null }),
creationDate: DS.attr(),
description: DS.attr('string'),
urgency: DS.attr()
});
mirage/models/staff-member.js
import {Model, hasMany} from 'ember-cli-mirage';
export default Model.extend({
tasksCreated: hasMany('task'),
tasksAssigned: hasMany('task', { inverse: 'assignee'})
});
mirage/models/task.js
import {Model, belongsTo} from 'ember-cli-mirage';
export default Model.extend({
creator: belongsTo('staff-member'),
assignee: belongsTo('staff-member')
});
mirage/factories/staff-member.js
import { Factory, faker, trait } from 'ember-cli-mirage';
export default Factory.extend({
name: faker.name.firstName,
atWork: faker.random.boolean,
avatar: faker.image.avatar,
withTasks: trait({
afterCreate(staffMember, server){
server.createList('task',2,{tasksCreated: [staffMember]});
}
})
});
mirage/factories/task.js
import { Factory, faker } from 'ember-cli-mirage';
export default Factory.extend({
creationDate: faker.date.recent,
description: faker.lorem.sentence,
urgency: faker.random.number({
'min': 0,
'max': 4
})
});
mirage/scenarios/default.js
export default function(server ) {
server.createList('staff-member', 4, 'withTasks');
}
I tried to remove most of the code that wasn't directly related to setting the relationships up. Before fiddling with the factories, try to get the relationships working in just the default scenario.
I've made a working twiddle here: https://ember-twiddle.com/5bcdcdee50faa0c0a679c3c4d35fe0ea?openFiles=mirage.scenarios.default.js%2C
Here's what the default scenario looks like:
export default function(server ) {
let member1 = server.create('staff-member');
server.createList('task', 2, { creator: member1 });
server.create('task', { assignee: member1 });
let member2 = server.create('staff-member');
server.create('task', { creator: member2 });
server.createList('task', 3, { assignee: member2 });
}
As far as your factory afterCreate code goes, one error is in this line
server.createList('task',2,{tasksCreated: [staffMember]});
You're creating a task, but passing in tasksCreated as a property of that task. But tasks don't have a tasksCreated property, staffMembers do. Maybe something like this is what you're looking for:
server.createList('task', 2, { creator: staffMember });
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);
}
I started working on a new ember project. I have never used ember before.
I am working with an api that does not conform with the JSON API spec and does not have a websocket.
So I poll the api to get the latest data.
I can get the latest data but it is rendered in the view on the bottom instead of the top. I have looked at 1, 2, 3 to no avail.
How do I get the new data to render at the top of the list?
//sample output after a new job fetched
2
1
3
//desired output
3
2
1
This is a new project so I don't want to use anything that will be depreciated in 2.0 (controllers, etc.). I am open to changing the model(s) if that works.
My route looks like this:
//routes query.js
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
var interval = 1000; // every second
Ember.run.later(this, function() {
this.model().then(function(json) {
this.controller.set('model', json);
}.bind(this));
}, interval);
return Ember.RSVP.hash({
query: this.store.find('query'),
job: this.store.peekAll('job')
});
},
});
My models are:
//models query.js
import DS from 'ember-data';
import Ember from 'ember';
export default DS.Model.extend({
jobs: DS.hasMany('job'),
count: DS.attr('number'),
jobQ: ['took'],
jobsSorted: Ember.computed.sort('jobs', 'jobQ'), <-- this doesn't seem to work
});
//models job.js
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string'),
submitter: DS.attr('string'),
submission_time: DS.attr('date'), // '2015-04-27 15:14:55', // date?
completion_time: DS.attr('date'), // '2015-04-27 15:15:08',
took: DS.attr('number'),
statuses: DS.attr('string'), // object
query: DS.belongsTo('query'),
});
So my serializer is:
// serializers query.js
import DS from 'ember-data';
import assign from 'npm:object-assign';
export default DS.RESTSerializer.extend({
isNewSerializerAPI: true,
normalizeResponse: function (store, primaryModelClass, payload) {
// delete things we don't want
delete payload.error;
delete payload.status;
// populate array
var jobs = [],
relationships = [];
// copies the jobs to a new var keep the payload clean
Object.keys(payload.jobs).forEach((key) => {
let attributes = {};
assign(attributes, payload.jobs[key]);
delete attributes.id;
jobs.push({ id: key, type: 'job', attributes: attributes});
relationships.push({ id: key, type: 'job'});
});
var c = payload.count;
//jobs
// [{
// id:
// type:
// attributes: {
// name:
// filter:
// ...
// },
// ...
// }]
return {
data: {
id: 1,
type: 'query',
relationship: {
job: {
data: relationships
}
},
attributes: { count: c }
},
included: jobs
};
}
});
A template that looks like this would work just fine:
{{#each model.query as |query|}}
{{#each query.jobsSorted as |job|}}
{{job.took}}
{{/each}}
{{/each}}
What does your template look like? Although to sort it on a descending order you would need to add :desc to the sort order:
export default DS.Model.extend({
jobs: DS.hasMany('job'),
count: DS.attr('number'),
jobQ: ['took:desc'],
jobsSorted: Ember.computed.sort('jobs', 'jobQ')
});
Here's a JSFiddle demonstrating that Ember.computed.sort works like you're trying to use it: http://emberjs.jsbin.com/towerukafa/3/edit?html,css,js,output
I have an ember model Category:
export default DS.Model.extend({
name: DS.attr('string'),
img: DS.attr('string'),
url: DS.attr('string'),
cnt: DS.attr('number'),
// parent_id: DS.belongsTo('category', {
// inverse: 'children',
// async: true
// }),
parent_id: DS.attr('string'),
// children: DS.hasMany('category', {
// inverse: 'parent_id',
// async: true
// }),
children: DS.attr(),
isSelected: false,
isExpanded: false,
hasChildren: function() {
return this.get('children').get('length') > 0;
}.property('children').cacheable(),
isLeaf: function() {
return this.get('children').get('length') == 0;
}.property('children').cacheable()
});
In my index route I have:
export default Ember.Route.extend({
model: function() {
var store = this.store;
return Ember.ArrayProxy.create({
categories: store.find('category'),
menuTopCategories: store.find('category', { parent_id: 1 })
});
}
});
I'm using a RESTAdapter so the store.find will send two requests to the server: categories and categories?parent_id=1.
I would like to have only the first request and then filter through the categories. I tried store.all - since I saw it reuses the already fetch data, but I can't manage to apply the filter.
I've rewritten the menuTopCategories and I don't see a new request:
menuTopCategories: store.filter('category', function(category) {
return category.get('parent_id') === "1";
})
My problem right now is to get the root category (first one) without hardcoding the parent_id.
I have 5 models in some relations:
App.Service = DS.Model.extend({
name: DS.attr('string'),
service_prices: DS.hasMany('servicePrice')
});
App.ServicePrice = DS.Model.extend({
unit_price: DS.attr('number'),
qty_unit: DS.belongsTo('qtyUnit'),
service: DS.belongsTo('service'),
partner:DS.belongsTo('partner')
});
App.Partner = DS.Model.extend({
"name": DS.attr('string')
});
App.QtyUnit = DS.Model.extend(Ember.Validations.Mixin, {
name: DS.attr('string'),
});
App.Order = DS.Model.extend({
service: DS.belongsTo('service'),
unit_price: DS.attr('numeric'),
qty_unit:DS.belongsTo('qtyUnit')
});
I try to load an order with the following JSON:
var order = {
"order" : {"id":1,"service":1,"qty_unit":4,"unit_price":10},
"service":[{"id":1,"name":"ENG-GER","service_prices":[1,2]}],
"servicePrices":[
{"id":1,"qty_unit":4,"unit_price":3,"partner":1},
{"id":2,"qty_unit":5,"unit_price":4,"partner":1}
],
"qtyUnits":[
{"id":4,"name":"character"},
{"id":5,"name":"word"},
{"id":6,"name":"sentence"}
],
"partner":[
{"id":1,"name":"Jessie Bains"}
]
};
But im getting the following error:
Error while loading route: TypeError: Cannot read property 'deserialize' of undefined
Is my Json wrong structured?
Here is the JsBin:
http://jsbin.com/finahuna/12/edit
When requesting records, the relationships in the json should be plural (services, partners)
var order = {
"order" : {"id":1,"service":1,"qty_unit":4,"unit_price":10},
"services":[{"id":1,"name":"ENG-GER","service_prices":[1,2]}],
"servicePrices":[
{"id":1,"qty_unit":4,"unit_price":3,"partner":1},
{"id":2,"qty_unit":5,"unit_price":4,"partner":1}
],
"qtyUnits":[
{"id":4,"name":"character"},
{"id":5,"name":"word"}
],
"partners":[
{"id":1,"name":"Jessie Bains"}
]
};
Additionally your jsbin isn't working per say because:
services and qtyUnits didn't exist in the scope (possibly you debugging)
return Ember.RSVP.hash({
order:store.find('order',1),
services: store.all('service'),
qtyUnits: store.all('qtyUnit')
});
If your controller has an object backing it is needs to extend ObjectController not Controller
App.IndexController = Ember.ObjectController.extend({
});
Example: http://jsbin.com/wimoz/1/edit