Access data from controller in view in didInsertElement - ember.js

I am trying to access data from DashboardIndexController in DashboardIndexView
JP.DashboardIndexController = Ember.Controller.extend({
users: []
});
Is it possible to access users in JP.DashboardIndexView in didInsertElement?
didInsertElement : function(){
console.log(this.get("controller.users").objectAt(0));
}
This is my DashboardIndexRoute:
JP.DashboardIndexRoute = Ember.Route.extend({
setupController: function(controller, model) {
controller.set('users', JP.User.find());
}
});
Thank you
EDIT
console.log(this.get("controller.users").objectAt(0));
Returns data only when I go to UsersIndex and then back to DashboardIndex... I think it's something with initialization, but I don't know, how to fix it.

Yes, this is how to access the list of users.
This happens because the DashboardIndexView is inserted into the DOM before the controller.users is populated with data from the server.
So when the view is rendered, controller.users is still empty, and will asynchronously be populated after the ajax request finishes, problem is, didInsertElement already fired.
In order to solve this, you need to access controller.users from another hook. DidInsertElement is the wrong place, because it will fire irrespective of whether JP.User.find() finished loading.
You just need to make sure that it re-fires when JP.User.find() is loaded even if view is already in the DOM. Something like this:
JP.DashboardIndexView = Ember.View.extend({
didInsertElement: function() {
this.usersChanged();
},
usersChanged: function() {
if (!this.$()) { return; } // View not in DOM
console.log(this.get('controller.users'));
}.observes('controller.users.#each')
});

Related

Loading Routes in nested route Hierachy

I am working on a mobile application with Ember. I want to make the user experience as good as possible and try to take into account that on mobile the connection is not always as good, that is why I want to utilize the loading routes with a loading spinner. Unfortunately in one case it is not behaving as I would expect:
In my Nested route Setup:
UserRoute:
UserIndexRoute (=Profile)
UserFriendsRoute
On the UserRoute I only load a small version (=different model) of the user. In 95% of the cases this model is already loaded when I want to navigate there. And in the Subroutes (e.g. UserIndexRoute and UserFriendsRoute I only need the full user.
What I want to achieve is that the UserRoute with its template is directly rendered when navigating to e.g. UserIndexRoute and then in the outlet for the Index part I want the UserLoadingView to be rendered. But the rendering always waits for all promises to be resolved and the UserLoadingView is never shown.
How can I force Ember to render the UserRoute and then the UserLoadingView in the outlet until the UserIndexRoute Model is resolved?
How I implemented it:
afterModel: function(model, transition){
var _this = this,
params = Ember.get(transition, 'params.user');
this.get('store').find('user', params.user_id).then(function(user){
_this.transitionTo('user.profile', user);
});
}
Don't use the index route for fetching the full model, just use it as a means for redirection.
Do something like this:
UserRoute:
UserIndexRoute
UserFooIndexRoute (=Profile) (Naming is up to you)
UserFriendsRoute
Then hook up your index route to fetch the full model and transition to FooIndex when it's completed getting the model, this depends on it being a route with a dynamic segment (:id).
App.UserIndexRoute = Em.Route.extend({
redirect: function(){
var self = this;
fetchTheFullModel.then(function(model){
self.transitionTo('user.fooIndex', model);
}
}
});
If it isn't like that you can do just transition to the other route after the transition and page has finished rendering.
App.UserIndexRoute = Em.Route.extend({
redirect: function(model, transition) {
var self = this;
transition.then(function(){
Ember.run.scheduleOnce('afterRender', function(){
self.transitionTo('user.fooIndex');
});
});
}
});
http://emberjs.jsbin.com/zohav/1/edit
You can read more about the transition promise, and afterRender here Ember transition & rendering complete event

Ember + Ember Data: How to rollback a record after calling deleteRecord()

I have an Em.ArrayController with a bunch of records in it. They are all controlled by an itemController.
App.ColorsController = Em.ArrayController.extend({
itemController: 'color',
actions: {
discardChanges: function() {
this.get('content').forEach(function(color) { color.rollback(); }
// also tried an arrayComputed property like this:
// deletedRecords: Em.computed.filterBy('content', 'isDeleted', true);
}
}
});
If I call deleteRecord() on one of the models (from an action in the itemController, the model is removed from the model of the ArrayController.
App.ColorController = Em.ObjectController.extend({
actions: {
deleteColor: function() {
// does not send a `DELETE` request, only
// changes the state of the record
this.get('content').deleteRecord();
}
}
});
Remember that deleteRecord doesn't submit a network request, it merely transitions the state of the object to deleted.uncommitted.
Do I need to manually retain some sort of handle on this object after deleting it? Or is there some way for the ArrayController to access items in this state.
I've attempted to filter the content of the ArrayController by isDeleted.
Your array controller is being backed by a filter. Filters are active, in that they automatically add/remove records as the store has active records added/removed. (fyi, find('foo') returns the all filter).
You can copy the contents to a non active collection/array which won't automagically add/remove the models (you will have to do everything manually). The easiest place would be to override the setupController and add a property onto your controller, which can be accessed in your template.
App.FooRoute = Em.Route.extend({
model: function(){
this.store.find('foo');
},
setupController: function(controller, model){
this._super(controller, model);
controller.set('staticFoos', model.toArray());
}
});

ember concerns implementation

How would you implement concerns in ember. For instance, send invite functionality:
user have 5 invites (store involved from fetching data)
invite available from any application state
it appears in modal
it can be more than one more - thus {{outlet modal}} doesnt work as well
user can enter email and send invite (available invites number decreased)
current modal implementation - thus cannot assign controller to a view...
openModal: function(modal) {
var modalView = this.container.lookup(modal);
modalView.appendTo(MS.rootElement);
}
Component approach doesnt work as for me: content (model) should be setuped by routed, i dont know event in component which can be useful for data fetching.
Nested routes doesnt work as well - to much to change.
I cant find any working approach for this, please help.
Thanks in advance.
You can assign a controller to a view. Here's a simple example where I can inject a view from anywhere in the page, assign it a controller, and when I repop up the view it has the same controller (if that was the intended outcome). If this doesn't get you going in the right direction, let me know.
You can use bubbling to allow it to be called from anywhere in the app as well.
http://emberjs.jsbin.com/uhoFiQO/4/edit
App.InviteView = Ember.View.extend({
templateName: 'invite'
});
App.InviteController = Ember.Controller.extend({
actions: {
pretendToSend: function(){
var invites = this.get('invites');
this.set('invites', invites-1);
}
}
});
App.ApplicationRoute = Ember.Route.extend({
setupController: function(controller, model){
var inviteController = this.controllerFor('invite');
inviteController.set('invites', 5);
},
actions:{
popInvite: function(){
var inviteController = this.controllerFor('invite');
// create/insert my view
var iv = App.InviteView.create({controller: inviteController});
iv.appendTo('body');
}
}
});

How can catch event after template is rendered at EmberJS?

I have an application that uses masonry and Ember JS I attempt to search DOM an element by selector, but it retrieves null It seems I do it early than template was rendered. Please, help me resolve it.
#GJK answer is correct, I just want to provide a working example: http://jsbin.com/enijad/3/edit
App.IndexView = Ember.View.extend({
didInsertElement: function() {
var $container = $('#container');
$container.masonry({
columnWidth: 150,
itemSelector: '.item'
});
}
});
The didInsertElement function will be called when the view was inserted into the DOM, so it will be safe to initialize additionally libraries.
Also worth mentioning is that if you need some clearing up after the view was removed from the DOM you would do this in didInsertElement's counterpart hook willDestroyElement.
Example:
App.IndexView = Ember.View.extend({
didInsertElement: function() {
// do initialization here
},
willDestroyElement: function() {
// and here you can remove stuff safely
}
});
Hope it helps.
Create a corresponding View for your Route and Template, and then override the didInsertElement method.

isLoaded observer does not fire at right time

I think I'm doing something wrong but I don't know what.
When my application loads it needs to retrieve all companies and when those arrive it needs to set a property activeCompany on my ApplicationController. But when I bind an observer on content.isLoaded on my CompaniesController is fires before the data is loaded.
Application
App = Ember.Application.create({
ApplicationController : Ember.Controller.extend({
needs: ['companies'],
activeCompany: null,
activateCompany: function(company) {
this.set('activeCompany',company);
}
})
});
Router
App.ApplicationRoute = Ember.Route.extend({
enableLogging : true,
setupController: function(controller, model) {
this.controllerFor('companies').set('content', App.Company.find());
}
});
CompaniesController
App.CompaniesController = Em.ArrayController.extend({
needs: ['application'],
activateCompany: function() {
console.log(this.get('content.length')); // 0
console.log(this.get('content.isLoaded')); // true
console.log(this.get('content.firstObject')); // undefined
this.get('controllers.application').activateCompany(this.get('content.firstObject'));
}.observes('content.isLoaded')
});
Why does content.isLoaded fire when my data is not loaded?
Maybe my concept is wrong but the rest of my application depends on the activeCompany to retrieve other data. I also have a 'company-switcher' which also sets the activeCompany property.
When I change my observer to content.#each it fires for all the items that are in the Array.
EDIT
I could work around it like this:
App.CompaniesController = Em.ArrayController.extend({
needs: ['application'],
activateCompany: function() {
if (this.get('content.length') > 0)
this.get('controllers.application').activateCompany(this.get('content.firstObject'));
}.observes('content.firstObject.isLoaded')
});
This only fires when my firstObject changes.
It turns out I should use findQuery. I tried it before like this: App.Store.findQuery(App.Company) but that didn't work. The right way of doing is like this:
this.controllerFor('companies').set('model', this.get('store').findQuery(App.Company));
I needed to get the store via this.get('store')