Created
July 21, 2013 04:34
-
-
Save kayhadrin/6047490 to your computer and use it in GitHub Desktop.
A CodePen by David. Custom Rivets binder for jQuery plugins - Example of how to write a custom rivet binder for jQuery plugins that report changes with custom events.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- JS Includes --> | |
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.4/underscore-min.js" type="text/javascript"></script> | |
<script src="//cdnjs.cloudflare.com/ajax/libs/backbone.js/1.0.0/backbone.js" type="text/javascript"></script> | |
<script src="https://rawgithub.com/mikeric/rivets/master/dist/rivets.js" type="text/javascript"></script> | |
<script src="https://rawgithub.com/azproduction/rivets-backbone-adapter/master/rivets-backbone.js" type="text/javascript"></script> | |
<p style="color: red;"> | |
Open your dev tools and watch the console log for output. You should notice that when setting the value manually using the Backbone set(), the "routine" is fired off twice. | |
</p> | |
<div> | |
<h2>Normal Field</h2> | |
<p>Manually enter a numeric value</p> | |
<input type="text" data-bind-value="model.counter" /> | |
</div> | |
<div> | |
<h2>jQuery Plugin</h2> | |
<p>Click the button below to increment</p> | |
<input type="text" data-bind-incrementer="model.counter" id="incrementer" /> | |
</div> | |
<div> | |
<h2>Backbone</h2> | |
<p>Clicking the button below fires of the "rountine" binder twice. This is because the set fires off the routine, which then ultimately triggers an "update" event, which fires off the set method again.</p> | |
<button onclick="Tracker.increment();">Increase</button> | |
</div> | |
<div> | |
<h3> | |
Bound Value: | |
<span data-bind-text="model.counter"></span> | |
</h3> | |
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* backbone */ | |
var Tracker = new Backbone.Model({counter:0}); | |
Tracker.increment = function (){ | |
Tracker.set("counter", parseInt(Tracker.get("counter"), 10) +1); | |
} | |
Tracker.on("change:counter", function (){ | |
console.log("log: " + this.get("counter")); | |
}); | |
/* rivets */ | |
/* global rivets configuration */ | |
rivets.configure({ | |
prefix: "bind" | |
}); | |
var rountineCounter = 0; | |
/* start custom binders */ | |
rivets.binders.incrementer = { | |
publishes: true | |
, bind: function(el){ | |
var rivetView = this, | |
binder = rivetView.binder; | |
// David: create a custom bind listener to check if the routine() function allowed its execution | |
var bindListener = function() { | |
var curEl = this, | |
isDisabled = $(curEl).data('rivets.binders.incrementer.isDisabled'), | |
ret; | |
//DEBUG | |
console.log('bindListener: update event received. isDisabled = ', isDisabled); | |
if (!isDisabled) { | |
ret = rivetView.publish.apply(curEl, arguments); | |
} | |
return ret; | |
}; | |
// David: save it on the current DOM element's data | |
$(el).data('rivets.binders.incrementer.bindListener', bindListener); | |
return rivets._.Util.bindEvent(el, "update", bindListener); | |
} | |
, unbind: function(el){ | |
var $el = $(el), | |
bindListener = $el.data('rivets.binders.incrementer.bindListener'); // David: retrieve the bindListener from the DOM Element | |
$el.data('rivets.binders.incrementer.bindListener', null); // David: remove reference to the bindListener from the DOM element | |
return rivets._.Util.unbindEvent(el, "update", bindListener); | |
} | |
, routine: function(el, value){ | |
var $el = $(el); | |
console.log('rountine id:' + rountineCounter++); | |
$el.data('rivets.binders.incrementer.isDisabled', true) // David: disable this binder's listener handler, because we know that the jQuery plugin emits 'update' events everytime | |
.incrementer("val", value) // force to a string and make sure to run the callback, but skip firing off the update trigger | |
.data('rivets.binders.incrementer.isDisabled', false); // David: re-enable this binder's listener handler | |
} | |
}; | |
/* jQuery plugin */ | |
;(function($){ | |
$.fn.incrementer = function(options) { | |
var method = typeof arguments[0] == "string" && arguments[0]; | |
var args = method && Array.prototype.slice.call(arguments, 1) || arguments; | |
// if a method is supplied, execute it for non-empty results | |
if( method && this.length ){ | |
// get a reference to the first incrementer found | |
var self = $.data(this[0], "incrementer"); | |
// if request a copy of the object, return it | |
if( method.toLowerCase() == "object" ) return self; | |
// if method is defined, run it and return either it's results or the chain | |
else if( self[method] ){ | |
// define a result variable to return to the jQuery chain | |
var result; | |
this.each(function (i){ | |
// apply the method to the current element | |
var r = $.data(this, "incrementer")[method].apply(self, args); | |
// if first iteration we need to check if we're done processing or need to add it to the jquery chain | |
if( i == 0 && r ){ | |
// if this is a jQuery item, we need to store them in a collection | |
if( !!r.jquery ){ | |
result = $([]).add(r); | |
// otherwise, just store the result and stop executing | |
} else { | |
result = r; | |
// since we're a non-jQuery item, just cancel processing further items | |
return false; | |
} | |
// keep adding jQuery objects to the results | |
} else if( !!r && !!r.jquery ){ | |
result = result.add(r); | |
} | |
}); | |
// return either the results (which could be a jQuery object) or the original chain | |
return result || this; | |
// everything else, return the chain | |
} else return this; | |
// initializing request | |
} else { | |
// create a new incrementer for each object found | |
return this.each(function (){ | |
new Incrementer(this, options); | |
}); | |
}; | |
}; | |
Incrementer = function (el, options){ | |
options = $.extend({}, Incrementer.defaults, options); | |
var self = this, $el = $(el), counter = 0; | |
// get/set the value of the field | |
this.val = function (value){ | |
// update the selected value | |
if( arguments.length > 0 ){ | |
setValue(value); | |
return $el; | |
} | |
else return $el.val(); | |
}; | |
// store a reference to this link select | |
$.data($el[0], "incrementer", this); | |
function setValue(value){ | |
counter = parseInt(value, 10); | |
// update the hidden value | |
updateValue() | |
} | |
function updateValue(){ | |
// update the hidden value | |
$el.val(counter); | |
$button.text(counter); | |
// announce the update (for Backbone/Rivets) | |
$el.triggerHandler("update", [counter, this]); | |
} | |
function increment(){ | |
counter++; | |
updateValue(); | |
} | |
// hide form element | |
$el.css({ | |
visibility: "hidden" | |
, position: "absolute" | |
, left: "-999em" | |
}); | |
var $button = $("<button>") | |
.on("click.incrementer", function (){ | |
increment() | |
}) | |
.insertAfter($el) | |
; | |
// update everything | |
updateValue(); | |
} | |
Incrementer.defaults = {}; | |
})(jQuery); | |
var rivetsViews; | |
/* DOM ready */ | |
$(function (){ | |
$("#incrementer").incrementer(); | |
rivetsViews = rivets.bind($("body"), {model: Tracker}); | |
console.log('rivetsViews = ', rivetsViews); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Related to this rivets conversation: mikeric/rivets#193 (comment)