I have a view that defines several events like this:
events: {
'click .js-icon-remove': 'removeFilter',
'change .select-control': 'updateFilters',
'click #btn_search': 'requestSearch',
'click #btn_add_search': 'requestSaveSearch'
}
The three click events are on buttons or links while the change event is on a dropdown. When the view first renders, all events work fine. However, when I go to a different page and then come back to this view, the dropdown event is lost. If I reload the browser, things work fine again. They even work fine multiple times, not just the first time. It's just when I re-render the view that the event binding is lost.
I have tried changing the event from a change to a click, or from the class to the element (select rather than .select-control). All things will work the first time the view is rendered and that's it.
This is getting into how event binding works in Backbone and the DOM.
When a view renders, and it's $el is added to the DOM, backbone binds the events automatically. However, once you remove the view's $el from the dom, those events will then need to be re-bound when the $el is added back into the dom. This is what your delegateEvents() method is doing.
A best practice when working in backbone is to make views disposable, eg don't keep them around after they are removed from the DOM. When you navigate away from the view, and remove it from the dom, then navigate back, you should re-instantiate the view, render it, and add it to the dom again. You can keep you models and collections around, so you don't have refetch data, but views work better when they are either left in the dom or are recreated each time they are added/ removed.
I highly recommend Marionette.js, and specifically it's region class. It saves you a lot of boilerplate code when it comes to view handling.
Related
The docs say the scheduler change event is "Fired when the user selects a cell or event in the scheduler."
Great. The problem is that event is also fired on browser window resize. My app now thinks when the browser window is resized that the user clicked in a cell.
How do I distinguish between these two conditions?
Thought I had the answer here but no dice. No idea what causes the scheduler to fire the change event on resizing. I am showing it in a Marionette JS view. Maybe that has something to do with it? Not sure. It shouldn't.
I know it's all wired correctly because the calendar works properly when clicked - correct views are displayed based on whether a calendar slot has an event or is blank. But the change event fires when on window resize causing my app to fire view changes. A break point in the scheduler change event confirms it is firing on window resize.
Below I tried ditching the change event in favor onClick based on some prescriptions found in various posts. Ugly code but it worked in Chrome. Unfortunately doesn't work in other browsers. Why is there simply no "onClick" event? I just looked at a competing scheduler product and it has one.
Chrome only solution: "e" below is the onClick event object:
//Get the element ID - the DOM ID of the thing that was clicked. You'll use it to
//dip into the scheduler data model to get the event specific data you're after.
var uid = e.toElement.parentElement.parentElement.attributes[2].nodeValue;
//Get a reference to the scheduler data model
var scheduler = $("#scheduler").data('kendoScheduler');
//Use the uid to find the event associated with the click action
var event = scheduler.occurrenceByUid(uid);
Here are the posts that helped formulate that Chrome solution:
Call Scheduler events from EventTemplate
Kendo UI scheduler doesn't allow for reliable cell click event handling
Problem with this obviously is that it doesn't work in other browsers. That uid value (e.toElement.parentElement.parentElement.attributes2.nodeValue) is in different places in different browsers. So this solution is a no go.
I have no idea how a product like this doesn't have an onClick event. Likely have to use a different calendar. Very frustrating.
The issue at hand is I am rendering this calendar in a Marionette js item view. when a scheduler cell item is clicked I take the user to a different view. But the event that is used by the scheduler to handle clicks, "change", also fires on browser window resize. So on a browser window resize my app takes the user to a different view. Not good. No work around.
Is there a way in Ractive to destroy the instance (unrender event listeners, etc) but not remove the DOM changes that it made?
I'm building a one page app and the goal is that each view is its own instance of ractive. However, in some cases there is persistent data and I want to avoid a 'pop in' effect. By preserving what's in the DOM, the next ractive instance can load on page change and do its diff, removing anything that changed but preserve the things that didn't.
Thanks in advance.
I have set up a partial view to render in an index page. The partial view gets posted using Ajax to the server when a user clicks a sort button. This is so the entire page won't refresh just the partial view table.
The problem is after the first sort, the JavaScript in the index page is no longer effective. I worked around this by putting the js in the partial view itself to persist the events, but this produces js errors to saying 'continue' or 'ignore'.
It is because your newly injected elements ( via ajax) is not aware of the events bounded. So those event bindings are not availabel to them.
You should change your event bindings to use on so that it handles the current elements and future elements (added to DOM dynamically via ajax or so)
for example, if you want to handle the click event for elements with css class someCssClassSelector,
Change
$(".someCssClassSelector").click(function(){
//do something
});
to
$(document).on("click",".someCssClassSelector",function(){
//do something
});
First here's a plunk: http://plnkr.co/edit/2gD0AB. This plunk doesn't seem to work correctly because $scope.$on("$viewContentLoaded") won't fire, but on my machine it works just fine and I hope you get the idea.
What I'm trying to do there is simply move field objects from $scope.fields to $scope.groupFields = [], $scope.listFields = [], $scope.dataFields = [] when dragging them to the appropriate droppable areas. I've manager to do this using jQuery UI and jQuery UI touch punch (just to be mobile safe).
If you drag an element from the fields box to the one of the empty boxes you'll notice nothing happens on the screen besides the field elements hanging around where you left them. But if do a console.log($scope.fields) in the drop event listener you'll notice that all of the field objects are correctly set inside each of the field boxes.
If you click the Add element button then you'll simply trigger console.log($scope.groupFields); and suddenly all the elements appear correctly in their corresponding boxes like intended in the first place.
After pulling my brains out and after searching the internet for some time I found out that $scope.$apply(), called in the drop event after the moveField function, actually fixes my issue.
And I don't understand why. Shouldn't there be a digest already running and updating the view based on what I'm doing in the controller?
Thanks!
Just because the code that handles these jQuery drop events is inside the controller, it doesn't mean it will run under the angularjs scope/word. For those changes to make effect, you will need to force the angularjs app to do a dirty checking on its models. That can be accomplished by calling $apply() or $digest() methods. You should always call one of these methods after handling an jQuery event that changes $scope properties or after a timeout (or interval).
I am creating a jQuery Mobile web app, which loads some pages.
For example, a.html is my main page. It may call b1.html,b2.html,...,b100.html (User may click on one of buttons). (The pages are loading with ajax navigation feature of jQuery Mobile)
And there is some events in each b[i].html page, and ids and many things are same in every b[i].html page. But the point is, at any time, just one of them may be in DOM. So there will be no id conflicts or such.
The problem
The problem is the conflict of the events. When user come back to a.html from b[i].html, the HTML will be removed, but events will remain. This will cause many problems if I first go to b[i].html, and then come back to a.html and then go to b[j].html. I mean, b[j].html will not work correctly... :(
What I have tried
I have putted this in a.html, to remove all events:
$("#mainpage").off("pagebeforeshow").on("pagebeforeshow",function() {
$("*").not("#mainpage").off();
//Other initialization codes...
});
But, problem not solved...
(mainpage is the id of data-role="page" of a.html)
Example
For example, I have this in each b[i].html:
$(window).resize(function () {
alert("Resized");
});
At the beginning (in a.html), If I resize the window, there will be no alerts, but after visiting b[i].html and then coming back to a.html, I'll see alerts if I resize the window, even with that line of code (What I have tried part.)...
So, How to remove those event handlers when users come back to a.html from b[i].html?
If you are using jQuery Mobile, more than one of said pages may exist in the dom at the same time, resulting in non-unique id conflicts.
I would ditch putting js on the individual pages and have it done from the primary page, or through a script loading system such as require.js. Then do all of the events through delegation from the document. Obviously that won't work with window.resize(), but it doesn't need to be delegated anyway.
"Can you please explain more?"
Basically, if you are including scripts on the child pages, you will need to have both setup and teardown for every page. setup adds the events, and teardown removes them. If you instead used a single global script that adds ALL of the events using event delegation from the document, all of the pages should work. Obviously that global script could get pretty big on a complex site, so you could instead use require.js to load in js that does the same thing as needed, preventing it from loading the same dependency more than once.
As far as removing all events, I've never tried this, but can you use $("*").off()? According to the docs it should work. I'm not sure how it will affect jQuery mobile. To remove events on the document and/or window, you will have to do it manually because $("*") will not select them.
$(document).on("vmousemove","#link",func) is how you delegate an event from the document.