How do I call a backbone view event only once?
Asked Answered
R

4

8

I have a backbone view that looks like this:

var myView = Backbone.view.extend({
    events: {'click .myClass': 'myFunction'},
    initialze: //initialize function,
    render: //render function,
    myFunction: function (e) {
        //do something
    }
});

I want to make myFunction work only one time, then stop being called. I believe I could use the backbone once() method to achieve this but can't figure it out. Is this the best way and how do I structure it? Thanks!

Reportage answered 19/2, 2014 at 3:41 Comment(0)
M
13

Simply replace your current definition:

myFunction: function(){....}

by

myFunction: _.once(function(){....})

myFunction may be called several times, but it will be a noop after the first call.

Montespan answered 19/2, 2014 at 4:59 Comment(6)
This worked perfectly and seemed the simplest solution, thanks!Reportage
What happens here if you have two instances of the view? Either at the same time or at different times while your application is running.Viridis
@muistooshort here it would be called only once per instance . If you wanted to call it only once overall, you'd use a function defined at the same level than myView for instance.Montespan
@muistooshort my apologies, the object passed into View.extend is used as the prototype, and is common to all instances. With my code above it'll be called once overall. To call it once per instance, you could make use of the initialize method as shown here jsfiddle.net/u6oshjwn/1Montespan
But in the case of an event handler, the handlers will be bound before initialize gets called (jsfiddle.net/ambiguous/zqhaj97w) so f won't be there or it won't be the right f, you'd need to throw a manual delegateEvents call into the mix (jsfiddle.net/ambiguous/jdhejxgz) to get everything hooked up properly. Sorry to belabour this point but there are some subtle traps.Viridis
No worries ! Indeed, event handlers make it tricky with _.once when you want the function called once per instance. In that case your solution is an elegant one. I played a bit and worked out yet other solutions for that case jsfiddle.net/zqhaj97w/1 (I quite like the one on f, not so much the one on g).Montespan
V
6

View events are bound using delegateEvents and that uses the delegation form of jQuery's on. That means that the event to handler mapping is dynamic and can be manipulated by manipulating the DOM. For example, you could do this:

Backbone.View.extend({
    events: {
        'click .myClass.onlyOnce': 'doThings'
    },
    doThings: function(e) {
        $(e.currentTarget).removeClass('onlyOnce');
        //...
    },
    //...
});

and make sure that the .myClass element also has an onlyOnce class. Then, the first time you click on it, it will match .myClass.onlyOnce and the click will get routed to doThings; doThings then proceeds to remove the onlyOnce class and do whatever needs to be done. Subsequent clicks on .myClass will be clicks on something that does not match .myClass.onlyOnce so doThings won't be called again.

This approach has the nice advantage of self documenting its behavior in events.

Demo: http://jsfiddle.net/ambiguous/7KJgT/

Viridis answered 19/2, 2014 at 4:58 Comment(0)
B
1

You could add an attribute to the element the first time, and then check if the element has the attribute:

var myView = Backbone.view.extend({
    events: {'click .myClass': 'myFunction'},
    initialze: //initialize function,
    render: //render function,
    myFunction: function (e) {
        if(e.target.getAttribute("data-fired")) return;
        e.target.setAttribute("data-fired", true);
        // Do your stuff
    }
});
Bureaucratic answered 19/2, 2014 at 4:0 Comment(4)
Aristizabal: data attribute has a partial support for IE, so we can use class in order to validate condition.Curt
@Mohit: Can you be more specific about this "partial support" in IE?Viridis
@muistooshort: caniuse.com/dataset is the reason of what i am saying. You may correct me, if i am wrong.Curt
@Mohit: That link does say that "All browsers can already use data-* attributes and access them using getAttribute" so this use is fine. caniuse also neglects to specify what specifically "partial support" means.Viridis
C
0

You could add a class to the element to validate your condition.

var myView = Backbone.view.extend({
    events: {
        'click .myClass': 'myFunction'
    },
    initialze: //initialize function,
    render: //render function,
    myFunction: function (e) {
        if (e.target.className.search(/triggered/) !== -1) return; // event already triggered once
        e.target.className += "triggered";
        //do something when its triggered first time
    }
});
Curt answered 19/2, 2014 at 4:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.