Ember View is rendering too early - ember.js

I have a Route for creating new documents, making a copy of an existing document.
App.DocumentsNewRoute = Ember.Route.extend({
model: function (params) {
this.modelParams = params; // save params for reference
return App.Document.createRecord();
},
setupController: function (controller, model) {
// use the params to get our reference document
var documentModel = App.Document.find(this.modelParams.document_id);
documentModel.one('didLoad', function () {
// once loaded, make a serialized copy
var documentObj = documentModel.serialize();
// and set the properties to our empty record
model.setProperties(documentObj);
console.log('didLoad');
});
}
});
I added some logs in my View.
App.DocumentView = Ember.View.extend({
init: function () {
this._super();
// fires before the didLoad in the router
console.log('init view');
},
willInsertElement: function () {
// fires before the didLoad in the router
console.log('insert elem');
}
});
And this is my template
{{#if model.isLoaded }}
{{ view App.DocumentView templateNameBinding="model.slug" class="document portrait" }}
{{ else }}
Loading...
{{/if}}
The problem it seems is that my model isLoaded, but not populated when my template is rendered, so the templateNameBinding doesn't exist at this point, and doesn't seem to be updated when the data gets populated.
Should I use something else than model.isLoaded in my template, or should I force a re-render of my template, and if so, how and where? Thanks!

It seem that you are overcomplicating the things. It should be:
EDIT
I misunderstood your question in first read, so
App.DocumentsNewRoute = Ember.Route.extend({
model: function (params) {
var originalDoc = App.Document.find(params.document_id),
newDoc = App.Document.createRecord();
originalDoc.one('didLoad', function () {
newDoc.setProperties(this.serialize());
});
return newDoc;
},
setupController: function (controller, model) {
controller.set('content', model);
}
});
If everything is right, ember will re-render things automatically..

Related

Can't get property/model array in selection binding until too late

I have an Ember Select view in my Ember CLI project that is bound to a dynamic model (an array of trips) that is created in setupController. Once loaded up the selection needs to default to a particular one indicated by tripId.
The method called in selectionBinding (selectedTrip) should do this but it can't seem to get the tripArray from model.
I've called console.debug(this.get('model')) and I can see tripArray there filled with entries but console.debug(this.get('model.tripArray')) returns undefined. It's almost like an illusion. At this point tripId transforms into null and the starting position for the select view is the prompt.
It's only until all the components are loaded that the controller can access tripArray. What am I doing wrong?
Controller:
export default Ember.ObjectController.extend(Ember.Validations.Mixin,{
trips: function(){
return this.get('model.tripArray');
}.property('model.tripArray'),
selectedTrip: function() {
var tempTrips = this.get('trips');
var trip;
console.debug(this.get('model.tripArray'));
if(tempTrips){
for(var i = 0; i < tempTrips.length; i++){
if(tempTrips[i].id === this.get('model.tripId')){
trip = tempTrips[i];
return;
}
}
}
return trip;
}.property('trips')
}
Route:
export default Ember.Route.extend({
beforeModel: function(){
this.controllerFor('application').checkSuperLogin();
},
setupController: function(controller, model){
this._super(controller, model);
controller.set('model', model);
$("#loading-spinner").modal("show");
$.getJSON("http://website.herokuapp.com/trips", function (data) {
console.log(data);
controller.set('model.tripArray', data);
}).done(function(){
$("#loading-spinner").modal("hide");
});
}
});
Select view:
{{view Ember.Select
contentBinding="controller.trips"
valueBinding="model.tripId"
optionValuePath="content.id"
optionLabelPath="content.locationName"
prompt="-"
classNames="form-control input-md"
selectionBinding="controller.selectedTrip"
}}

Ember: store.update updates all attributes?

I'm creating a real time multiplayer textual game in Ember.js.
So far very exciting, but I'm running a little problem.
I have a game model which looks a bit like this:
App.Game = DS.Model.extend({
numbers: DS.attr(),
drawnNumbers: DS.attr(), // array
gameStatus: DS.attr(),
table: DS.belongsTo('table'),
bingoCards: DS.hasMany('bingoCard', { async: true })
});
My controller looks like this (leaving out unnecessary information):
App.GameController = Ember.ObjectController.extend({
gameBingoCards: function () {
var gameId;
gameId = this.get('id');
console.log("inside gameBingoCards");
return this.get('store').filter('bingoCard', function (bingoCard) {
return (bingoCard.get('game.id') === gameId);
});
}.property('model.bingoCards'),
ownBingoCards: function () {
var gameId, userId;
gameId = this.get('id');
userId = this.get('session.content.id');
console.log("inside ownBingoCards");
return this.get('store').filter('bingoCard', function (bingoCard) {
return (bingoCard.get('game.id') === gameId && bingoCard.get('user.id') === userId);
});
}.property('gameBingoCards.[]'),
gameMessages: function () {
var gameId;
gameId = this.get('id');
console.log("gameMessages");
return this.get('store').filter('message', function (message) {
return (message.get('game.id') === gameId);
});
}.property('model.messages'),
});
In the view I render the cards:
{{#each bingoCard in ownBingoCards}}
<div class="col-sm-4">
<div class="table-responsive">
<span class="label label-primary">Card {{bingoCard.id}}</span>
<table class="table table-bordered table-card">
<tbody>
{{#each row in bingoCard.squares}}
<!-- displaying the numbers here -->
{{/each}}
</tbody>
</table>
</div>
</div>
{{/each}}
Whenever the game updates I update the store like this:
record = serializer.extractSingle(store, type, data);
// record looks like this:
// {id: "538c56843800226245c3621a", gameStatus: "idle"}
store.update("game", record);
If I open the console I get the following:
inside ownBingoCards GameController.js:102
inside gameBingoCards GameController.js:32
inside ownBingoCards GameController.js:102
Note: the game receives many updates during the game, so every time all the cards get rerendered. How can I prevent this?
edit:
After I reload the page on that specific game route it only goes inside ownBingoCards and gameBingoCards once and it doesn't re-render everytime after an update.
edit2:
The gameMessages attribute also only gets called once, why does the gameBingoCards keep getting called?
Alright, I've fixed it after countless hours.
My route looked like this:
model: function (params) {
return this.store.find('game', params.game_id);
},
setupController: function (controller, model) {
model.reload();
controller.set('model', model);
},
And I've changed it to this:
model: function (params) {
return this.store.find('game', params.game_id);
},
setupController: function (controller, model) {
model.reload();
controller.set('model', model);
controller.set('modelBingoCards', model.get('bingoCards'));
controller.set('modelMessages', model.get('messages'));
},
Plus I also changed the property listeners to .property('modelMessages') and .property('modelBingoCards').
Could any please tell me why this worked?

Setting a belongsTo attribute via an action on controller not working

I have the following models, customer:
App.Customer = DS.Model.extend({
//Other Attributes
area: DS.belongsTo('area', {async: true})
});
and area model -
App.Area = DS.Model.extend({
name: DS.attr('string'),
zip_code: DS.attr('number')
});
And I use an Ember.Select view to show a dropdown of the area a customer is in -
{{view Ember.Select viewName="select_area" content=possible_areas optionValuePath="content.id" optionLabelPath="content.name" value=area.id prompt="No area selected" selectionBinding="selected_area"}}
and the controller which wires up everything together -
App.CustomerController = Ember.ObjectController.extend({
possible_areas: function() {
return this.store.find('area');
}.property(),
selected_area: null,
actions: {
save: function() {
var selected_area_id = this.get('selected_area.id');
console.log(selected_area_id);
var selected_area = this.store.find('area', selected_area_id);
var self = this;
selected_area.then(function(area) {
console.log(area.get('id'));
var updated_customer = self.get('model');
updated_customer.set('area', area );
console.log(new_customer.get('area.id'));
updated_customer.save()
.then(function(customer) {
//success
},
function() {
//failure
});
});
}
}
});
Now here is the weird thing. Upon calling the 'save' action the first time, the line updated_customer.set('area', area ); fails with the error
Uncaught Error: Assertion Failed: Cannot delegate set('id', ) to the 'content' property of object proxy <DS.PromiseObject:ember551>: its 'content' is undefined.
Upon calling 'save' action immediately after that, the save goes through without any error and area of the customer is updated successfully. Although the dropdown shows selected_area to be null.
How do I prevent the first save from erroring out?
I am using ember-data 1.0.0-beta.6.
Since you have the association defined in your Customer model, I would remove the selected_area property from the controller and use ember-data's associations instead. Bind to the "area" association in the Ember.Select by using the selectionBinding property.
{{view Ember.Select viewName="select_area"
content=possible_areas
optionValuePath="content.id"
optionLabelPath="content.name"
prompt="No area selected"
selectionBinding="area"}}
This way, the area attribute will change when the user interacts with the select menu.
This has the added benefit of cleaning up your save action since we're binding directly to the area association for the Customer.
App.CustomerController = Ember.ObjectController.extend({
possible_areas: function() {
return this.store.find('area');
},
actions: {
save: function() {
this.get('model').save().then(this.onDidCreate.bind(this), this.onDidFail.bind(this))
}
onDidCreate: function() {
// Fullfilled callback
}
onDidFail: function() {
// Rejected callback
}
}
});
However, the possible_areas property won't be populated when the template first renders since this.get('area') returns a promise. I would wait to render the select until the promise settles. You can do this in the routes model hook since it waits until promises settle to render the template.
App.CustomerRoute = Ember.Route.extend({
route: function(params) {
return Ember.RSVP.hash({
customer: this.store.find('customer', params.customer_id),
possible_areas: this.store.find('area')
});
},
// Since the route hook returns a hash of (hopefully) settled promises, we
// have to set the model property here, as well as the possible_areas property.
setupController: function(controller, model) {
controller.set('model', model.customer);
controller.set('possible_areas', model.possible_areas);
}
});

Computed property in handlebar #if not updating

I am trying to use the following template:
<script type="text/x-handlebars" data-template-name="login">
{{#if logged_in}}
Logged in
{{else}}
Not logged in
{{/if}}
</script>
with the model:
App.Login = DS.Model.extend({
access_token: DS.attr('string'),
logged_in: function() {
return (this.get('access_token') != null);
}.property('access_token')
});
to display the user's logged-in state.
The access_token is being set via an async callback in the Route's setupController:
App.LoginRoute = Ember.Route.extend({
setupController: function(controller, model) {
controller.set('content', model);
// call async login method
window.setInterval(function test() {
model.set('access_token', 'MY_ACCESS_TOKEN');
console.log(model.get('access_token'));
}, 5000);
},
model: function() {
return App.Login.find();
}
});
The problem is logged_in never seems to change (even though the model.set line is executed and 'access_token' is updated). Am I doing something wrong or should I be filing a bug?
Full code: http://jsfiddle.net/Q8eHq/
You are setting the model to App.Login.find() which returns an enumerable, not a single object. One way to do it, is to set the model to a single object:
App.LoginRoute = Ember.Route.extend({
model: function() {
return App.Login.find(1);
}
});
Or if you are going to use a dynamic route (e.g. users/login/9):
App.LoginRoute = Ember.Route.extend({
model: function(params) {
return App.Login.find(params.id);
}
});

Same Ember.JS template for display/edit and creation

I am writing a CRUD application using Ember.JS:
A list of “actions” is displayed;
The user can click on one action to display it, or click on a button to create a new action.
I would like to use the same template for displaying/editing an existing model object and creating a new one.
Here is the router code I use.
App = Ember.Application.create();
App.Router.map(function() {
this.resource('actions', {path: "/actions"}, function() {
this.resource('action', {path: '/:action_id'});
this.route('new', {path: "/new"});
});
});
App.IndexRoute = Ember.Route.extend({
redirect: function() {
this.transitionTo('actions');
}
});
App.ActionsIndexRoute = Ember.Route.extend({
model: function () {
return App.Action.find();
}
});
App.ActionRoute = Ember.Route.extend({
events: {
submitSave: function () {
this.get("store").commit();
}
}
});
App.ActionsNewRoute = Ember.Route.extend({
renderTemplate: function () {
this.render('action');
},
model: function() {
var action = this.get('store').createRecord(App.Action);
return action;
},
events: {
submitSave: function () {
this.get("store").commit();
}
}
});
The problem is that when I first display an action, and then come back to create a new one, it looks like the template is not using the newly created record, but use instead the one displayed previously.
My interpretation is that the controller and the template are not in sync.
How would you do that?
Maybe there is a simpler way to achieve this?
Here is a JSBin with the code: http://jsbin.com/owiwak/10/edit
By saying this.render('action'), you are not just telling it to use the action template, but also the ActionController, when in fact you want the action template, but with the ActionNewController.
You need to override that:
this.render('action', {
controller: 'actions.new'
});
Updated JS Bin.