-
Notifications
You must be signed in to change notification settings - Fork 24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added support for the Blaze rendering engine. Also added a more flexible... #26
base: master
Are you sure you want to change the base?
Changes from all commits
96df0a2
2263b2d
52b8f31
c89c656
b73d4f0
c656d8f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,6 +21,7 @@ ngMeteor | |
- [Creating and inserting template views](https://github.com/loneleeandroo/ngMeteor#creating-and-inserting-template-views) | ||
- [Routing](https://github.com/loneleeandroo/ngMeteor#routing) | ||
- [Module Injection](https://github.com/loneleeandroo/ngMeteor#module-injection) | ||
- [Flexible module bootstrapping](https://github.com/loneleeandroo/ngMeteor#flexistrap) | ||
|
||
### New Data-Binding to avoid conflict | ||
To prevent conflicts with Handlebars, ngMeteor has changed the default AngularJS data bindings from <code>{{foo}}</code> to <code>[[foo]]</code>. For example: | ||
|
@@ -165,14 +166,12 @@ You can render this template using handlebars as you would for any other Meteor | |
|
||
{{> foo}} | ||
|
||
Templates will also be added to the $templateCache of the ngMeteor angular module. To invoke the template in AngularJS you could use ng-view and specify the template in the $templateCache when defining your routes using the $routeProvider or your could use the ng-template directive to render your template like this: | ||
Meteor templates can also be rendered in your Angular views using the ng-template directive like this: | ||
|
||
<ANY ng-template="foo"></ANY> | ||
|
||
<!--Add the ng-controller attribute if you want to load a controller at the same time.--> | ||
<ANY ng-template="foo" ng-controller="fooCtrl"></ANY> | ||
|
||
Templates with names starting with an underscore, for example "_foo", will not be put into the $templateCache, so you will not be able to access those templates using ng-template, ng-include or ng-view. | ||
|
||
### Routing | ||
The [ngRoute](http://docs.angularjs.org/api/ngRoute) module developed by the AngularJS team is included in ngMeteor, which will satisfy those with simple routing needs. For example, if you want to call a template called 'foo' and a controller called 'TodoCtrl' when someone lands on your home page you would define your route like this: | ||
|
@@ -212,10 +211,38 @@ Using this method, additional functionality has been provided to ngMeteor in the | |
|
||
Feel free to make ngMeteor module smart packages, and please contact [loneleeandroo](https://github.com/loneleeandroo) if you would like your package to be listed here as well. | ||
|
||
### Flexible module bootstrapping (flexistrap) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added doc for the fleximodel way of bootstraping angular modules into the dom. This allows multiple modules to be bootstrapped on the same page. |
||
|
||
By default, the ngMeteor angular module is bootstrapped into the top level document on all routes. Since modules cannot be nested in angular, should you want to have parts of your DOM controlled by different modules you can do so using a flexible bootstrapping mechanism called flexistrap which is built in to the ngMeteor package. To use this mechanism you call ngMeteor.addFlexistrap() and provide a jQuery-like selector and an angular module name (or array of module names) as parameters. Fleximodel will then instruct angular to bootstrap the modules with the name (or names, if you provided an array of names) into the DOM elements matching the provided selector. | ||
|
||
Optionally, you can provide a path string (or array of path strings) as the 3rd parameter to addFlexistrap. This will apply your modules to the selected DOM elements only when the path(s) identified by the provided strings are being rendered. You can provide a value of '*' to apply your flexistrap configuration to all paths (this is the default behavior if this parameter is not provided). | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed typeo ... 'infront' became 'in front' in the third line. |
||
|
||
Optionally, you can provide a boolean as the 4th parameter. If this value is true, the configuration you provide will replace any existing flexistrap configuration for the given path(s) and selector. If False, it will ONLY update EXISTING configurations. If not supplied or null, it will update existing configurations or add new ones if they don't already exist. | ||
|
||
Example: | ||
|
||
Define two new modules: 'myModule' and 'yourModule': | ||
|
||
myCoolModule = angular.module('myModule',[]); | ||
yourCoolModule = angular.module('yourModule',[]); | ||
|
||
|
||
Remove existing (default) bootstrapping of ngMeteor on the toplevel document: | ||
|
||
ngMeteor.removeFlexistrap(); // removes all existing flexistrap configurations for all paths and selectors. | ||
|
||
Bootstrap div with id 'xxx' with new module named 'myModule' | ||
|
||
ngMeteor.addFlexistrap('#xxx', 'myModule') | ||
|
||
Bootstrap div with id 'yyy' with both the new module 'yourModule' and the original 'ngMeteor' module: | ||
|
||
ngMeteor.addFlexistrap('#yyy', ['yourModule', 'ngMeteor']) | ||
|
||
|
||
<!--- | ||
### Dynamic routing | ||
Routes will automaticlly be created based on a template's name, however, you can override the dynamic routes by manually assigning a route using $routeProvider. The route will load that template and conditionally load a controller with the same name if it exists. You can prevent a template from creating a route by adding a "_" infront of the template name. Based on the URL, this is how you should name your templates: | ||
Routes will automaticlly be created based on a template's name, however, you can override the dynamic routes by manually assigning a route using $routeProvider. The route will load that template and conditionally load a controller with the same name if it exists. You can prevent a template from creating a route by adding a "_" in front of the template name. Based on the URL, this is how you should name your templates: | ||
|
||
| URL | Template / Controller name | $routeParams | | ||
| :-------------------------------- | :----------------------------- | :----------- | | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,321 @@ | ||
(function () { | ||
"use strict"; | ||
/** | ||
* Bindonce - Zero watches binding for AngularJs | ||
* @version v0.3.1 | ||
* @link https://github.com/Pasvaz/bindonce | ||
* @author Pasquale Vazzana <[email protected]> | ||
* @license MIT License, http://www.opensource.org/licenses/MIT | ||
*/ | ||
|
||
var bindonceModule = angular.module('pasvaz.bindonce', []); | ||
|
||
bindonceModule.directive('bindonce', function () | ||
{ | ||
var toBoolean = function (value) | ||
{ | ||
if (value && value.length !== 0) | ||
{ | ||
var v = angular.lowercase("" + value); | ||
value = !(v === 'f' || v === '0' || v === 'false' || v === 'no' || v === 'n' || v === '[]'); | ||
} | ||
else | ||
{ | ||
value = false; | ||
} | ||
return value; | ||
}; | ||
|
||
var msie = parseInt((/msie (\d+)/.exec(angular.lowercase(navigator.userAgent)) || [])[1], 10); | ||
if (isNaN(msie)) | ||
{ | ||
msie = parseInt((/trident\/.*; rv:(\d+)/.exec(angular.lowercase(navigator.userAgent)) || [])[1], 10); | ||
} | ||
|
||
var bindonceDirective = | ||
{ | ||
restrict: "AM", | ||
controller: ['$scope', '$element', '$attrs', '$interpolate', function ($scope, $element, $attrs, $interpolate) | ||
{ | ||
var showHideBinder = function (elm, attr, value) | ||
{ | ||
var show = (attr === 'show') ? '' : 'none'; | ||
var hide = (attr === 'hide') ? '' : 'none'; | ||
elm.css('display', toBoolean(value) ? show : hide); | ||
}; | ||
var classBinder = function (elm, value) | ||
{ | ||
if (angular.isObject(value) && !angular.isArray(value)) | ||
{ | ||
var results = []; | ||
angular.forEach(value, function (value, index) | ||
{ | ||
if (value) results.push(index); | ||
}); | ||
value = results; | ||
} | ||
if (value) | ||
{ | ||
elm.addClass(angular.isArray(value) ? value.join(' ') : value); | ||
} | ||
}; | ||
var transclude = function (transcluder, scope) | ||
{ | ||
transcluder.transclude(scope, function (clone) | ||
{ | ||
var parent = transcluder.element.parent(); | ||
var afterNode = transcluder.element && transcluder.element[transcluder.element.length - 1]; | ||
var parentNode = parent && parent[0] || afterNode && afterNode.parentNode; | ||
var afterNextSibling = (afterNode && afterNode.nextSibling) || null; | ||
angular.forEach(clone, function (node) | ||
{ | ||
parentNode.insertBefore(node, afterNextSibling); | ||
}); | ||
}); | ||
}; | ||
|
||
var ctrl = | ||
{ | ||
watcherRemover: undefined, | ||
binders: [], | ||
group: $attrs.boName, | ||
element: $element, | ||
ran: false, | ||
|
||
addBinder: function (binder) | ||
{ | ||
this.binders.push(binder); | ||
|
||
// In case of late binding (when using the directive bo-name/bo-parent) | ||
// it happens only when you use nested bindonce, if the bo-children | ||
// are not dom children the linking can follow another order | ||
if (this.ran) | ||
{ | ||
this.runBinders(); | ||
} | ||
}, | ||
|
||
setupWatcher: function (bindonceValue) | ||
{ | ||
var that = this; | ||
this.watcherRemover = $scope.$watch(bindonceValue, function (newValue) | ||
{ | ||
if (newValue === undefined) return; | ||
that.removeWatcher(); | ||
that.checkBindonce(newValue); | ||
}, true); | ||
}, | ||
|
||
checkBindonce: function (value) | ||
{ | ||
var that = this, promise = (value.$promise) ? value.$promise.then : value.then; | ||
// since Angular 1.2 promises are no longer | ||
// undefined until they don't get resolved | ||
if (typeof promise === 'function') | ||
{ | ||
promise(function () | ||
{ | ||
that.runBinders(); | ||
}); | ||
} | ||
else | ||
{ | ||
that.runBinders(); | ||
} | ||
}, | ||
|
||
removeWatcher: function () | ||
{ | ||
if (this.watcherRemover !== undefined) | ||
{ | ||
this.watcherRemover(); | ||
this.watcherRemover = undefined; | ||
} | ||
}, | ||
|
||
runBinders: function () | ||
{ | ||
while (this.binders.length > 0) | ||
{ | ||
var binder = this.binders.shift(); | ||
if (this.group && this.group != binder.group) continue; | ||
var value = binder.scope.$eval((binder.interpolate) ? $interpolate(binder.value) : binder.value); | ||
switch (binder.attr) | ||
{ | ||
case 'boIf': | ||
if (toBoolean(value)) | ||
{ | ||
transclude(binder, binder.scope.$new()); | ||
} | ||
break; | ||
case 'boSwitch': | ||
var selectedTranscludes, switchCtrl = binder.controller[0]; | ||
if ((selectedTranscludes = switchCtrl.cases['!' + value] || switchCtrl.cases['?'])) | ||
{ | ||
binder.scope.$eval(binder.attrs.change); | ||
angular.forEach(selectedTranscludes, function (selectedTransclude) | ||
{ | ||
transclude(selectedTransclude, binder.scope.$new()); | ||
}); | ||
} | ||
break; | ||
case 'boSwitchWhen': | ||
var ctrl = binder.controller[0]; | ||
ctrl.cases['!' + binder.attrs.boSwitchWhen] = (ctrl.cases['!' + binder.attrs.boSwitchWhen] || []); | ||
ctrl.cases['!' + binder.attrs.boSwitchWhen].push({ transclude: binder.transclude, element: binder.element }); | ||
break; | ||
case 'boSwitchDefault': | ||
var ctrl = binder.controller[0]; | ||
ctrl.cases['?'] = (ctrl.cases['?'] || []); | ||
ctrl.cases['?'].push({ transclude: binder.transclude, element: binder.element }); | ||
break; | ||
case 'hide': | ||
case 'show': | ||
showHideBinder(binder.element, binder.attr, value); | ||
break; | ||
case 'class': | ||
classBinder(binder.element, value); | ||
break; | ||
case 'text': | ||
binder.element.text(value); | ||
break; | ||
case 'html': | ||
binder.element.html(value); | ||
break; | ||
case 'style': | ||
binder.element.css(value); | ||
break; | ||
case 'src': | ||
binder.element.attr(binder.attr, value); | ||
if (msie) binder.element.prop('src', value); | ||
break; | ||
case 'attr': | ||
angular.forEach(binder.attrs, function (attrValue, attrKey) | ||
{ | ||
var newAttr, newValue; | ||
if (attrKey.match(/^boAttr./) && binder.attrs[attrKey]) | ||
{ | ||
newAttr = attrKey.replace(/^boAttr/, '').replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); | ||
newValue = binder.scope.$eval(binder.attrs[attrKey]); | ||
binder.element.attr(newAttr, newValue); | ||
} | ||
}); | ||
break; | ||
case 'href': | ||
case 'alt': | ||
case 'title': | ||
case 'id': | ||
case 'value': | ||
binder.element.attr(binder.attr, value); | ||
break; | ||
} | ||
} | ||
this.ran = true; | ||
} | ||
}; | ||
|
||
return ctrl; | ||
}], | ||
|
||
link: function (scope, elm, attrs, bindonceController) | ||
{ | ||
var value = attrs.bindonce && scope.$eval(attrs.bindonce); | ||
if (value !== undefined) | ||
{ | ||
bindonceController.checkBindonce(value); | ||
} | ||
else | ||
{ | ||
bindonceController.setupWatcher(attrs.bindonce); | ||
elm.bind("$destroy", bindonceController.removeWatcher); | ||
} | ||
} | ||
}; | ||
|
||
return bindonceDirective; | ||
}); | ||
|
||
angular.forEach( | ||
[ | ||
{ directiveName: 'boShow', attribute: 'show' }, | ||
{ directiveName: 'boHide', attribute: 'hide' }, | ||
{ directiveName: 'boClass', attribute: 'class' }, | ||
{ directiveName: 'boText', attribute: 'text' }, | ||
{ directiveName: 'boBind', attribute: 'text' }, | ||
{ directiveName: 'boHtml', attribute: 'html' }, | ||
{ directiveName: 'boSrcI', attribute: 'src', interpolate: true }, | ||
{ directiveName: 'boSrc', attribute: 'src' }, | ||
{ directiveName: 'boHrefI', attribute: 'href', interpolate: true }, | ||
{ directiveName: 'boHref', attribute: 'href' }, | ||
{ directiveName: 'boAlt', attribute: 'alt' }, | ||
{ directiveName: 'boTitle', attribute: 'title' }, | ||
{ directiveName: 'boId', attribute: 'id' }, | ||
{ directiveName: 'boStyle', attribute: 'style' }, | ||
{ directiveName: 'boValue', attribute: 'value' }, | ||
{ directiveName: 'boAttr', attribute: 'attr' }, | ||
|
||
{ directiveName: 'boIf', transclude: 'element', terminal: true, priority: 1000 }, | ||
{ directiveName: 'boSwitch', require: 'boSwitch', controller: function () { this.cases = {}; } }, | ||
{ directiveName: 'boSwitchWhen', transclude: 'element', priority: 800, require: '^boSwitch' }, | ||
{ directiveName: 'boSwitchDefault', transclude: 'element', priority: 800, require: '^boSwitch' } | ||
], | ||
function (boDirective) | ||
{ | ||
var childPriority = 200; | ||
return bindonceModule.directive(boDirective.directiveName, function () | ||
{ | ||
var bindonceDirective = | ||
{ | ||
priority: boDirective.priority || childPriority, | ||
transclude: boDirective.transclude || false, | ||
terminal: boDirective.terminal || false, | ||
require: ['^bindonce'].concat(boDirective.require || []), | ||
controller: boDirective.controller, | ||
compile: function (tElement, tAttrs, transclude) | ||
{ | ||
return function (scope, elm, attrs, controllers) | ||
{ | ||
var bindonceController = controllers[0]; | ||
var name = attrs.boParent; | ||
if (name && bindonceController.group !== name) | ||
{ | ||
var element = bindonceController.element.parent(); | ||
bindonceController = undefined; | ||
var parentValue; | ||
|
||
while (element[0].nodeType !== 9 && element.length) | ||
{ | ||
if ((parentValue = element.data('$bindonceController')) | ||
&& parentValue.group === name) | ||
{ | ||
bindonceController = parentValue; | ||
break; | ||
} | ||
element = element.parent(); | ||
} | ||
if (!bindonceController) | ||
{ | ||
throw new Error("No bindonce controller: " + name); | ||
} | ||
} | ||
|
||
bindonceController.addBinder( | ||
{ | ||
element: elm, | ||
attr: boDirective.attribute || boDirective.directiveName, | ||
attrs: attrs, | ||
value: attrs[boDirective.directiveName], | ||
interpolate: boDirective.interpolate, | ||
group: name, | ||
transclude: transclude, | ||
controller: controllers.slice(1), | ||
scope: scope | ||
}); | ||
}; | ||
} | ||
}; | ||
|
||
return bindonceDirective; | ||
}); | ||
}) | ||
})(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No longer need this since users only will render templates they identify with the ng-template directive. Templates are no longer pre-rendered and cached. This provides for rendering templates via meteor spacebars first and then rendering that output using angular's compile/link functionality.
Pre-rendering with render() and then caching the result was not very flexible in any case if the template was not 'static'. If you needed to generate the template contents, for example, using meteor's rendering engine, and then feed that result into the angular compile/link processor, then using the pre-rendered HTML from the templateCache would not have been helpful.