I would like to include multiple mixins within a view in Ember.js and more than one of the mixins and/or the view uses a same event (e.g. willInsertElement). I'm running Ember 1.4.0-beta.5.
I understand that the event in each mixin will be overridden by the view. However, I have read that it is possible to use the same event hook in the mixin and view, or multiple mixins included in the same view, by calling this._super(); at the start of the mixin's aforementioned event method. However, I have not been able to successfully make this happen. My question is, thus, how can I write logic within the same event hook in a view and mixin (or multiple mixins included in the same view) so that all the logic within each occurrence of the event hook will be called.
Here is an example:
App.StatsView = Em.View.extend(
App.DateFormatting, {
willInsertElement: function() {
// Some view-specific logic I want to call here
},
});
App.DateFormatting = Em.Mixin.create({
willInsertElement: function() {
this._super(); // This doesn't work.
// Some mixin logic I want to call here
},
});
N.B. One approach here might be to not use a mixin and extend a view instead (because willInsertElement is specific to Em.View), but that isn't maintainable in our apps.
If the different functions you're using are not dependent on each other, it's the best solution to not override the willInsertElement hook, but to tell the function to be raised when the event/hook gets called.
Like:
App.StatsView = Em.View.extend(App.DateFormatting, {
someSpecificFunction: function () {
console.log('beer me');
}.on('willInsertElement')
});
App.DateFormatting = Em.Mixin.create({
dateFormattingFunction: function () {
console.log('beer you');
}.on('willInsertElement')
});
Related
Is there a way to have multiple functions that are all called on a route's didTransition event?
Here is an example where the actions.didTransition is run normally, but "someHook" is not: http://emberjs.jsbin.com/hedebigedi/1/edit?html,js,console,output
Is running arbitrary functions even supposed to be possible by using ".on()"?
Have I misunderstood what an event means in this case?
The reason I wanted to do this is because I wanted to make a mixin that would get added to certain routes that would then do some general setup after didTransition, but the routes would also need to do some custom setup as well. I can copy-paste the same bit of code into each route's actions.didTransition, but I'd rahter have it only in one place.
It doesn't work. What would work is on('init'), but that is a method not an event, see: http://emberjs.com/api/classes/Ember.Route.html
To solve your problem do something like this:
import CleverMixin from 'path/to/mixin';
import AnotherMixin from 'path/to/mixin';
App.IndexRoute = Ember.Route.extend(CleverMixin, AnotherMixin, {
model: function() {
return ['red', 'yellow', 'blue'];
},
actions: {
didTransition: function () {
// This function will be provided by a mixin
this.setupStuff();
// This function will be provided by another mixin
this.doMoreStuff();
}
}
});
I am maintaining a javascript application and I would like there to be a jquery function invoked on pretty much every view. It would go something like this:
SomeView = Backbone.Marionette.ItemView.extend
initialize: ->
#on( 'render', #after_render )
after_render: ->
this.$el.fadeOut().fadeIn()
Clearly there is a better way to do this than have an after_render() in each view? What is the better way to do it? If you can give an answer that includes jasmine tests, I'll <3 you ;)
The event you are looking for is onDomRefresh. See here for the documentation:
https://github.com/marionettejs/backbone.marionette/blob/master/docs/marionette.view.md#view-domrefresh--ondomrefresh-event
Create your own base view class and put your afterRender code in it. When you create a view, inherit from this class.
var MyApp.ItemView = Backbone.Marionette.ItemView.extend({
afterRender: function() {
// This will be called after rendering every inheriting view.
}
});
var SpecificItemView = MyApp.ItemView.extend({
// this view will automatically inherit the afterRender code.
});
In general, it seems to be considered good practice to define your own base views for all 3 view types. It will enable you to easily add global functionality later.
There is a common pattern used across all Backbone frameworks, normally they have a render method which in turn calls beforeRender, renderTemplate and afterRender methods.
render:function(){
this.beforeRender();
this.renderTemplate();// method names are just indicative
this.afterRender();
return this;
}
In your Base view you can have these methods to be empty functions, and implement them wherever you want it. Not sure this answer applies to Marionette
Combining thibaut's and Robert Levy's answer, the correct solution would be:
var baseView = Backbone.Marionette.ItemView.extend({
onDomRefresh: function() {
// This will be triggered after the view has been rendered, has been shown in the DOM via a Marionette.Region, and has been re-rendered
// if you want to manipulate the dom element of the view, access it via this.$el or this.$('#some-child-selector')
}
});
var SpecificItemView = baseView.extend({
// this view will automatically inherit the onDomRefresh code.
});
I'm developing a webapplication with Resthub, so there is a backbone.js stack at the front-side. I need to call a method, everytime a new view (also all sorts of subviews) is rendered, to add some Twitter-Bootstrap specific stuff (help-popovers, kind of quick help, which get their options from a global json file, so the help-texts are easier to maintain).
As far as I know there isn't a backbone-built-in event which is fired every time a view is rendered.
So my question is: What is the easiest way to extend all views, so that they fire an event when the render method is (implicitly or explicitly) called. I want to extend all my views cause I don't want to trigger this event manually in all views I have, because it's error-prone and all developers has to remember that they've to trigger that event.
If you want to do something(fire an event or anything else) for all cases when the render method is called, the most straight forward way might be to update the render method in your copy of Backbone's source code (assuming you want the behavior across the project).
By default the render method just returns 'this'
render: function() {
return this;
},
If there is something you always want to do before render, you can add it within the render method
render: function() {
//add your extra code/call
return this;
},
Alternatively you can also override the prototype of Backbone.View function and update/create your own version(s) something like
_.extend(Backbone.View.prototype, Backbone.Events, {
render: function() {
console.log('This is a test');
return this;
}
});
var testView = Backbone.View.extend({
});
var testview = new testView();
testview.render(); //displays This is a test
//any view rendered will now have the console log
Taking this a step further, you can add your own version of render, calling it say 'myrender' and/or add your own event(s) say 'myevent' which can then be called before/after you call render/myrender
_.extend(Backbone.View.prototype, Backbone.Events, {
render: function() {
//console.log('This is a test');
this.mynewevent();
return this;
},
myrender: function() {
console.log('Pre-render work');
this.render();
},
mynewevent: function() {
console.log('New Event work');
}
});
var testView = Backbone.View.extend({
});
var testview = new testView();
//testview.render();
testview.myrender();
Underscore's extend is being used here and since Backbone has a dependency on Underscore, if you are using Backbone, Underscore should be available for you as well.
I want to write a reusable component as part of my Backbone app. Fundamentally I want to write a form filter helper so I can:
call a func inside a view js file which will create a drop-down which can listen to changes and then trigger changes in data and refreshes the view.
Ultimately I'd like to be able to do something like this:
// generic view.js
// to spawn a dropdown
formFilter('select', data);
// to spawn a series of checkboxes
formFilter('checkbox', data);
Obviously the module code would listen for events and handle the work.
My question is, what is the standard way of creating a reusable component? Google isn't giving me much and the #documentcloud IRC isn't particularly active.
Based on the information you've provided in your question, it's not easy to say how your particular component would be best componentized. However, one powerful strategy for reusability is mixins.
Simply you define the methods in a simple object literal, such as:
Mixins.filterable = {
filterForm: function(selector, data) {
this.$(selector)...
}
}
Mixins.sortable = {
sortForm: function(selector) {
this.$(selector)...
}
}
And then you can mix them into any View's prototype:
_.extend(FooView.prototype, Mixins.filterable, Mixins.sortable);
The mixin methods will then be available in all instances of FooView.
render: function() {
//..
this.filterForm('select', this.model);
}
Because the mixin methods will be bound to the context of the view instance, you can refer to this, and by logical extension, this.$el, inside the mixin methods. This will enable you to listen to the view's events:
Mixins.filterable = {
filterForm: function(selector, data) {
this.$(selector).on('change', this._handleFilterChange);
},
_handleFilterChange: function(e) {
//..
}
}
To make the methods available on all views, mix them into the Backbone.View prototype instead:
_.extend(Backbone.View.prototype, Mixins.filterable, Mixins.sortable);
I need to run a layout script as soon as my views are inserted into the DOM. So...
$(".widgets").append(widgets.render().el)
$(".widgets .dashboard").isotope # <-- This needs to be called whenever new widgets are inserted
The problem is I have to insert new widgets a few different views and re-call this script a few different places, which is not DRY. I am wondering how I can define the isotope in the View class.
Would it be a good idea to define an event listener to watch for append into the ".widgets" and to run the script? Is there a built in way of building views that are smart about when they are added to the DOM?
(For that matter, it would be also useful to define a callback for when a View is removed from the DOM.)
How about calling the isotope each time the view is rendered? You'll need to be careful to call render() only after the widget is injected, but this ought to take care of your problem:
//in Backbone.view.extend({
initialize: function() {
// fix context for `this`
_.bindAll(this);
},
render: function() {
// .. do rendering..
this.isotope();
return this;
}
// }) // end .extend
use:
var self = this;
this.$el.on('DOMNodeInserted', function(evt){
self.isotope();
$(evt.target ).stopPropagation();
})