Delay ember view render till $getJSON isLoaded - ember.js

The problem with this code is that the render code is entered twice, and the buffer is not where I expect it. Even when I get the buffer, the stuff I push in is not rendered to the screen.
App.FilterView = Ember.View.extend({
init: function() {
var filter = this.get('filter');
this.set('content', App.ViewFilter.find(filter));
this._super();
},
render: function(buffer) {
var content = this.get('content');
if(!this.get('content.isLoaded')) { return; }
var keys = Object.keys(content.data);
keys.forEach(function(item) {
this.renderItem(buffer,content.data[item], item);
}, this);
}.observes('content.isLoaded'),
renderItem: function(buffer, item, key) {
buffer.push('<label for="' + key + '"> ' + item + '</label>');
}
});
And the App.ViewFilter.find()
App.ViewFilter = Ember.Object.extend();
App.ViewFilter.reopenClass({
find: function(o) {
var result = Ember.Object.create({
isLoaded: false,
data: ''
});
$.getJSON("http://localhost:3000/filter/" + o, function(response) {
result.set('data', response);
result.set('isLoaded', true);
});
return result;
}
});
I am getting the data I expect and once isLoaded triggers, everything runs, I am just not getting the HTML in my browser.

As it turns out the answer was close to what I had with using jquery then() on the $getJSON call. If you are new to promises, the documentation is not entirely straight forward. Here is what you need to know. You have to create an object outside the promise - that you will return immediately at the end and inside the promise you will have a function that updates that object once the data is returned. Like this:
App.Filter = Ember.Object.extend();
App.Filter.reopenClass({
find: function(o) {
var result = Ember.Object.create({
isLoaded: false,
data: Ember.Object.create()
});
$.getJSON("http://localhost:3000/filter/" + o).then(function(response) {
var controls = Em.A();
var keys = Ember.keys(response);
keys.forEach(function(key) {
controls.pushObject(App.FilterControl.create({
id: key,
label: response[key].label,
op: response[key].op,
content: response[key].content
})
);
});
result.set('data', controls);
result.set('isLoaded', true);
});
return result;
}
});
Whatever the function inside then(), is the callback routine that will be called once the data is returned. It needs to reference the object you created outside the $getJSON call and returned immediately. Then this works inside the view:
didInsertElement: function() {
if (this.get('content.isLoaded')) {
var model = this.get('content.data');
this.createFormView(model);
}
}.observes('content.isLoaded'),
createFormView: function(data) {
var self = this;
var filterController = App.FilterController.create({ model: data});
var filterView = Ember.View.create({
elementId: 'row-filter',
controller: filterController,
templateName: 'filter-form'
});
self.pushObject(filterView);
},
You can see a full app (and bit more complete/complicated) example here

Related

Emberjs - how to reset a field on a component after saving?

I have an embedded object called Comment, inside a Game. Each game can have many Comments.
When a user (called a Parent) views the game page, they can leave a comment.
The problem I have is that I cannot seem to reset the body of the comment field back to empty after the comment is saved.
This is the component:
MyApp.AddCommentComponent = Ember.Component.extend({
body: '',
actions: {
addComment: function() {
var comment, failure, success;
if (!!this.get('body').trim()) {
comment = this.store.createRecord('comment', {
body: this.get('body'),
game: this.get('game'),
parent_id: this.get('parent_id'),
parent_name: this.get('parent_name')
});
success = (function() {
this.set('body', '');
});
failure = (function() {
return console.log("Failed to save comment");
});
return comment.save().then(success, failure);
}
}
}
});
The error is on the 'this.set' line - this.set is not a function
All the examples I find are about doing this in a controller or by creating a new record upon route change (but the route is not changing in my example, since it is just adding another embedded object to the existing page).
You are using
this.set('body', '');
in success, but the scope of this here is changed, you need to keep the controller scope and set the body to empty string like
MyApp.AddCommentComponent = Ember.Component.extend({
body: '',
actions: {
addComment: function() {
var that = this;
var comment, failure, success;
if (!!this.get('body').trim()) {
comment = this.store.createRecord('comment', {
body: this.get('body'),
game: this.get('game'),
parent_id: this.get('parent_id'),
parent_name: this.get('parent_name')
});
success = (function() {
that.set('body', '');
});
failure = (function() {
return console.log("Failed to save comment");
});
return comment.save().then(success, failure);
}
}
}
});
When you introduce a function, you must remember that the value for this is not (necessarily) the same as the enclosing scope's value for this. Save the reference to the Component to use in a closure, like this:
MyApp.AddCommentComponent = Ember.Component.extend({
body: '',
actions: {
addComment: function() {
var comment, failure, success;
var self= this;
if (!!this.get('body').trim()) {
comment = this.store.createRecord('comment', {
body: this.get('body'),
game: this.get('game'),
parent_id: this.get('parent_id'),
parent_name: this.get('parent_name')
});
success = (function() {
self.set('body', '');
});
failure = (function() {
return console.log("Failed to save comment");
});
return comment.save().then(success, failure);
}
}
}
});

Emberjs - setProperties from a Promise

I'm trying to set a variable in my router from the inside of a promise, but it seems that it is not set properly.
I have create a small example to show you :
App.ApplicationRoute = Ember.Route.extend({
myVar: null,
model: function() {
var data = getData();
console.log(myVar); // = null
return data;
},
getData: function() {
var self = this;
return new Ember.RSVP.Promise( function(resolve, reject) {
self.setProperties({ myVar: 42 });
resolve(someGoodStuff);
reject(someBadStuff)
});
}
})
When I try to display myVar, it still at null even when it was waiting for the promise to resolve...
Do I need to do something special ? or I'm doing it wrongly ?
I believe you are looking for something like this: http://emberjs.jsbin.com/gosoj/3/edit?html,css,js,output
I'm not entirely sure what you are going for here, so there is also some other commented code that may have been your intention.
I think maybe the problem was when you were console logging, as well as the fact that you weren't using your getters and setters properly, and the way you were calling getData()
Here is the code:
App.ApplicationRoute = Ember.Route.extend({
myVar: null,
model: function() {
var data = this.getData();
console.log(data);
console.log(this.get('myVar')); // = null
return data;
},
getData: function() {
var self = this;
return new Ember.RSVP.Promise( function(resolve, reject) {
self.setProperties({ myVar: 42 });
resolve(42);
reject(reason);
}).then(function (value) {
//self.setProperties({ myVar: value });
console.log(self.get('myVar'));
}, function(reason){
console.log(reason);
});
}
})

ember.js sending actions to controllers from views

I've created a typeahead view and i'm trying to send an action to the current controller to set a property. Here is my typeahead view
App.Typeahead = Ember.TextField.extend({
dataset_name: undefined, //The string used to identify the dataset. Used by typeahead.js to cache intelligently.
dataset_limit: 5, //The max number of suggestions from the dataset to display for a given query. Defaults to 5.
dataset_template: undefined, //The template used to render suggestions. Can be a string or a precompiled template. If not provided, suggestions will render as their value contained in a <p> element (i.e. <p>value</p>).
dataset_engine: undefined, //The template engine used to compile/render template if it is a string. Any engine can use used as long as it adheres to the expected API. Required if template is a string.
dataset_local: undefined, //An array of datums.
dataset_prefetch: undefined, //Can be a URL to a JSON file containing an array of datums or, if more configurability is needed, a prefetch options object.
dataset_remote: undefined, //Can be a URL to fetch suggestions from when the data provided by local and prefetch is insufficient or, if more configurability is needed, a remote options object.
ctrl_action: undefined,
didInsertElement: function () {
this._super();
var self = this;
Ember.run.schedule('actions', this, function () {
self.$().typeahead({
name: self.get('dataset_name'),
limit: self.get('dataset_limit'),
template: self.get('dataset_template'),
engine: self.get('dataset_engine'),
local: self.get('dataset_local'),
prefetch: self.get('dataset_prefetch'),
remote: self.get('dataset_remote')
}).on('typeahead:selected', function (ev, datum) {
self.selected(datum);
});
});
},
willDestroyElement: function () {
this._super();
this.$().typeahead('destroy');
},
selected: function(datum) {
this.get('controller').send(this.get('ctrl_action'), datum);
}
});
Here's an implementation
App.CompanyTA = App.Typeahead.extend({
dataset_limit: 10,
dataset_engine: Hogan,
dataset_template: '<p><strong>{{value}}</strong> - {{year}}</p>',
dataset_prefetch: '../js/stubs/post_1960.json',
ctrl_action: 'setCompanyDatum',
selected: function (datum) {
this._super(datum);
this.set('value', datum.value);
}
});
and here's my controller
App.PeopleNewController = Ember.ObjectController.extend({
//content: Ember.Object.create(),
firstName: '',
lastName: '',
city: '',
state: '',
ta_datum: undefined,
actions: {
doneEditing: function () {
var firstName = this.get('firstName');
if (!firstName.trim()) { return; }
var lastName = this.get('lastName');
if (!lastName.trim()) { return; }
var city = this.get('city');
if (!city.trim()) { return; }
var state = this.get('state');
if (!state.trim()) { return; }
var test = this.get('ta_datum');
// Create the new person model
var person = this.store.createRecord('person', {
firstName: firstName,
lastName: lastName,
city: city,
state: state
});
// Clear the fields
this.set('firstName', '');
this.set('lastName', '');
this.set('city', '');
this.set('state', '');
// Save the new model
person.save();
},
setCompanyDatum: function(datum) {
this.set('ta_datum', datum);
}
}
});
I'm expecting the setCompanyDatum controller action to be called, but it's not. Everything else is working as expected. The App.Typeahead.selected method is being called with the right action name, but it doesn't actually call the action method.
the controller inside your App.Typeahead points to the instance of the App.Typeahead, not the controller from the route where you are creating the view.
You should just be using sendAction
http://emberjs.jsbin.com/EduDitE/2/edit
{{view App.Typeahead}}
App.IndexRoute = Ember.Route.extend({
model: function() {
return ['red', 'yellow', 'blue'];
},
actions:{
externalAction:function(item){
console.log('helllllo' + item);
}
}
});
App.Typeahead = Ember.TextField.extend({
internalAction: 'externalAction',
didInsertElement: function () {
this.sendAction('internalAction', " I'm a sent action");
this._super();
}
});

Ember How fire observes when create object(observe was set already in model)

I cannot fire observes function when was created object in Controller Array
My code:
Model
App.Meeting = Em.Object.extend({
id: null,
name: null,
type: null,
proposes: null
});
App.Meeting.reopen({
proposedChanged: function() {
//some do
}.observes('proposes')
});
Controller
App.meetingsController = Ember.ArrayController.create({
content: [],
loadList: function(){
var me = this;
$.getJSON(url,function(data){
if(data.status == 0){
$(data.meetings).each(function(index,value){
var m = App.Meeting.create(value)
me.pushObject(m);
});
}else{
alert('Error loading content');
}
});
},
});
App.meetingsController.loadList();
When i run application Controller has get JSON data and created App.Meeting with that data, but observer not fire
While I was creating a jsbin to play with #Darshan Sawardekar got it right, so now you have to answers to play with :)
The important code:
App.meetingsController = Ember.ArrayController.create({
content: [],
loadList: function(){
var me = this;
$.getJSON(url, function(data){
if(data.status == 0){
$(data.meetings).each(function(index, value){
var m = App.Meeting.create();
m.set('id', value.id);
m.set('name', value.name);
m.set('type', value.type);
m.set('proposes', value.proposes);
me.pushObject(m);
});
} else {
alert('Error loading content');
}
});
}
});
Hope it helps.
EDIT
See here for a working jsbin that shows the concept.
I think observers fire when you do meeting.set('proposes', 'value'). They don't fire inside a create call. You could modify your create to retouch proposes. This might work,
var m = App.Meeting.create(value);
m.set('proposes', value.proposes);

Backbone.js results of fetch doubling

I am trying to use Django and tastypie with backbone.js and mustache. I have set up an example to study those. When using the below code I get results of users doubling as:
-Id User name
-1 yol
-2 dada
-1 yol
-2 dada
--- MY CODE ---
// I think this tastypie adjustment is not related with the problem but i want you to see the //whole code
window.TastypieModel = Backbone.Model.extend({
base_url: function() {
var temp_url = Backbone.Model.prototype.url.call(this);
return (temp_url.charAt(temp_url.length - 1) == '/' ? temp_url : temp_url+'/');
},
url: function() {
return this.base_url();
}
});
window.TastypieCollection = Backbone.Collection.extend({
parse: function(response) {
this.recent_meta = response.meta || {};
return response.objects || response;
}
});
(function($){
// MODELS-COLLECTIOS
//USERS
var User = TastypieModel.extend({
url: USERS_API
});
var Users = TastypieCollection.extend({
model: User,
url:USERS_API
});
//VIEWS
var UsersView = Backbone.View.extend({
render: function(){
// template with ICanHaz.js (ich)
this.el = ich.userRowTpl(this.model.toJSON());
return this;
}
});
//main app
var AppView = Backbone.View.extend({
tagName: 'tbody',
initialize: function() {
this.users = new Users();
this.users.bind('all', this.render, this);
this.users.fetch();
},
render: function () {
// template with ICanHaz.js (ich)
this.users.each(function (user) {
$(this.el).append(new UsersView({model: user}).render().el);
}, this);
return this;
}
});
var app = new AppView();
$('#app').append(app.render().el);
})(jQuery);
I would say you are firing two times the render, because your view is listening to all events, try instead to bind it just to reset and see how it goes:
initialize: function() {
this.users = new Users();
this.users.bind('reset', this.render, this);
this.users.fetch();
},