Computer Property not updating on hasMany - ember.js

I have a pretty simple model:
//models/build
import DS from 'ember-data';
export default DS.Model.extend({
shipments: DS.hasMany('shipment')
});
I need to update a computer property whenever a new shipment is added or removed, so I'm using my controller, like this:
//controllers/build
import Ember from 'ember';
export default Ember.Controller.extend({
init: function() {
this.get('allShips');
this.get('ships');
},
allShips: Ember.computed.alias('model.shipments'),
ships: function() {
console.log('updating ships');
}.property('model.#each.shipments')
});
I'm not consuming the computer properties in my template anywhere, I just need to keep them updated to do some computational work so I'm just getting them in the init function. I'm getting "updating ships" in the console when I'm entering the build route correctly, but after that it won't update no matter how many shipments I add or remove.
I've tried many different properties, model.ships.#each, allShips, #each.allShips, allShips.#each, and dozens of all combinations, but all were in vain. I've never had this kind of trouble with computer properties so any advice would be appreciated.

The placement of #each is incorrect - it should be after model.shipments.
As one of the comments state - if you're not accessing properties (e.g. model.shipments.#each.id then you should observe models.shipments.[].
For some reason the computed property only updates for me when returning an array - not returning a value or returning any type of value other than an array fails.
Model
//models/build
import DS from 'ember-data';
export default DS.Model.extend({
shipments: DS.hasMany('shipment')
});
Controller
//controllers/build
import Ember from 'ember';
export default Ember.Controller.extend({
init: function() {
this.get('allShips');
this.get('ships');
},
allShips: Ember.computed.alias('model.shipments'),
ships: function() {
var ships = this.get('model.shipments');
console.log('updating ships');
return [];
}.property('model.shipments.#each')
});
It's probably best to observe model.shipments.[] if you're not returning a value:
//controllers/build
import Ember from 'ember';
export default Ember.Controller.extend({
doShipsWork: function() {
console.log('updating ships');
}.observes('model.shipments.[]')
});

Related

ember-pouch: does not load hasMany relationship when the belongsTo side is polymorphic

I have an ember-data app that uses the ember-pouch adapter for local & remote storage.
I am unable to load hasMany relationships when the belongsTo side is polymorphic. I've played with async: true/false and donstsave: true/false options on the hasMany side, to no avail.
The setup:
post can have many comments.
comment can have many comments.
comment belongs to commentable.
// app/models/post.js
import DS from 'ember-data';
import { Model } from 'ember-pouch';
export default Model.extend({
DS.hasMany('comment', { inverse: 'commentable' });
});
// app/models/comment.js
import DS from 'ember-data';
import { Model } from 'ember-pouch';
export default Model.extend({
DS.belongsTo('commentable', { polymorphic: true });
DS.hasMany('comment', { inverse: 'commentable' });
});
The Problem
Calling post.get('comments') loads nothing. If comments are loaded into the store separately, however, then post is able to correctly render comments:
// In the console (being mindful that `post.get('comments')` returns a promise)
const post = store.findRecord('post', '123');
post.get('comments').get('length'); // => 0
store.findAll('comment');
post.get('comments').get('length'); // => 12
What worked for me during an experiment (although I was heavily modifying ember-pouch within the adapter) was using post.get('comments').content.length but don't ask me why this is so and if it is supposed to be that way ...
EDIT:
It seems the problem is that the data is not loaded at that time. So probably something like post.get('comments').then(function() {this.debug(post.get('comments').length})) will work.

How to hand off model data to component in Ember 2.x

I have a route in ember which looks like
//fish.js
import Ember from 'ember';
export default Ember.Route.extend({
model: function(params) {
return Ember.RSVP.hash({
fishPrices: this.store.query('fish-price', {filter: {type: params.type}}),
type: params.type
});
}
});
My fish.hbs uses model.type to change text on the page. However, I need to hand model.fishPrices off to a component which plots the price of the fish as a function of time:
//fish-price-plot.js
import Ember from 'ember';
/* global Plotly */
export default Ember.Component.extend({
didInsertElement() {
console.log(this.get('model'));
Plotly.plot( 'fish-price-plot', [{
// ...
//Need to access model.fishPrices here
}
});
How do I access the model in this component? I see a lot of information that suggests that I should be able to do something like
var fishPrices = this.get('model.fishPrices');
//do something with fishPrices
but this always ends up undefined.
One way is directly passing it to the component props like this:
// route template in which you want to use your component & model
{{fishprice-plot model=model}}
Have a look at the following twiddle that demo's the first use case.
The other is injecting a service into the component with the required data.
// components/fishprice-plot.js
export default Ember.Component.extend({
fishData: Ember.inject.service()
});
Have a look at this twiddle that demonstrates passing data to a component more comprehensively, and also this part of the guides, as pointed out by #locks in comments.
You can also have a look at this SO link regarding passing properties to your component.

Cannot pass queryParams

I'm newbie in ember.js and i'm trying to sort a list of products in my app.
I have a route catalog/category.js:
import Ember from 'ember';
export default Ember.Route.extend({
queryParams: {
ordering: {
refreshModel: false,
},
},
model (params) {
return Ember.RSVP.hash({
categories: this.store.peekAll('category'),
category: this.store.peekRecord('category', params.category_id),
});
}
});
and controller catalog/category.js:
import Ember from 'ember';
export default Ember.Controller.extend({
queryParams: ['ordering'],
ordering: null,
sortedProducts: Ember.computed.sort('model.category.products', 'ordering'),
});
Link passes a parameter:
{{#link-to 'catalog.category' (query-params ordering='price')}}Price Asc{{/link-to}}
ordering parameter is setting to price and it doesn't work. But when I manually set the ordering parameter to ['price'] - everything works as expected.
Can anyone suggest how to fix it?
ordering parameter is setting to price and it doesn't work. But when I
manually set the ordering parameter to ['price'] - everything works as
expected.
There is a difference between string:price (set by query param) and array:['price'] (set by you manually).
You need another property which will create that array for you:
import Ember from 'ember';
export default Ember.Controller.extend({
queryParams: ['ordering'],
ordering: null,
orderingObserver: Ember.on('init', Ember.observer('ordering', function() {
let ordering = this.get('ordering');
let array = [];
if (ordering) {
array.push(ordering);
}
this.set('orderingArray', array);
})),
sortedProducts: Ember.computed.sort('model.category.products', 'orderingArray'),
});
isn't it supposed to work "out of the box"?
It works with arrays of properties - http://emberjs.com/api/classes/Ember.computed.html#method_sort.

What array name does Ember Data expect for sub directory models?

I recently started learning Ember and using Ember-CLI so I'm not quite well educated about Ember Data and what array names it expects for relationships that are in sub directories in my app.
// models/server.js
import DS from 'ember-data';
export default DS.Model.extend({
serverHistory: DS.hasMany("history/server", { async: true })
});
// models/history/server.js
import DS from 'ember-data';
export default DS.Model.extend({
server: DS.belongsTo("server", { async: true })
});
I've tried returning these names from my API
server_historys_ids
server_histories_ids
history_server_ids
history_servers_ids
But I don't see an XHR request for Server history in my application. The servers itself are fetched fine.
Update
I changed my relationship name and the API is returning history ids but I'm still not getting an history json request even though I'm trying to each in the template. The game relationship data is accessible in the template and a request is successfully made.
// models/server.js
import DS from 'ember-data';
export default DS.Model.extend({
// attr's here.. not relevant
// Relationships
game: DS.belongsTo("game", { async: true }), // works
serverHistories: DS.hasMany("history/server", { async: true }) // doesn't make a request like game does.
});
I also have an adapter/history/server.js but it's only telling what namespace to use - "api".
Update 2
I think the problem may be in the way I'm calling the data to the model.
// routes/server/view/index.js
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
var parentModel = this.modelFor("server.view");
return this.store.query("server", { server_address: parentModel.server_address });
// return this.store.find("server", 1);
}
});
How come when I use find with an id it updates the template data and when I use query with parameters it doesn't?
Update 3
So I got my find and query problem sorted out, here's the way I got it to work: https://stackoverflow.com/a/31831667/1814027
The relationship problem still persists. I see no serverHistory data in my Ember toolbar nor a request being made to the API for it.
I beleive serverHistory is anti-conventional name for hasMany and serverHistories should be instead.
export default DS.Model.extend({
serverHistories: DS.hasMany("history/server", { async: true })
});
Then in case of ActiveModelAdapter expected server payload is:
{"server": {"id": 1, "server_history_ids": [1,2,3]}}
It doesn't depend on the fact that serverHistory is namespaced model, it depends on relation name only.
For example for model:
// models/server.js
import DS from 'ember-data';
export default DS.Model.extend({
bars: DS.hasMany("history/server", { async: true })
});
expected payload is:
{"server": {"id": 1, "bar_ids": [1,2,3]}}
Update
Working ember-cli example: https://github.com/artych/so_ember_data_subdir
Artych's answer helped me on the right path but Ember didn't want to recognise server_history_ids so I just renamed the hasMany relation to histories and returned histories: [] from my API. Now it works.. don't know why but it works.

Ember queryParams not updating URL

I'm trying to set queryParams in an Ember controller, but they don't seem to be updating the URL at all.
I have this abbreviated mixin being applied to the route:
import Ember from 'ember';
import ControllerPaginationMixin from './controller-pagination';
export default Ember.Mixin.create({
setupController(controller, model) {
this._super(controller, model);
controller.reopen(ControllerPaginationMixin);
}
});
And here's the abbreviated controller mixin that is applied above:
import Ember from 'ember';
export default Ember.Mixin.create({
sortKey: null,
queryParams: ['sortKey'],
actions: {
sort(key) {
this.set('sortKey', key);
}
});
When I call the sort method from a component, I can see in the Ember Inspector that the sortKey property has been changed to the correct new value, but the URL remains unchanged. Am I missing something?
Your problem is that you're trying to customize the controller class at the runtime.
You will reopen the controller every time a user visits the route, that's ridiculous.
Simply extend the controller definition with the mixin and you're good to go.