can't save/update record using ember-data - ember.js

My Router defines the following:
this.resource('uoms', { path: '/uoms' }, function() {
this.route('new');
});
And the uoms route is defined as:
App.UomsRoute = Ember.Route.extend({
model: function() {
return this.store.find('uom');
},
actions: {
save: function() {
this.modelFor('uoms').save().then(function() {
console.log("saved UOMs");
});
}
}
});
But for some reason when I try and save it I am getting:
Uncaught TypeError: Object [object Object] has no method 'save'
Can someone help me identify what I'm doing wrong?
---- UPDATE ----
I also tried the following from the controller but with the same results:
App.UomsController = Ember.ArrayController.extend({
actions: {
save: function() {
this.get('model').save().then(function() {
console.log("saved UOMs");
});
}
}
});
---- UPDATE 2 ----
Looking at the object returned by the this.get('model') call we get the following:

This is what I would do:
Using Chrome Developer Tools set a breakpoint at the line where you try to save the model
To inspect the model held by the controller enter this.get('model') the Chrome Developer Tools Console.
Check if the console output is really what you expect. Most probably it is not the model instance you want to save. You will see that you will get the same error when you execute this.get('model').save() in the console.
BTW: Why are you using an ArrayController and not an ObjectController. It looks like you use it for a single model. See the docs for more details on that.
Edit:
Try this.get('model').get('transaction').commit()

The problem I was running into was that I was trying to call save() on an array of records rather than a singular record. This problem was created because I was operating on a singular record but doing this within an ArrayController. I don't think there's anything wrong with this although it could be argued that I should have created a sub-route called "edit" and then presumably my code logic would have worked.
That said, if you want to do as I did (aka, save a singular record within an ArrayController) then this code will work:
save: function(id) {
var promise = this.store.find('uom',id).then(function(uom){
console.log(uom);
uom.save();
});
}
And then in the template put something like this:
<span class="glyphicon glyphicon-floppy-disk" {{action "save" id}}></span>
This works but is not the best answer. Instead you can specify in the ArrayController an ObjectController with the itemController property. Below is an example of both a save() and deleteRecord() handler using this strategy:
App.PluralController = Ember.ArrayController.extend({
itemController: 'singular'
});
App.SingularController = Ember.ObjectController.extend({
actions: {
save: function() {
this.get('model').save();
},
deleteRecord: function() {
this.get('model').deleteRecord();
this.get('model').save();
}
}
});
Then you'll want to do something like the following for your handlebars template:
<span class="glyphicon glyphicon-floppy-disk" {{action "save" this}}></span>
<span class="glyphicon glyphicon-remove-circle" {{action "deleteRecord" this}}></span>

Related

Ember createRecord not creating an id

I'm trying to create a "note" record with createRecord. When I pass it into my action, it properly creates the record, but only creates the "body" attribute I pass in, and not the id or timestamps. It does, however, create these attributes after I refresh. My problem is that I want it to create these as soon as I click my "create" button, so I can sort in descending order, and be able to delete the note without having to refresh each time.
My controller:
import Ember from "ember";
export default Ember.ArrayController.extend({
actions: {
newNote: function() {
var body = this.get('noteCopy');
var note = this.store.createRecord('note', { body: body });
this.set('noteCopy', '');
note.save();
},
deleteNote: function(id) {
this.store.find('note', id).then(function(note) {
note.deleteRecord();
note.save();
});
}
}
});
My template:
{{textarea placeholder="Add a note!" value=noteCopy class='newNoteArea'
autofocus=true}}<br>
<button class='createNoteButton'{{action 'newNote'}} style='font-size:2em'>Save Note</button><br><br>
<br>
{{#each note in model}}
<div class="noteShow">
{{note.body}}<br>
<img src="assets/erase.gif" alt="" class='deleteNoteButton'{{action 'deleteNote' note.id}} style='width:4em'/>
</div>
{{/each}}
{{outlet}}
My server does the sorting properly once the note creates the timestamps attributes... But since I get
id: null, body: "some body", created_at: undefined, updated_at: undefined
every time I create a new note, it doesn't do anything it's supposed to, until I refresh. It occurred to me that this may be a problem with promises, but after trying to implement some .success() and .then() lines, I have a feeling this isn't the case.
Forgive me if this is a newbie question, but I'm still quite new to Ember. Any input is appreciated. Thanks!
The id is given to you by the API server you POST to. You can retrieve the id after the creation was successful.
var newJob = jobController.store.createRecord('job', {
status: 'requested',
message: '',
});
console.log(newJob);
newJob.save().then(() => {
console.log('Job ID ', newJob.id, ' created.');
}.bind(jobController), (err) => {
console.log(err.message);
}.bind(jobController)
);

Ember way to implement a search dialog

I want to implement simple ember app, where I have a search dialog, a list of results and a detailed view if I click on the results, something like this:
http://jsbin.com/tuyapabuhe/2/edit
The search method of the IndexController is doing an ajax request to populate the model, but I'm not sure if that is the best way to do it. I specially don't like the var self = this; part. Is there an ember way to do that search?
EDIT
I updated the example, now is doing an ajax request and is more realistic:
http://jsbin.com/wimogu/4/edit
The ajax call should be happening inside the model hook for the Index route. Instead of observes you can just use a property as follows:
App.IndexRoute = Ember.Route.extend({
model: function(){
return data; // your ajax call here...
}
});
App.IndexController = Ember.ArrayController.extend({
filtered: function() {
var name = this.get('name') || '';
var people = data.filter(function(el){
if(el.name.toLowerCase().indexOf(name)>-1)
return el;
});
return people;
}.property('name', 'model')
});
Then, in your template you can just do
{{#each user in filtered}}
{{#link-to 'person' user.id}}
<div>{{user.name}}</div>
{{/link-to}}
<hr/>
{{/each}}
Working solution here
Per my comment on another answer, I would suggest the following for AJAX calls based on one or more filters, complete with debouncing to limit the number of requests:
function handleSearch() {
this.set('model', this.store.find('user', this.get('query')));
}
App.IndexController = Ember.Controller.extend({
search: '',
sort: 'first_name',
direction: 'asc',
query: function() {
return {
search: this.get('search'),
sort: this.get('sort'),
direction: this.get('direction')
};
}.property('search'),
queryDidChange: function() {
Ember.run.debounce(this, handleSearch, 200);
}.observes('query').on('init'),
actions: {
clearSearch: function() {
this.set('search', '');
}
}
});
I have this running in the wild right now and it works perfectly.

Ember.js - How to clear the form data after saving the record?

I'm using Ember App Kit. I have a form that takes a student name and I can save the data into a database without any problem. The problem is the data (student name) retains on the form whenever I get transition back to this route (http://localhost:8000/#/students/new) from another page. If I refresh the screen, then the data will be cleared out and I will get a fresh form. What am I doing wrong?
Also, if I decide not to save or add the record and go to see the list of students, I see an empty record on screen. That record is not in the database though. How can I prevent that?
//--------------------------------------------
// Controller
var StudentsNewController = Ember.ObjectController.extend({
init: function() {
var newSystem = this.store.createRecord('student');
this.set('newStudent', newStudent);
this._super();
},
actions: {
acceptChanges: function () {
var self = this;
self.get('newStudent').save().then(function(student){
self.transitionToRoute('students');
});
}
}
});
export default StudentsNewController;
//--------------------------------------------
// Model
var Student = DS.Model.extend({
name: DS.attr('string'),
major: DS.belongsTo('major')
});
export default Student;
//--------------------------------------------
// Template
<form {{action 'updateSystem' on="submit"}}>
<fieldset>
<div>
<label>Student Name</label>
{{input value=newStudent.name size="50"}}
</div>
<button {{action 'acceptChanges'}}>Add</button>
</fieldset>
</form>
Try to setup the controller in you route (here) instead of using the init method in the controller. It's one of the routes responsibilities.
I think the problem is that you assume that every time you transition to the StudentsNewRoute a new StudentsNewController is created, and thus the init method is called.
The truth is Ember creates the controller once and changes it's content based on the model and the setupController hooks. So the init method of the controller it's called once and you end up with the same record every time you transition to the route.
To solve this you'd do something like this:
var StudentsNewController = Ember.ObjectController.extend({
newStudent: null,
actions: {
acceptChanges: function () {
this.get('newStudent').save().then((student) => {
this.transitionToRoute('students');
});
}
}
});
//--------------------------------------------
var StudentsNewRoute = Ember.Route.extend({
model: function() {
return this.store.createRecord('student');
},
setupController: function(controller, model) {
controller.set('newStudent', model);
},
});
I hope this helps you!
actions: {
acceptChanges: function () {
var self = this;
self.get('newStudent').save().then(function(student){
self.set('newStudent','');
self.transitionToRoute('students');
});
}
}

Route only loads on page refresh - Ember JS

I am currently learning Ember and I am making a simple app, but I have encountered a strange problem. I have a route setup and it only pulls the data when I reload the page. Here is my code:
// Start Ember application
App = Ember.Application.create({
LOG_TRANSITIONS: true
});
// Router paths
App.Router.map(function () {
// Homepage
this.resource('index', { path: '/' });
// Book view
this.resource('book', { path: '/book/:id/:version' });
});
// Homepage route
App.IndexRoute = Ember.Route.extend({
model: function () {
// Get all the books
return Ember.$.getJSON('/books');
}
});
// Single book view
App.BookRoute = Ember.Route.extend({
model: function (params) {
// Get a single book
return Ember.$.getJSON('/book?id=' + params.id + '&version=' + params.version);
}
});
When I go to /#/book/1/1 by clicking from the homepage, the page is blank. When I just refresh the page when I'm on there it loads the data and everything works.
Why is this? What can I do to make it work when the user clicks from the homepage?
Thank you everyone for your suggestions. I figured it out with this code here:
App.BookRoute = Ember.Route.extend({
setupController: function (controller,model) {
// Get a single book
Ember.$.getJSON('/book?id=' + model.id + '&version=' + model.version,
function (data) {
controller.set('model',data);
});
}
});
I used setupController instead of model.
you should pass a model in link-to. paste your template code to check how you are creating links. check this for more info http://emberjs.com/guides/templates/links/
If you use link-to helper you must not set context model in this one... you simply need to set an id,
{{#each book in books}}
{{#link-to "book" book.id}}{{book.name}}{{/link-to}}
{{/each}}
and the model of the next route will be request.
The Ember link-to helper doesn't execute the model callback. I've dealt with a situation very much like this recently, and here's how I solved it:
// In the index template
{{#each book in model}}
{{#link-to 'book' book}}{{book.name}}{{/link-to}}
{{/each}}
When you click on the link, you'll get a blank page like you are now, because the BookRoute model callback isn't fired. However, the BookController#init function will be fired. You can use this to go and grab the rest of the details about the book there.
App.BookController = Ember.Controller.extend({
init: function() {
this._super();
// Replace doesNotHaveAllInfo with your check
if (doesNotHaveAllInfo) {
this.set('model', Ember.$.getJSON('/book?id=' + this.get('model.id') + '&version=' + this.get('model.version')));
}
},
});
This way, if the book is loaded through link-to, the new details will be fetched. Note that this will require that the books loaded on the index route contain at least id and version.
Alternatively, if you refresh the page, the model callback will occur, and your doesNotHaveAllInfo check will return false. Whether you click on a link-to or refresh the page, you should see your data.
I'd also recommend abstracting the Ember.$.getJSON code into a reusable method, perhaps on the model, like this:
App.Book.reopenClass({
find: function(id, version) {
return Ember.$.getJSON('/book?id=' + id + '&version=' + version);
}
});
You can then use App.Book.find() to return your model in both your route and your BookController.

ember - didLoad event in view for waiting for a ember-data model to load

hi i have the following route:
MB3.PlaylistRoute = Ember.Route.extend({
model: function(params) {
return MB3.Playlist.find(params.playlist_id);
}
});
The playlist has a hasMany realtion with tracks. in the playlist view i want do do some logic with an attribute of the first track of the playlist.
so i added this code:
MB3.PlaylistView = Ember.View.extend({
didInsertElement: function() {
console.log(this.get("controller.tracks").objectAt(0).get("title"));
}
});
The problem is title is undefined (i think because it is not yet loaded. the second thing i tried is waiting for the didLoad event:
MB3.PlaylistView = Ember.View.extend({
didInsertElement: function() {
var self=this;
this.get("controller.tracks").on("didLoad", function() {
console.log(self.get("controller.tracks").objectAt(0).get("title"));
});
}
});
but this logges null as well. How do i accomplish that?
Like Adrien said in the comments, it seems you are running into issue 587. That said, I don't think you actually need the "didLoad" callback in this case. Instead, try using a computed property to get the video_id or track title. For example:
MB3.PlaylistView = Ember.View.extend({
firstTrackTitle: function() {
return this.get('controller.tracks.firstObject.title');
}.property('controller.tracks.firstObject.title')
});
Then in your template, embed the player if this property is defined:
{{#if view.firstTrackTitle}}
embed code here
{{/if}}
FWIW I would put this logic in controller instead of view, but same idea.