Events not firing with Marionette - backbone.js

I'm creating a simple page using a CompositeView from Marionette, with a parent lodash template that wraps a group of templates: one for each item in its Collection.
The page displays properly, and each ItemView renders properly with it's own child template, but binding a 'click' event fails to register anywhere in the application, even if moved into the CompositeView.
I have my suspicions that this is happening due to the events being bound before the element is created, and it not updating afterwards. From what I have read, it uses / used .live() / .on(), but I can't see any other reason that it wouldn't register.
Is there a way to make sure that events are rebound with onRender()? Am I doing something else wrong entirely?
Environment: RequireJS, BabelJS (es6), Backbone 1.1.2, Marionette 2.4.1, jQuery 1.10.2
I've created a gist of the code in question.

Non ES6: Try assigning this.events before the parent constructor call, in Backbone 1.2.1+ the events are created BEFORE initialize in the parent constructor. In 1.1.2 they are created AFTER initialize, but still as the last thing the parent constructor does - so you should either assign them in initialize, or do it before the super call (I recommend before super call in the constructor so that if you upgrade it doesn't break again).
In ES6 you can't use this before the super call.
You could call this.delegateEvents(); again, but it's not ideal as it stops listening and then re-listening to any existing events which is less efficient (though the impact is probably negligible).
You can use es7's static property on the class definition if you are using a transpiler that supports it (e.g. Babel), or make events a function which returns an object if you need it to be dynamic.

Related

Angular 2: How property binding/interpolation works when data is changed in component

In Angular 1.x, the digest cycle use to take care of updating view when data is changed on scope. How does property binding and interpolation works in Angular 2 when data is changed in component?
Angular2 has its own version of Zone (called NgZone) that take care of detecting changes. When NgZone detects a change is called an event called onTurnDone. Angular has a component called ApplicationRef that listens for this event to occur and when it does the tick() method (of the ApplicationRef) is called. The tick() method, at this point, is the responsible for the changeDetection cycle that updates the view.
If I've understood the question you have made, then maybe you'd like to read a post that goes deeper into this subject of the change detection strategy:
https://blog.thoughtram.io/angular/2016/02/22/angular-2-change-detection-explained.html

A way to check if html changed in ReactJS

I have a JS code which should do some things (animations etc.) BEFORE ReactJS possibly (or not) will update HTML element. I'm getting the whole template for this HTML element (i.e. part of the page, in particular a widget) for this purpose.
For example, I have a DIV with some content, which may change in time or not. Then I get a new version (i.e. as HTML template) of that DIV through AJAX call, and I want ReactJS first to compare the actual (or virtual) current DOM with the one just retrieved new HTML template for the same DIV in a string as got from AJAX call on success. Only then - when these two are different - it makes sense to do animations first, and only then update the actual DOM in the browser by React JS.
How to do that? Is there a method in React JS that allows me to get e.g. true/false when comparing virtual DOM with its new version as a HTML template got through AJAX (since it does internally this thing anyway - i.e. comparing current and updated DOM in its algorithm)?
EDIT: Hmm after initial thought that ReactJS is way much better than other JS Frameworks in my case (i.e. server functionality is already developed), I found multiple features not flexible enough; further more, I found ReactJS is not smart enough to figure out that only one element at the top of the list is added (ReactJS replaces the whole DOM template for the list, even with keys set for each element!), which suggests it's still not that mature as one could expect.
Besides, all Lifecycle methods with ***Component***Update are not called; only ***Component***Unmount are being called in the case of mentioned list. I can do it in a simpler way by myself, however Relay / Flux + ReactJS architecture is still nice, and I think I'll just abandon Facebook's implementation, and replace it with my own solution, inspired by Relay/Flux+ReactJS.
You might want to look at the following life cycle methods:
componentWillUpdate(object nextProps, object nextState)
componentDidUpdate(object prevProps, object prevState)
From the react docs:
componentWillUpdate
Invoked immediately before rendering when new props or state are being received. This method is not called for the initial render.
Use this as an opportunity to perform preparation before an update occurs.
componentDidUpdate
Invoked immediately after the component's updates are flushed to the DOM. This method is not called for the initial render.
Use this as an opportunity to operate on the DOM when the component has been updated.

How to detect when ReactJS has finished page manipulation?

Our clients add our JS tag to the head of their page. Our tag needs to know when reactJS has finished before it modifies the page.
I have tried using jQuery's $(document).ready() but this fires before reactJS has finished. I can use $(window).load() but if there are a lot of images on the page, this would be too slow.
What are the options to bind to the completion of React.renderComponent?
Set off the execution of your code via the optional callback that you can pass to React.renderComponent. This callback won't execute until React.renderComponent has finished. If you don't have access to the code that is executing React.renderComponent (which I've just realized is almost definitely the case) then there is no definitive/reliable way to listen for the execution of a function unless of course that function exposes some sort of event that you can listen for, or even guarantees that "x" will be the case once it has finished. Sadly for your case, this doesn't appear to be an option with React.renderComponent.. Let me know if you have any further question.
If you don't have direct control over the React code, you could always see if you can sniff for relevant changes via DOM Mutation events.

Should all Backbone on/off events be replaced with listenTo/stopListening?

As far as I have been able to tell, listenTo and stopListening should replace on and off respectively. Do I understand that correctly? Is there any situation where on/off should be used instead of listenTo/stopListening?
Edit:
As I went to refactor my code, it became obvious that there are some cases for on over listenTo. The documentation is pretty clear that it is for when one object listens to another object:
Tell an object to listen to a particular event on an other object.
Therefore, when a collection or model is listening to an event on itself, we should use on instead of listenTo.
Assuming I have this correct...
The simple rule to follow is this:
Use listenTo when listening for events on another object. Use on when listening to events on self.
Copying an extract from an interesting blog post that I read recently. Hope it helps.
Avoiding common backbone pitfalls: Creating memory leaks by not unbinding events
A common pattern in Backbone.js is creating views that listen on changes in models or collections. This technique is usually intended to allow the view to automatically re-render itself when the underlying data changes. It also means that for large collections we may end up with many views (at least one for every model in the collection) that we may dynamically create or destroy based on changes to the data.
The problem arises when we remove a view (usually by calling its .remove() method), but forgetting to unbind the methods that listen on model changes. In such a case, even though our code may no longer hold a reference to that view, it is never garbage collected since the model still holds such a reference via the event handler.
Take this view for example:
var SomeModelView = Backbone.View.extend({
initialize: function() {
this.model.on('change', this.render, this);
},
render: function() {
// render a template
}
});
When calling the .remove() method, the "change" event handler (our render function) is still bound. So while the DOM element may be removed, the view object itself is never released from memory.
Solving this is easy (especially since Backbone 0.9.x) - all we need to do is to stop using .on() when binding the event handler. instead, we can use the new .listenTo() method, like this:
initialize: function() {
this.listenTo(this.model, 'change', this.render);
}
The biggest difference here being the shift in responsibility from the model to the view. This means that whenever we call .remove(), the view will automatically unbind any event bound to it using the .listenTo() method, essentially fixing this common leak.
For the most part, you understand it correctly. Here is a discussion on the matter from their github repository: https://github.com/documentcloud/backbone/issues/1923#issuecomment-11462852
listenTo and stopListening keep track of state. It will take care of cleanup for you at the cost of a little code overhead. In just about every case I can think of you'd want this behavior for your views, but you wouldn't be at fault for handling on/off calls yourself either; they won't be deprecating on and off any time soon.

Don't call render() if the View itself updated Model via Backbone.Model.set()

In my Backbone.js project I have one Model and several Views. All Views have registered callbacks for 'change:currentTextTitle' on this model:
// 'this' stands for any of the Views here
myModel.on('change:currentTextTitle', this.render, this);
Now a user performs some action, which causes the specific View to change its "current text title" field. This specific view then calls myModel.set("currentTextField", newTextValue) which in turn triggers the 'change:currentTextTitle' event calling all Views (including the one from which set() originated). Then all Views call their render callback functions.
The problem is that the render method is also called on the View from which the set()-Method was originally called, which is completely unnecessary because it is already up-to-date with currentTextTitle.
How would my Views call myModel.set() in a way that the other Views' callbacks get informed, but without triggering/calling the "source View" itself?
One workaround seems to be to pass the source view as part of the options parameter of the set() method (which gets passed along to trigger() and then along the the render() callback):
myModel.set("currentTextField", newTextValue, thisViewSetAttribute)
Then in the render callback one could check if thisViewSetAttribute != this. However, instead of implementing checks in every callback, I think it would make more sense to handle this in the Model itself by only calling the necessary callbacks and ignoring the source View from which the set() method call originated. Is this possible?
I think the 'proper' MCV solution is that your views should not know or care how the model changed, they should simply handle the change and update accordingly. If they are already current, the user shouldn't know the difference.
I definitely would not pass the source view to the model. Instead when the model changes, you could just have the view check if it is current and not re-render. But if the extra render doesn't cause any issues then just let it happen :)
In Backbone, the 'view' is both view and controller. So try to treat the change as 2 separate steps. First, convert user input into changes on the model, then as a separate step (initiated by model change event), handle that change and update the view. If each view does this, no matter how the model changes, everything will stay up-to-date.

Resources