Ember createRecord not creating an id - ember.js

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)
);

Related

Ember computed.alias in nested views

I'm trying to create a reusable generated element that can react to changing outside data. I'm doing this in an included view and using computed.alias, but this may be the wrong approach, because I can't seem to access the generic controller object at all.
http://emberjs.jsbin.com/nibuwevu/1/edit
App = Ember.Application.create();
App.AwesomeChartController = Ember.Object.extend({
data: [],
init: function() {
this.setData();
},
setData: function() {
var self = this;
// Get data from the server
self.set('data', [
{
id: 1,
complete: 50,
totoal: 100
},
{
id: 2,
complete: 70,
total: 200
}
]);
}
});
App.IndexController = Ember.Controller.extend({
needs: ['awesome_chart']
});
App.ChartView = Ember.View.extend({
tagName: 'svg',
attributeBindings: 'width height'.w(),
content: Ember.computed.alias('awesome_chart.data'),
render: function() {
var width = this.get('width'),
height = this.get('height');
var svg = d3.select('#'+this.get('elementId'));
svg.append('text')
.text('Got content, and it is ' + typeof(content))
.attr('width', width)
.attr('height', height)
.attr('x', 20)
.attr('y', 20);
}.on('didInsertElement')
});
And the HTML
<script type="text/x-handlebars">
<h2> Welcome to Ember.js</h2>
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="index">
<h2>Awesome chart</h2>
{{view App.ChartView width=400 height=100}}
</script>
For what it's worth, this didn't seem to work as a component, either. Is the ApplicationController the only place for code that will be used on multiple pages? The 'needs' seems to work, but the nested view can't access it. If I make a proper Ember.Controller instance to decorate the view, that doesn't seem to work either.
Any help is much appreciated.
Update:
I can't edit my comment below, but I found a good answer on how to use related, and unrelated, models in a single route.
How to use multiple models with a single route in EmberJS / Ember Data?
Firstly, your controllers should extend ObjectController/ArrayController/Controller
App.AwesomeChartController = Ember.Controller.extend({...});
Secondly when you create a view the view takes the controller of the parent, unless explicitly defined.
{{view App.ChartView width=400 height=100 controller=controllers.awesomeChart}}
Thirdly you already had set up the needs (needed a minor tweak), but just as a reminder for those reading this, in order to access a different controller from a controller you need to specify the controller name in the needs property of that controller.
App.IndexController = Ember.Controller.extend({
needs: ['awesomeChart']
});
Fourthly from inside the view your computed alias changes to controller.data. Inside the view it no longer knows it as AwesomeChart, just as controller
content: Ember.computed.alias('controller.data')
Fifthly inside your on('init') method you need to actually get('content') before you attempt to display what it is. content doesn't live in the scope of that method.
var content = this.get('content');
http://emberjs.jsbin.com/nibuwevu/2/edit
First, AwesomeChart does sound like it's gonna be a reusable self-contained component. In which case you should better user Ember.Component instead of Ember.View (as a bonus, you get a nice helper: {{awesome-chart}}).
App.AwesomeChartComponent = Ember.Component.extend({ /* ... */ });
// instead of App.ChartView...
Second, for AwesomeChart to be truly reusable, it shouldn't be concerned with getting data or anything. Instead, it should assume that it gets its data explicitly.
To do this, you basically need to remove the "content:" line from the awesome chart component and then pass the data in the template:
{{awesome-chart content=controllers.awesomeChart.data}}
Already, it's more reusable than it was before. http://emberjs.jsbin.com/minucuqa/2/edit
But why stop there? Having a separate controller for pulling chart data is odd. This belongs to model:
App.ChartData = Ember.Object.extend();
App.ChartData.reopenClass({
fetch: function() {
return new Ember.RSVP.Promise(function(resolve) {
resolve([
{
id: 1,
complete: 50,
total: 100
},
{
id: 2,
complete: 70,
total: 200
}
]);
// or, in case of http request:
$.ajax({
url: 'someURL',
success: function(data) { resolve(data); }
});
});
}
});
And wiring up the model with the controller belongs to route:
App.IndexController = Ember.ObjectController.extend();
App.IndexRoute = Ember.Route.extend({
model: function() {
return App.ChartData.fetch();
}
});
Finally, render it this way:
{{awesome-chart content=model}}
http://emberjs.jsbin.com/minucuqa/3/edit

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');
});
}
}

incorrect display of attributes in child route

I'm using Ember 1.2, Handlebar 1.12, and EmberModel (not EmberData).
In my application, I have the common parent/child relationship in my route map, with a simple model similar to that shown below.
App.Router.map(function () {
this.resource('strats', {path: "/"}, function() {
this.route('strat', {path: "/strat/:strat_id"});
});
});
App.Item = Ember.Model.extend({
id : Ember.attr(),
itemName : Ember.attr()
});
App.Strat = Ember.Model.extend ({
id : Ember.attr(),
stratName : Ember.attr(),
stratDetail : Ember.attr(),
items : Ember.hasMany ('App.Item', {key: 'items', embedded: true})
});
As in many of the examples, I display strat.id on the left side (using the Handlebar #each helper), and details (i.e., strat.id, strat.stratName and strat.stratDetail) for the selected 'strat' on the right side. To add a new 'strat', I use a button connected to the action function "newStrat," which is shown below.
When adding a new child, everything displays correctly when there's already another child present (e.g., everything works fine when adding the 2nd and subsequent child). But if I'm adding the first child, strat.Id doesn't get displayed on the left side, but strat.id, strat.stratName and strat.stratDetail do get displayed on the right side. If I then call this.get('model').save() and hit the browser's refresh button, display is as expected on both left and right side (as a result of a request for data sent to the server, and the server replying with all saved data).
What's causing this behaviour? Is there anyway to fix it?
When there's no data, in reply to findAll(), my server returns {"strats":" "}. Does what I return for the no-data scenario have anything to do the problem?
Controller
App.StratsController = Ember.ArrayController.extend({
actions: {
newStrat: function() {
var nwStrat =
{
id: "newId",
stratName: "untitled",
stratDetail: "someDetail"
};
var newStrat = App.Strat.create (nwStrat);
this.get('model').pushObject(newStrat);
this.transitionToRoute ('strats.strat', newStrat);
}
}});
Code Snippet from template that displays left side
<div class=\"list-group\" style=\"margin-top: 10px\">
{{#each controller}}
{{#linkTo \"strats.strat\" this class=\"list-group-item\"}}
{{id}}
{{/linkTo}}
{{/each}}
</div>

can't save/update record using ember-data

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>

Ember.js Chosen integration

I've done a sample Ember.js integration with Chosen (https://github.com/harvesthq/chosen)
Coffeescript:
App.ChosenSelectView = Em.Select.extend({
didInsertElement: ->
#_super()
#$().chosen()
# Assumes optionLabelPath is something like "content.name"
#addObserver(#get("optionLabelPath").replace(/^content/, "content.#each"), -> #contentDidChange())
contentDidChange: ->
# 2 ticks until DOM update
Em.run.next(this, (-> Em.run.next(this, (-> #$().trigger("liszt:updated")))))
})
The thing that bothers me is I don't have a good idea about how much time do I need before triggering update on the Chosen widget. From my experiments 2 run loops is ok, but maybe there is a better way for the whole thing?
Full example at jsfiddle: http://jsfiddle.net/oruen/qfYPy/
I think the problem is that your observer is notified kind of too early, meaning that the changes have not yet been written to the DOM.
I've hacked a little around and in the end I came up with a solution, which calls Ember.run.sync() before the event for the chosen plugin is triggered, see http://jsfiddle.net/pangratz666/dbHJb/
Handlebars:
<script type="text/x-handlebars" data-template-name="selectTmpl" >
{{#each items tagName="select" }}
<option {{bindAttr value="id"}} >{{name}}</option>
{{/each}}
</script>​
JavaScript:
App = Ember.Application.create({});
App.items = Ember.ArrayProxy.create({
content: [Ember.Object.create({
id: 1,
name: 'First item'
}), Ember.Object.create({
id: 2,
name: 'Second Item'
})]
});
Ember.View.create({
templateName: 'selectTmpl',
itemsBinding: 'App.items',
didInsertElement: function() {
this.$('select').chosen();
},
itemsChanged: function() {
// flush the RunLoop so changes are written to DOM?
Ember.run.sync();
// trigger the 'liszt:updated'
Ember.run.next(this, function() {
this.$('select').trigger('liszt:updated');
});
}.observes('items.#each.name')
}).append();
Ember.run.later(function() {
// always use Ember.js methods to acces properties, so it should be
// `App.items.objectAt(0)` instead of `App.items.content[0]`
App.items.objectAt(0).set('name', '1st Item');
}, 1000);​
Michael Grosser posted his working ChosenSelectView here: http://grosser.it/2012/05/05/a-chosen-js-select-filled-via-ember-js
This might work for you on Ember 1.0 and Chosen v0.12:
JavaScript:
App.ChosenSelect = Ember.Select.extend({
chosenOptions: {width:'100%', placeholder_text_multiple: 'Select Editors', search_contains: true},
multiple:true,
attributeBindings:['multiple'],
didInsertElement: function(){
var view = this;
this._super();
view.$().chosen(view.get('chosenOptions'));
// observes for new changes on options to trigger an update on Chosen
return this.addObserver(this.get("optionLabelPath").replace(/^content/, "content.#each"), function() {
return this.rerenderChosen();
});
},
_closeChosen: function(){
// trigger escape to close chosen
this.$().next('.chzn-container-active').find('input').trigger({type:'keyup', which:27});
},
rerenderChosen: function() {
this.$().trigger('chosen:updated');
}
});
Handlebars:
{{view App.ChosenSelect
contentBinding="options"
valueBinding="selectedOption"
optionLabelPath="content.name"
}}