"todo lists were never this easy before!"
"omg data-binding!"
"made by Google you say?"
"desk beers?"
app controllers someCtrl.js # 100 more... directives someDirective.js # 100 more... templates
app features home-page index.html index.js account user-widget index.html index.js components breadcrumb index.js index.html common resource index.js
:(
"You can package code as reusable modules."
"You can package code as reusable modules."
"...as long as all your code is Angular"
exports.controllers = { "SearchCtrl": SearchCtrl, }; exports.directives = {}; exports.routes = { "/": { template: require("./search.html") }, } function SearchCtrl() { this.message = "hello" }
npm install --save browserify watchify stringify
require("angular"); require("angular-route"); var _ = require("lodash"); angular.module('events', ["ngRoute"]) .config(function($controllerProvider, $compileProvider, $routeProvider) { exports.controller = function(controller, name) { $controllerProvider.register(name, controller); } // same for filter, routes, etc // can use these for lazy-load var deps = [ require("../create-event"), require("../search-events"), ]; deps.forEach(function(module) { _.each(module.controllers || {}, exports.controller) // same for directives, filters etc }); });
watchify -r stringify -o dist/app.js features/core/index.js
I give people one piece of advice: make smaller things
Sandi Metz (we can learn things from Rubyists)
Better to have 100 functions for one data structure than 10 functions for 10 data structures."
Explicit is better than implicit.
Zen of Python, PEP 20, Tim Peters
## Scope vars are implicit - who owns them?
<div ng-controller="GraphCtrl" <table > <tr ng-repeat="row in data"> </tr> </table> <div class='line-graph-header'> <h3>{{ lineGraphTitle }}</h3> </line-graph> </div>
<data-table controller-as="dataTable" width="800 + ctrl.padding" > </data-graph> <line-graph x-scale="ctrl.scale" y-scale="ctrl.yScale" data="dataCtrl.series" width="800 + ctrl.padding" on-inspect="dataTable.data = $series" > </line-graph>
module.exports = function() { return { restrict: "E", controllerAs: "ctrl", bindToController: true, controller: LineGraphCtrl, link: lineGraph, scope: { xScale: "=", // ... onInspect: "&" } } }
function lineGraph(scope, el, attrs, ctrl) { // ... points.on("click", function(d, i) { ctrl.onInspect({ $series: d, }); }) }
<my-widget ng-model="something">
<form name=timeForm> <div class='extended-clock'> <svg width=1000 height=1000 > <g clock-input ng-model=item.time name=time></g> <text x=500 y=500> {{ item.time | date:"shortTime" }} </text> <text x=500 y=550 ng-click="toggle()"> {{ ticking ? "Stop" : "Start" }} </text> </svg> </div> </form>
function clock() { return { // we require an instance of ng-model to work require: "ngModel", link: function(scope, el, attrs, ngModel) { // initialize the non-angular widget, and // pass in our model updating fn var clock = donutClock({ onInput: view2model, el: el[0], }); // when angular detects a change to the model, // we update our widget ngModel.$render = model2view; function model2view(time) { clock.set(ngModel.$viewValue); } function view2model(fromView) { ngModel.$setViewValue(fromView); } } } }