Skip to content

Instantly share code, notes, and snippets.

@tomconnors
Created September 16, 2013 17:15
Show Gist options
  • Save tomconnors/6583595 to your computer and use it in GitHub Desktop.
Save tomconnors/6583595 to your computer and use it in GitHub Desktop.
an alternate api for the Meteor template lifecycle
//==============================================================================
// template utilities
// making Meteor's template api a bit less, well, awful
//==============================================================================
define([
"lib/ext"
],
function(ext){
"use strict";
var
/*
* internal mappings of templateNames to callbacks
*/
_createdFns = {},
_destroyedFns = {},
_renderedFns = {},
_firstRenderFns = {},
/*
* add a callback `fn` for `eventName` (created | rendered | destroyed)
* for the template Template[templateName]
*/
addCallback = function(eventName, callbacks, templateName, fn){
var callbacksFortemplateName = callbacks[templateName];
if(callbacksFortemplateName){
callbacks[templateName].push(fn);
} else {
callbacksFortemplateName = callbacks[templateName] = [fn];
var oldCallback = Template[templateName][eventName];
Template[templateName][eventName] = function(){
var instance = this;
_.each(callbacks[templateName], function(callback){
callback(instance);
});
_.isFunction(oldCallback) && oldCallback.call(instance);
};
}
},
/*
* remove `fn` as a callback for some event for Template[templateName].
* if fn isn't provided, remove all callbacks for that template + event.
*/
removeCallback = function(callbacks, templateName, fn){
var callbacksFortemplateName = callbacks[templateName];
if(callbacksFortemplateName){
if(fn){
callbacksFortemplateName.splice(_.indexOf(fn), 1);
} else {
callbacks[templateName] = [];
}
}
},
addCreatedCallback = _.partial(addCallback, "created", _createdFns),
addDestroyedCallback = _.partial(addCallback, "destroyed", _destroyedFns),
addRenderedCallback = _.partial(addCallback, "rendered", _renderedFns),
/*
* only call `fn` when the first `rendered` callback fires, per instance of Template[templateName]
*/
addFirstRenderCallback = function(templateName, fn){
var
callbacks = _firstRenderFns[templateName],
doCallback = false;
if(callbacks){
_firstRenderFns[templateName].push(fn);
} else {
callbacks = _firstRenderFns[templateName] = [fn];
addCreatedCallback(templateName, function(instance){ doCallback = true; });
addRenderedCallback(templateName, function(instance){
if(doCallback){
doCallback = false;
_.each(_firstRenderFns[templateName], ext.invoke(instance));
}
});
}
},
removeCreatedCallback = _.partial(removeCallback, _createdFns),
removeDestroyedCallback = _.partial(removeCallback, _destroyedFns),
removeRenderedCallback = _.partial(removeCallback, _renderedFns),
removeFirstRenderCallback = _.partial(removeCallback, _firstRenderFns),
/*
* add event listeners that are dependent upon the existence of an
* instance of Template[templateName], but which are not
* inside of that instance's DOM.
* @example
* addExternalEventListeners('popup', { "click .elsewhere": function(e, template){ closeThePopup(); } });
*/
addExternalEventListeners = function(templateName, eventMap){
var handlers = {};
addCreatedCallback(templateName, function(instance){
_.each(eventMap, function(handler, name){
var
splitKey = name.split(" "),
event = _.first(splitKey),
selector = splitKey.length === 1 ? null : _.rest(splitKey).join(" "),
$el = selector ? $(selector) : $(document.body);
handlers[name] = function(e){
handler(e, instance);
};
$el.on(event, handlers[name]);
});
});
addDestroyedCallback(templateName, function(instance){
_.each(eventMap, function(handler, name){
var
splitKey = name.split(" "),
event = _.first(splitKey),
selector = splitKey.length === 1 ? null : _.rest(splitKey).join(" "),
$el = selector ? $(selector) : $(document.body);
$el.off(event, handlers[name]);
delete handlers[name];
});
});
};
return {
addCreatedCallback: addCreatedCallback,
addDestroyedCallback: addDestroyedCallback,
addRenderedCallback: addRenderedCallback,
addFirstRenderCallback: addFirstRenderCallback,
removeCreatedCallback: removeCreatedCallback,
removeDestroyedCallback: removeDestroyedCallback,
removeRenderedCallback: removeRenderedCallback,
removeFirstRenderCallback: removeFirstRenderCallback,
addExternalEventListeners: addExternalEventListeners
};
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment