Ember list of model not reloaded without refreshing page - ember.js

I'm displaying only published articles by sorting my list with the 'published' attribute.
Now when I edit an article and set it from 'published' to 'draft' and then I return to the list I see the 'draft' article even if I wrote a filter in my controller.
How i'm saving
article.set('isPublished', true);
article.save();
this.transitionToRoute('article.list');
Route :
model() {
return this.get('store').findAll('articles');
}
Controller :
articleSorted: computed.filterBy('model', 'isPublished', true),
Besides before I refresh the page some article are still 'draft' and when I refresh they are 'published'... Just going to another page and return to the list, or doing a browser refresh is enough to list properly only 'published' articles and so solve my problem.
Where am I suppose to look to solve my problem without refreshing ?
Thanks

I'm taking a best guess here based on your question and comments. Have full route and controller code would be helpful, so if this doesn't help I'll need that information.
Based on:
Just going to another page and return to the list, or doing a browser refresh is enough to list properly only 'published' articles and so solve my problem.
I would guess that there is an issue loading the articles or else the computed property is not being re-evaluated when isPublished changes.
I would try to load everything and filter it in a computed property. This might looks like:
import Route from '#ember/routing/route';
import { inject as service } from '#ember/service';
export default Route.extend({
store: service(),
model() {
return this.store.findAll('article');
}
});
import { computed } from '#ember/object';
import Controller from '#ember/controller';
export default Controller.extend({
articles: computed('model.#each.isPublished', function () {
return this.model.filterBy('isPublished');
}),
});
This will load all the articles in the model hook and then handle the filtering in a computed property. When the isPublished property changes on any one of the articles then the list should updated.
The reason for the delay in updating is probably due to the way you're saving the change. When running .save() it's an asynchronous operation that you need to wait on before transitioning. Try:
actions: {
async publishArticle(article){
article.set('isPublished', true);
await article.save();
this.transitionToRoute('article.list');
}
}
Which will wait for the promise to resolve first.

First try to set variable into the model , then make model.save(). like
article.set('name', 'draft');
article.save().then(transitionToarticle).catch(failure);; // => PATCH to '/article/id'

Related

EmberJS: refreshing a data model does not update associated computed properties

Let's say there is a route with capabilities to update it's data when requested by the user (assume the backend returns different data for the same call, maybe it's stock data, or just random numbers).
export default Ember.Route.extend({
model() {
return this.get('store').findAll('foo');
},
actions: {
invalidateModel() {
this.refresh();
}
}
});
Now, a component consuming this model directly will update its view as expected.
Model: {{#each model as |m|}}{{m.bar}}{{/each}}
<button {{action "refreshModel"}}>Refresh model</button>
But, if the component is using a computed property observing the model, then the updates do not carry through.
Template
Model: {{#each computedModel as |m|}}{{m}}{{/each}}
<br>
<button {{action "refreshModel"}}>Refresh model</button>
Component
computedModel: Ember.computed('model', function() {
return this.get('model').map(function(m) {
return `Computed: ${m.data.bar}`;
});
}),
For a full repro, you can check out: https://github.com/myartsev/ember-computed-properties-on-data-model
The latest commit is the non-working computed properties case.
The previous commit is when everything is still working correctly when using the model directly.
What am I missing?
Your computed property is listening for changes to the array itself. Try listening for changes to the arrays items with model.[]
https://guides.emberjs.com/v2.15.0/object-model/computed-properties-and-aggregate-data/#toc_code-code-vs-code-each-code
computedModel: Ember.computed('model.[]', function() {
return this.get('model').map(function(m) {
return `Computed: ${m.data.bar}`;
});
}),
UPDATE
Here is a twiddle showing that the above solution fixes the problem.
If it's not working on your end then there is some issue with what your api is returning.
As per my comments about how to send actions. You are using 2 year old syntax from Ember 1.13 which I am not familiar with.
I suggest you read the docs for the version you are running Ember 2.15
computedModel: Ember.computed('model.#each.bar', function() {
return this.get('model').map(function(m) {
return `Computed: ${m.data.bar}`
});
})
To close the loop; the answer from #Subtletree was very close and it got me thinking on the right track.
The difference was subtle but important enough, model.[] will only work if the size of the data being returned changes; elements are added or removed. In my case, the size of the data returned remained constant, only it's value got updated. So the correct way was to listen to the dependent key you are looking for, in this case, 'model.#each.bar'.

how to filter model to get data in realtime in emberfire

so i have quota model like this :
export default DS.model.extend({
quota : DS.attr('number'),
sellerId: DS.attr('string'),
buyerId:DS.attr('string') });
and i have assignQuota routes with dynamic segment like this:
this.route('assignQuota', {path:'/assignQuota/:buyer_id'}
and in assignQuota.js :
export default Ember.Route.extend({
model(params) {
const sellerId = this.get("session").get("uid");
return this.store.query('quota',{
orderBy:'buyerId',
equalTo: params.buyer_id
}).then(function(quota){
return quota.filterBy('sellerId',sellerId)
});
}
});
and in my template (simplify) is like this:
{{#each model as |quota|}}
{{quota.quota}}
{{/each}}
it worked but if someone add data or delete data in quota model, the list didn't update automatically in the template.
The template only refresh after i refresh the browser. The funny thing is if I use ember inspector to inspect the data for quota, it shown that the model already changes if someone changes the model but the template didn't reflect the changes.
please help
thanks
The issue lies, how are you doing transitionTo to assignQuota route, If you are passing model to the dynamic segment,then it will skip calling the model hook and it will render same model data.
The reason is that the model does not observe changes.
Create a computed property and make it observer change changes of the model, and then using the computed value to create a list (your each loop).
quotaList: Ember.computed('model.[]', function() {
// Your update logic here
// return the new value
})

How can I request the relationships of a resource without using a filter?

I have a backend that follows the JSON API specification.
In my Ember app, I do something like this:
model() {
return this.store.query('category', { filter: { forum: 'main' } });
}
This works well and the request sent to the server is GET /categories?filter[forum]=main. My app gets all the categories from the forum with ID main.
Now, instead of the previous request, I would like to make a GET /forums/main/categories from the model. How can this be done in Ember with Ember Data?
Here's something I tried with Ember AJAX:
ajax: Ember.inject.service(),
model() {
return Ember.RSVP.hash({
categories: this.get('ajax').request('/forums/main/categories'),
});
}
The request works and the correct data is returned from the server. But Ember Data just doesn't know about it and I can't use the model in my template. How can I make Ember AJAX work with Ember Data?
The Ember AJAX GitHub page proposes to write something like that:
import DS from 'ember-data';
import AjaxServiceSupport from 'ember-ajax/mixins/ajax-support';
export default DS.JSONAPIAdapter.extend(AjaxServiceSupport);
https://github.com/ember-cli/ember-ajax#usage-with-ember-data
But it doesn't seem to change anything.
Okay, /forums/main/categories looks a bit like a relationship link. Like forum is a model as well, and has a relationship categories. Is this right?
If yes, probably the best thing is to first fetch the forum and then the relationship. Maybe you already have the forum record? So something like this:
store.findRecord('forum', 'main').then(forum => forum.get('categories'));
However if you want to filter the categories based on a forum string, this is also possible. So basically you want to do this:
this.store.query('category', { filter: { forum: 'main' } });
But it should request /forums/main/categories instead of /categories?filter[forum]=main. This can be done with a custom adapter. Probably you just have to override urlForQuery:
urlForQuery(query, modelName) {
if(query.filter.forum)
const forum = query.filter.forum;
delete query.filter.forum;
return `/forums/${forum}/categories`
} else {
return this._super(...arguments);
}
},

Loading/reloading data from an action function without changing the route

I am just starting with ember and trying to do a simple test.
Which, also very simple, got me stuck for some reason and I cant find the answer anywhere.
So I need load data from the server without transition to another route and do it from within a submit action (or any other action for that matter).
I have a simple input form where I type in manually an object ID and
I want it to be loaded say right underneath. Simple enough. Seams to be a three minutes job in angular. Here, I just cant get the hang of communication between route and controller.
So given this little emblem
form submit="submit"
= input type="text" value=oid
button type="submit" Submit
#display
= person
And this route
import Ember from 'ember';
export default Ember.Route.extend({
model: {
person: null
},
actions: {
submit: function() {
var oid = this.controllerFor('application').get('oid');
var person = this.store.find('person', oid);
this.modelFor('application').set('person', person);
}
}
});
This is as far as I could think. I want to click submit with ID of an object and I want that object loaded and displayed in the div#display.
So what am I doing wrong? What is the right way to do it?
First, I don't even know where to put such an action? Controller or route?
If I put it in controller, I don't know how to refresh the model. If I put it in route, I am stuck with the above. Would be also nice to see how to do it if action was placed in the controller.
For simplicity I just do it all in application route, template, controller ...
Thank you
The best place to put your code is on Controller given it responds to UI, so doing that on your controller the code is much more simple.
On this jsfiddle I have put some dummy code which tries to do something what you want to achieve.
//Index Route
App.IndexRoute = Ember.Route.extend({
model: function () {
return ['red', 'yellow', 'blue'];
}
});
//Here my dummy controller.
App.IndexController = Ember.Controller.extend({
oid: 1,
actions: {
submitAction() {
//Here your logic to find record given the input and attach
//the response to the model object as UI is binding to model
//if you add/remove new records they will show up.
//On this example I have added a new object.
this.get('model').addObject('green');
}
}
})
Enjoy!

Proper way to set multiple models on route; depending on user authentication?

I'm currently working on an Ember app and it is coming along fine but since I am new to MVC applications in general there are a lot of concepts that don't come naturally to me.
I am currently trying to return two models for my index route. I referred to another SO question (EmberJS: How to load multiple models on the same route?) for the correct method and it has worked great.
My problem is now that I need to only set one of the two models only if the user is authenticated. I am using ember-simple-auth, and currently this is what I've got:
// app/routes/index.js
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
if (this.get('session.isAuthenticated')) {
var _this = this;
this.get('store').find('user', this.get('session.uid')).then(function(user) {
_this.set('model.entries', user.get('entries'));
});
}
return Ember.RSVP.hash({
newEntry: this.get('store').createRecord('entry', {
body: 'Write here ...'
})
});
}
});
For some reason, this does not work. After my route is loaded, the model only has the 'newEntry' property and not an 'entries' property, although the promise does get fulfilled (I put console.logs inside to prove it).
What could be happening? And is this the best way to accomplish this?
There is a set of data that you always want to load, for every user. Do that in the model hook, that is actually the data for the route.
There is another piece of info that you want to add only if a condition is met (authentication). Do that in the afterModel hook.
...is provided the route's resolved model...
http://emberjs.com/api/classes/Ember.Route.html#method_afterModel
So, now you can append or remove data from the model. Or take any relevant action depending on the data that you received.