Skip to content

Instantly share code, notes, and snippets.

@cameron
Last active August 29, 2015 14:16
Show Gist options
  • Save cameron/9eeae7696aae303af29d to your computer and use it in GitHub Desktop.
Save cameron/9eeae7696aae303af29d to your computer and use it in GitHub Desktop.
angular.module().view()

ng.module.view

module.view() is a deliberately half-baked attempt at implementing a web components pattern in angular.

It began as a convenience wrapper on module.directive() to avoid specifying the templateUrl in a context where the templateUrl is derivable from the name of the view.

It also allows views to be included via expression (see example below), which is useful for routing, modals, and other dynamic view inclusion.

Defining a widget with a controller

// in src/views/exampleView/exampleView.js
angular.module('exampleModule')
.view('exampleView', function(){
 // $scopey stuff
});
<!-- in src/views/exampleView/exampleView.html -->
<you-know/>
<markup/>

After which, there are two ways to include it:

<body>
  <em-example-view></em-example-view>   <!-- `em-` is configurable -->
  
  <!-- 
  Results in the same when $scope.expr evaluates to 'exampleView' 
  -->
  <em-view view-name="expr"></em-view> 
</body>

It's basically just .directive()

angular.module().view('viewName', { /* directive definition */ })
angular.module('pixinote')
/* Notes/Deliberations
*
* ? wrap view contents in an element with a class of the same name, so that we can...
* ? scope all view styles to the view class? (gulp would modify the view scss file directly to wrap all rules)
*
* */
.directive('pxView', ['$compile', function($compile){
/* <div px-view="myView"></div> where scope.myView = 'login'
*
* results in
*
* <div px-view="myView">
* <px-login></px-login>
* </div>
*
* Currently, does not support transference of attributes.
*/
var module = angular.module('pixinote');
var insertView = function(scope, el, viewName){
viewName = viewName.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
var html = angular.element('<px-' + viewName + '></' + viewName + '>');
el.append(html);
$compile(html)(scope);
}
return {
restrict: 'A',
link: function(scope, el, attrs){
window.el = window.el || el;
if(!attrs.pxView[0].match('["\']')){
scope.$watch(attrs.pxView, function(viewName){
if(viewName) insertView(scope, el, viewName);
});
} else {
insertView(scope, el, attrs.pxView);
}
}
}
}])
/*
* Extends the pixinote module with a function `view` that makes directive
* definition a little sweeter (using a naming convention to save
* naming templates explicitly) and a little more powerful.
*
* Compose views in html like this:
*
* <px-my-view></px-my-view>
* <div px-view="'myView'"></div>
* <div px-view="myViewExpr"></div>
*
* The last pattern is used for creating modal dialogs. See views/modals/modals.js
*
* The expectation is that all UI components outside of a minimal index.html
* are defined with `view`.
*
* NB: I find myself preferring '&' in scope bindings and $eval in link functions
* to avoid incurring {{ expr }} notation inside directive attributes. This
* necessitates attr="'strings'", which is a bit odd, and maybe worse for being
* subtler and easy to forget. Keep an eye out.
*
*/
.view = function(name, viewDef){
var module = angular.module('pixinote');
module.views = module.views || {};
viewDef = viewDef || {};
// Handle the shorthand signature `function view(name, controller)`.
var controller;
if(typeof viewDef == 'function' || (viewDef.slice !== undefined)){
controller = viewDef;
viewDef = {controller: controller};
}
// If the template isn't specified, assume it's named after the view.
if(!viewDef.template && !viewDef.templateUrl){
viewDef.templateUrl = 'views/' + name + '/' + name + '.html';
}
// Default to creating a child scope.
viewDef.scope = viewDef.scope === undefined ? true : viewDef.scope;
viewDef.priority = viewDef.priority === undefined ? viewDef.priority : 1;
/* TODO: resolve the double wrapper syndrome that arises from the view tag
including an html template that itself is wrapper in a div. e.g.,
viewController.html.
- seems highly desirable to define directives on the view from inside
it's own template (again, see viewController).
- counter to that is the simplicity of defining the view's contents
alone inside its template, and allowing the directive to be its only
wrapper in markup.
- one resolution would be to use replace on the directive, but that would
force the inclusion of a wrapper inside the template, something
that would quickly feel like boilerplate in 90% of views (that don't need
to define directives or classes on themselves)
- possible and slightly cray compromise would be to use replace only if
the template doesn't include its own wrapper. would require inspecting
the template at view definition time.
- alternative: automagically wrap the template contents in a tag by the
view's name and always use replace.
RELATED: autoscope view styles to their owner tags
*/
// Catalog the view by name and create a directive prefixed with px.
module.views[name] = viewDef;
module.directive('px' + name[0].toUpperCase() + name.slice(1),
function(){
return viewDef;
});
return module;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment