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.
Related
I am using a radialProgress as a jQuery plugins (homemade), and I need to implement it for ember but I have some issue to do that.
Quick explanation for the plugins :
var chart = $(yourElement).pieChart(options); // initialise the object to an element
chart.setCompleteProgress( complete, false ); // set how many item you have to complete the task
chart.incrementProgress(); // increment + 1 every time you call it
It's a very simple progress pie.
In my case my task are located inside my controller, but the chart as to select a dom element so I need to initialise it inside my view.
My task in the controller are called from the router from the setupController to reload the model over time.
Here is a small sample of what I would like to do :
App.ApplicationRoute = Ember.Route.extend({
setupController: function(controller) {
var promise = controller.getModel();
this._super(controller, promise);
}
})
App.ApplicationController = Ember.ArrayController.extend({
getModel: function() {
// chart.setcompleteProgress();
// A lot of code are here to get some data
// chart.incrementProgress();
return newModel;
}
})
App.ApplicationView = Ember.View.extend({
didInsertElement: function() {
var chart = $(element).pieChart(opts);
}
})
I don't know how to pass the chart object from the view to the controller to be able to have access to my plugin function.
Che chart won't be inserted into the DOM until the didInsertElement therefore you can't attempt to manipulate it in the route during setupController etc. I'd suggest creating a method in the controller setupChart and calling that on didInsertElement.
App.ApplicationView = Ember.View.extend({
prepPieChart: function() {
var chart = $(element).pieChart(opts);
this.get('controller').setupPieChart(chart);
}.on('didInsertElement')
})
App.ApplicationController = Ember.ArrayController.extend({
setupPieChart: function(chart) {
chart.setcompleteProgress();
// A lot of code are here to get some data
chart.incrementProgress();
}
})
All that being said, maybe it belongs in the view, but I'm not sure of what you're completely doing.
I'm loading the RebelMouse embed widget into an Ember.View, like so:
App.RebelMouseView = Em.View.extend({
didInsertElement: function() {
var widgetEmbedCode = '<script type="text/javascript" class="rebelmouse-embed-script" src="https://www.rebelmouse.com/static/js-build/embed/embed.js?site=W3portals&height=900&flexible=1"></script>';
this.$().append(widgetEmbedCode);
}
});
But I'm hoping there's a better way to do so.
One undesired side-effect is that everytime I initialize that view it reloads the entire widget with a 1 second delay. Thanks.
Ok, good idea on showing/hiding, see http://jsfiddle.net/9EC8F/ for how to do it. Basically, the trick is to keep the view outside any outlet that will be torn up when the route changes. Then, put this in your route:
activate: function() {
$(".rebel-mouse-view").show();
},
deactivate: function () {
$(".rebel-mouse-view").hide();
}
and this in your view:
classNames: ['rebel-mouse-view'],
Something like document ready, but after all Ember views rendering
I am doing this right now with an override on ApplicationView didInsertElement, which seems to be working so far:
App.ApplicationView = Em.View.extend({
didInsertElement: function() {
// Do your magic.
}
});
I am wondering if this is the right way for an Ember document ready, or if Ember has a more native support for this simple and very common thing.
You can easily add a "post render" hook by reopening the base View class and adding it into the render queue.
Here's some code to show you how:
Ember.View.reopen({
didInsertElement : function() {
this._super();
Ember.run.scheduleOnce('afterRender', this, this.didRenderElement);
},
didRenderElement : function() {
// Override this in your View's
}
});
The didInsertElement is the right place, but if you want to be completely sure your render queue is completely flushed you could also listen to the afterRender event, something like this:
App.ApplicationView = Ember.View.extend({
didInsertElement: function() {
Ember.run.scheduleOnce('afterRender', this, 'processChildElements');
},
processChildElements: function() {
// do here what you want with the DOM
}
});
Hope it helps.
App.ApplicationView = Ember.View.extend({
afterRender: function () {
Ember.run.next(this, function () {
// This will run one time, after the full initial render.
});
}
});
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')
});
In a method on an Ember.View subclass, I would like to make changes to the DOM only if the view element has already been inserted into the DOM. How can I check that?
I know I could create an auxiliary property like so:
didInsertElement: function() {
this.set('elementIsInserted', true);
}
willDestroyElement: function() {
this.set('elementIsInserted', false);
}
But is there some canonical, built-in way?
I didn't find anything skimming view.js, but perhaps I'm missing something.
Every view has a _state property, which is set to "inDOM" when the element is inserted.
if (this._state=="inDOM") doStuff();
should work. Make sure you have the correct this!
If you want to avoid having to set an auxiliary flag, you can extend Ember.View:
Ember.View.reopen({
didInsertElement: function() {
this.set('elementIsInserted', true);
this._super();
},
willDestroyElement: function() {
this.set('elementIsInserted', false);
this._super();
}
});
Now every View that extends Ember.View will get the above.
Also a member of the core team suggested that you avoid referring to inDOM as it is an internal variable and not intended to be used in code.