Reload from save() response with Ember-Data - ember.js

I'm currently using EmberJs along with Ember-Data to build an app backed by a Laravel JSON api.
I got a little issue on the saving process, mostly on model creation.
Here is my workflow :
The Ember ObjectController saves itself this.get("model").save()
Laravel (REST api) receives the data and persist it, therefore
creating a unique ID for it
The api return the new data (that
respect Ember convention) with the proper ID
???? Ember-Data doesn't
seems to care about the response since it does nothing...
The problem here : the id remains undefined even if it has been given...
The workaround I found is to reload models... but it's a big performance flaw considering that the data I want to be reloaded it available to Ember straight after the save()
any ideas ?
EDIT **
The problem only occurs on the first object that I add. When I repeat the process the next objects are refreshed correctly. If I hit refresh, it start again : the first object miss his refresh and the following are okay.
Here my code about the add process :
route
App.CategoriesNewRoute = Ember.Route.extend({
model: function()
{
return this.store.createRecord("category").set("active", true);
},
setupController: function(ctrl, model)
{
ctrl.set("errors", 0);
ctrl.set("model", model);
}
});
I don't have any Router for CategoriesRoute since the data is all in my ArrayController from the start.
controller
App.CategoriesNewController = Ember.ObjectController.extend({
needs: "application",
actions:
{
save: function()
{
this.get("model").save();
this.get("target").transitionTo("categories");
},
cancel: function()
{
this.get("model").rollback();
this.get("target").transitionTo("categories");
}
}
});
EDIT ** 2
I tried the code provided below with no success...
I added 2 records, and the first don't have it's ID... the second got it, so the problem appears to only be on the first save...
Here are the 2 responses I got from my API
ObjectA
{"category":{"nameFr":"aaa","nameEn":"aaa","active":true,"id":10}}
ObjectB
{"category":{"nameFr":"bbb","nameEn":"bbb","active":true,"id":11}}

It could be because you're transitioning before the save finishes, and so the model hook on the categories route fires while the model you're saving is still in flight (are you getting any errors in the console?). Try changing the save action to
save: function()
{
var that = this;
this.get("model").save().then(function(){
that.get("target").transitionTo("categories");
});
},
Also, you don't need to this.get('target')... as there's a controller method transitionToRoute. You can simplify to:
save: function()
{
var that = this;
this.get("model").save().then(function(){
that.transitionToRoute("categories");
});
},

Found that the problem seems to be on Ember-Data's side...
documented the whole thing here :
http://discuss.emberjs.com/t/missing-id-on-first-save-on-a-new-object/4752

Related

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!

Saving data captured from form rendered in a modal component

Ember - v1.7.0
Ember Data - v1.0.0-beta.10
I created a modal component using zurb foundation 5 CSS framework reveal features, though all works well, am unable to save data captured from the form in controller save action.
Controller which handles on save button execution
App.PersonModalController = Ember.ObjectController.extend({
actions: {
close: function() {
return this.send( 'closeModal' );
},
save:function() {
this.get('model').save();
}
}
});
The issue am facing is that the this.get('model').save() is not working and data is not been posted to restful backend.
Am not sure exactly how to go about storing the data captured from the form, when I console.log( this.get('model') ); it appears to be a proper model object with all the bells and whistles.
I tried obtaining the store to add model to it but that doesn't work too.
A. Addendum
After searching around I came across a number of Stack Overflow questions relating to this.get('model').save() it appears it doesn't quite work as expect, perhaps based on context.
difference-between-model-save-versus-model-getstore-commit
ember-js-how-to-save-a-model
save-record-of-model-is-not-working-in-ember-data-1-0-0-beta-3
When I change code to the following:
App.PersonModalController = Ember.ObjectController.extend({
actions: {
close: function() {
return this.send( 'closeModal' );
},
save:function() {
var person = this.store.createRecord('person',{firstName:firstName,lastName:lastName});
person.save();
}
}
});
It POSTs data correctly to back-end and saves, I however believe there must be a better way, cause if you have a form with say 50 fields, you won't want to manually set each attribute.
After careful inspection, though posting occurs, the data posted is empty.
I would try to continue your save method as in Bart's answer to the ember-js-how-to-save-a-model question.
person.save().then(function() {
// SUCCESS
}, function() {
// FAILURE
});
and in those methods I would console.log() the results.
I would imagine it has something to do with the promise aspect of the save functionality.

Reload invalid ember-data model

This is very similar to this question Force reload of dirty/invalid model in Ember
I'm using ember.js with ember-data.
DEBUG: ------------------------------- ember.js?compile=false:3521
DEBUG: Ember : 1.5.0 ember.js?compile=false:3521
DEBUG: Ember Data : 1.0.0-beta.7+canary.e3b896bc ember.js?compile=false:3521
DEBUG: Handlebars : 1.3.0 ember.js?compile=false:3521
DEBUG: jQuery : 1.11.0 ember.js?compile=false:3521
DEBUG: -------------------------------
The server returns a 422 with errors response for validation errors, which gets added to model's errors and marked as invalid, then the error shows up on my template. This all works fine. However,
after the model is marked invalid following an attempted save(), if I then linkTo another route let's say to /show/id to view that same model. The data store retrieves the invalid model with invalid values instead of getting a fresh valid model. I have tried doing a rollback() onFail like: group.save().then(onSuccess, onFail); and the rollback works, however it also clears the model errors and refreshes the template so the user never sees the validation errors. What I want is to show the validation errors and if a linkTo another route happens; From there the model with invalid state, should no longer be pulled from the data store, but rather pulled from the server again. The only way I can get a valid model currently is to reload the entire page.
I have also tried forcing a reload using the model hook in the router, but this seems to cause errors:
Ricauth.GroupShowRoute = Ember.Route.extend({
model: function(params) {
var group = this.store.find('group', params.id);
group.reload(); // doesn't work, causes error
return group;
},
setupController: function(controller, group) {
controller.set('model', group);
controller.set('readOnly', true);
controller.set('meta', Ember.copy(this.store.metadataFor("group")))
}
});
This is not really a good way to do it anyway, since I'm essentially reloading the model every time ShowRoute is requested. I also tried to check group.isValid, however it's undefined at that point. Any ideas on how to get this reloaded and only when the model is invalid?
I found a reasonable solution to this using onFail and unloadRecord(). unloadRecord will remove the record from the datastore so the store will then retrieve from the server next time this record is queried. My update action
actions: {
update: function (group) {
var self = this;
var onSuccess = function(group) {
console.info("save: "+group);
self.transitionTo('group.show', group);
};
var onFail = function(error) {
console.info("failed: "+error.message);
group.unloadRecord();
};
if(group.get('currentState').stateName == 'root.loaded.updated.uncommitted') {
group.save().then(onSuccess, onFail);
}
else {
onSuccess(group);
}
}
}
So while the unload does remove the record from the datastore, it goes into a state 'root.deleted.saved'. From this state I can't seem to then save the record because of the way the ember data state manager works. At this point I'm just having a difficult time understanding why I can't make something so conceptually simple work. Does anyone else have enough experience with ember-data to know how this should be handled?
Use the deactivate method on Ember.Route (http://emberjs.com/api/classes/Ember.Route.html#method_deactivate). There you can check to see if the model is invalid, then rollback the record.
deactivate: function(){
if(!this.model().get("isValid")){
this.model().rollback();
}
}

Computed property for the number of records in the store?

This may be abusing Ember, but I want to create a computed property for the number of items in the store.
I'm trying to prototype a UI that exists entirely client-side. I'm using fixture data with the local storage adapter. That way, I can start off with canned data, but I can also add data and have it persist across reloads.
As I'm currently working on the data layer, I've built a settings route that gives me a UI to reset various models. I would like to add a Handlebars expression like {{modelCount}} so I can see how many records there are in the store. That's quicker than using the Ember Data Chrome extension, which resets to the routes tab on every page reload.
The following will show me the number of records once, but does not change when the number of records changes:
modelCount: function() {
var self = this;
this.store.find("my_model").then(function(records) {
self.set("modelCount", records.get("length"));
});
}.property()
I get that the store is supposed to proxy an API in the real world, and find returns a promise, but that's about the limit of my knowledge. I don't know how tell Ember to that I want to know how many records there are, or if this is even a valid question.
Load the result of store.find into an Ember.ArrayController's content and then bind the length of content to modelCount. An example:
App.SomeRoute = Ember.Route.extend({
model: function(){
return this.store.find('my_model');
}
});
App.SomeController = Ember.ArrayController.extend({
modelCount: Ember.computed.alias('content.length')
});
See a working example in http://jsbin.com/iCuzIJE/1/edit.
I found a workable solution by combining the answer from #panagiotis, and a similar question, How to load multiple models sequentially in Ember JS route.
In my router, I sequentially load my models:
model: function() {
var self = this;
return new Ember.RSVP.Promise(function(resolve, reject) {
self.store.find("model1").then(function(model1) {
self.store.find("model2").then(function(model2) {
self.store.find("model3").then(function(model3) {
resolve({
model1: model1,
model2: model2,
model3: model3
});
});
});
});
});
},
Then in my controller, I define simple computed properties:
model1Count: function() {
return this.get("model1.length");
}.property("model1.length"),
...

DS.defaultStore.load(App.foo, payload) only works first time with DS.RESTAdapter

UPDATE:
THIS IS A NON-ISSUE
(see below)
So I wrote a jsfiddle to show the bad behavior except the fiddle works! and my real code doesn't. The only difference is I am using the RESTAdapter in my real code so the data is pulled from server instead of FIXTURES.
In the jsfiddle: first click 'Simulate 1st manual load', then click the 2nd button to see it work properly (i.e. loading new or updated data to the store multiple times in a row)
http://jsfiddle.net/iceking1624/NZZ42/4/
The Issue
I am sending updated information over websockets to my Ember App. I successfully set up a listener to trigger a function on the correct controller and am able to update records the first time. But all successive attempts do not update the store and I wonder if this has to do with the state of the store? But I am unsure of how to handle if that is the case.
This is the code that updates or adds the records that come over websockets:
App.SessionController = Ember.ObjectController.extend({
updateReceived: function(data) {
console.log(data);
DS.defaultStore.load(App.Choice, data.choice);
DS.defaultStore.load(App.Question, data.question);
}
});
Notice the console.log(data) part. Every single time I send updated data via websockets, updateReceived is called and the correct data is logged every time, but DS.defaultStore.load(...) only works the first time.
The reason I update both App.Question & App.Choice is because they have a relationship:
App.Question = DS.Model.extend({
"choices" : DS.hasMany('App.Choice')
});
App.Choice = DS.Model.extend({
"question" : DS.belongsTo('App.Question')
});
I don't think the code below is relevant to the issue but just in case someone is interested, this is how I listen for events over websockets (using socket.io):
App.SessionRoute = Ember.Route.extend({
enter: function() {
this.socket = io.connect('http://10.0.1.4')
},
setupController: function(controller, model) {
var self = this;
this.socket.on('update', function(data) {
self.controller.send('updateReceived', data)
})
}
});
Are there any suggestions for how I can continuously load new or updated records directly into the store (and not just once)?
UPDATE:
The code is indeed correct. The new data was loading into the store just fine but I wasn't re-rending a view correctly when new/updated information was loaded into DS.defaultStore
I don't want to delete this question since others may find the information in it useful but vote how you like. I'm sorry I didn't catch this before writing the question.