limurezzz@gmail.com
templates/
_login.html
_feed.html
app/
app.js
controllers/
LoginController.js
FeedController.js
directives/
FeedEntryDirective.js
services/
LoginService.js
FeedService.js
filters/
CapatalizeFilter.js
app/
app.js
Feed/
_feed.html
FeedController.js
FeedEntryDirective.js
FeedService.js
Login/
_login.html
LoginController.js
LoginService.js
Shared/
CapatalizeFilter.js
Having said, I'd rather see developers build kick-ass apps that are well-designed and follow separation of concerns, than see them waste time arguing about MV* nonsense. And for this reason, I hereby declare AngularJS to be MVW framework - Model-View-Whatever. Where Whatever stands for "whatever works for you".
Lead of the AngularJS
var underscore = angular.module('underscore', []);
underscore.factory('_', function() {
return window._; //Underscore must already be loaded on the page
});
var app = angular.module('app', ['underscore']);
app.controller('MainCtrl', ['$scope', '_', function($scope, _) {
init = function() {
_.keys($scope);
}
init();
}]);
<div ng-controller="navCtrl">
<span>{{user}}</span>
<div ng-controller="loginCtrl">
<span>{{user}}</span>
<input ng-model="user"></input>
</div>
</div>
<div ng-controller="navCtrl">
<span>{{user.name}}</span>
<div ng-controller="loginCtrl">
<span>{{user.name}}</span>
<input ng-model="user.name"></input>
</div>
</div>
app.controller('navCtrl', function($scope) {
$scope.user = {name: ''};
});
var app = angular.module('app',[]);
app.service('MyService', function(){
//service code
});
app.controller('MyCtrl', function($scope, MyService){
//controller code
});
Small application
var services = angular.module('services',[]);
services.service('MyService', function(){
//service code
});
var controllers = angular.module('controllers',['services']);
controllers.controller('MyCtrl', function($scope, MyService){
//controller code
});
var app = angular.module('app',['controllers', 'services']);
var sharedServicesModule = angular.module('sharedServices',[]);
sharedServices.service('NetworkService', function($http){});
var loginModule = angular.module('login',['sharedServices']);
loginModule.service('loginService', function(NetworkService){});
loginModule.controller('loginCtrl', function($scope, loginService){});
var app = angular.module('app', ['sharedServices', 'login']);
Group similar types of objects
Group features
$(document).ready(function () {
$("#allow_max_members").bind('click', function() {
var shown;
shown = $(this).prop('checked');
$("#max_members").toggle(shown);
});
});
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<link rel="stylesheet" href="style.css" />
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="script.js"></script>
</head>
<body>
<div>
<input type="checkbox" id="allow_max_members" checked='true'/>
<input id="max_members" type="text" />
</div>
</body>
</html>
script.js
In order to really understand how to build an AngularJS application, stop using jQuery. jQuery keeps the developer thinking of existing HTML standards, but as the docs say "AngularJS lets you extend HTML vocabulary for your application."
AngularJS is a framework!
$scope.$watch('qty * cost', function(newValue, oldValue) {
//update the DOM with newValue
});
For example, these are valid expressions in Angular:
1+2
a+b
user.name
items[index]
$scope
//Pseudo-Code of $apply()
function $apply(expr) {
try {
return $eval(expr);
} catch (e) {
$exceptionHandler(e);
} finally {
$root.$digest();
}
}
Goes through list of watchers and calls listeners if it's needed starting with the current scope recursively to all descendants.
appModule.directive("check", function () {
return function (scope, elem, attrs) {
elem.bind('click', function () {
if (elem.prop("checked")) {
scope.$apply(attrs["myCheck"]);
}
});
};
});
Example: when you need to call $apply manually
Error: $digest already in progress
if(!$scope.$$phase) {
//$digest or $apply
}
anti-pattern
$timeout(function() {
$scope.message = "Timeout called!";
})// no need to pass 2nd param if it's zero!
Right way
setTimeout(function () {
$scope.$apply(function () {
$scope.message = "Timeout called!";
});
}, 0);
=
$timeout(function() {
//code here
}, 100, false); //doesn't call $apply();
Don’t, please don’t use DOM selectors inside controller!
appModule.controller('myCtrl', function($scope) {
// bad programmer =(
if ($('.header').length > 0) {
$('.pointer').css({
top: - $('.header').height()
});
}
})
Error: [jqLite:nosel] Looking up elements via selectors is not supported by jqLite!
native element object
// include BEFORE AngularJS
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.js"></script>
// now AngularJS uses jQuery instead of jqLite
<script src="https://code.angularjs.org/1.2.25/angular.js"></script>
Why? why?
Because...
angular.module('appModule')
.directive("metaTitle", function ($title) {
return {
restrict: 'AC',
link: function (scope, element, attrs) {
scope.$watch("$current", function(current) {
if (current) {
$title.documentHeight = element.height();
}
});
}
}
})
.value('$title', {documentHeight: 50});
<body ng-controller='MainCtrl'>
<div class='header'>
<ul>
<li ng-repeat="item in pointers" ng-click="goToParagraph(item)">Go to {{item}}
<span ng-hide="$last"> | </span>
</li>
<li class='change-height'
ng-click='$emit('changeHeight')'>Change height</li>
</ul>
</div>
...
<ul>
<li ng-repeat="item in pointers">
<a class='pointer' name="{{item}}"></a>
<span>{{item}}</span>
...
</li>
</ul>
</body>
var app = angular.module('app', []);
app.controller('MainCtrl', function($scope, $location, $anchorScroll) {
$scope.pointers = [1, 2, 3, 4, 5, 6];
$scope.goToParagraph = function(hash) {
$location.hash(hash);
$anchorScroll();
}
});
.header {
position: fixed;
background-color: green;
width: 100%;
color: #fff;
}
.pointer{
position: relative;
display: block;
top: -50px;
}
.change-height {
float: right;
}
app.directive('header', function($rootScope) {
return {
link: function(scope, element, attrs) {
scope.$on('changeHeight', function() {
var height = Math.floor(Math.random() * 100) + 50;
element.css({
height: height + 'px'
});
$rootScope.$broadcast('newHeight', height);
}
},
restrict: 'C'
});
app.directive('pointer', function() {
return {
link: function(scope, element, attrs) {
scope.$on('newHeight', function(e, value) {
if (value) {
element.css({'top': -value + 'px'});
}
});
},
restrict: 'C'
}
})
app.directive('header', function($header) {
return {
link: function(scope, element, attrs) {
scope.$on('changeHeight', function() {
var height = Math.floor(Math.random() * 100) + 50;
element.css({
height: height + 'px'
});
$header.height = height;
});
},
restrict: 'C'
}
}
).value('$header', {height: 50});
app.directive('pointer', function($header) {
return {
link: function(scope, element, attrs) {
scope.$header = $header;
scope.$watch('$header.height', function(newVal) {
if (newVal) {
element.css({'top': -newVal + 'px'});
}
})
},
restrict: 'C'
}
})
app.directive('pointer', function($header) {
return {
link: function(scope, element, attrs) {
scope.$watch(function() {
return $header.height;
}, function(newVal) {
if (newVal) {
element.css({'top': -newVal + 'px'});
}
})
},
restrict: 'C'
}
})
app.directive('header', function() {
return {
link: function(scope, element, attrs) {
scope.$on('changeHeight', function() {
var height = Math.floor(Math.random() * 100) + 50;
element.css({
height: height + 'px'
});
scope.newHeight = height;
}
},
restrict: 'C'
});
app.directive('pointer', function() {
return {
link: function(scope, element, attrs) {
scope.$watch('newHeight', function(newVal) {
if (newVal) {
element.css({'top': -newVal + 'px'});
}
});
},
restrict: 'C'
}
})
app.directive('header', function() {
return {
link: function(scope, element, attrs) {
scope.$on('changeHeight', function() {
var height = Math.floor(Math.random() * 100) + 50;
element.css({
height: height + 'px'
});
scope.internalHeight = height;
}
},
restrict: 'C',
scope: {
internalHeight: '=height' // what is the difference between '@', '=', '&'?
}
});
app.directive('pointer', function() {
return {
link: function(scope, element, attrs) {
scope.$watch('offset', function(newVal) {
if (newVal) {
element.css({'top': -newVal + 'px'});
}
});
},
restrict: 'C',
scope: {
offset: '='
}
}
})
<body ng-controller='MainCtrl'>
<div class='header' height='pageScope.newHeight'>
<ul>
<li ng-repeat="item in pointers" ng-click="goToParagraph(item)">Go to {{item}}
<span ng-hide="$last"> | </span>
</li>
<li class='change-height'
ng-click='$emit('changeHeight')'>Change height</li>
</ul>
</div>
...
<ul>
<li ng-repeat="item in pointers">
<a class='pointer' offset='pageScope.newHeight' name="{{item}}"></a>
<span>{{item}}</span>
...
</li>
</ul>
</body>
var app = angular.module('app', []);
app.controller('MainCtrl', function($scope, $location, $anchorScroll) {
$scope.pointers = [1, 2, 3, 4, 5, 6];
$scope.goToParagraph = function(hash) {
$location.hash(hash);
$anchorScroll();
}
$scope.pageScope = {newHeight: 50};
});
<body ng-controller='MainCtrl'>
<div class='header'>
<ul>
<li ng-repeat="item in pointers" ng-click="goToParagraph(item)">Go to {{item}}
<span ng-hide="$last"> | </span>
</li>
<li class='change-height' height='pageScope.newHeight'>Change height</li>
</ul>
</div>
...
<ul>
<li ng-repeat="item in pointers">
<a class='pointer' offset='pageScope.newHeight' name="{{item}}"></a>
<span>{{item}}</span>
...
</li>
</ul>
</body>
app.directive('changeHeight', function() {
return {
link: function(scope, element, attrs) {
element.bind('click', function() {
var height = Math.floor(Math.random() * 100) + 50;
element.css({ height: height + 'px' }); // <-- this is not '.header'. It doesn't work!!!
scope.internalHeight = height;
scope.$apply();
});
},
restrict: 'C',
scope: {
internalHeight: '=height'
}
});
app.directive('changeHeight', function() {
return {
link: function(scope, element, attrs, ctrl) {
element.bind('click', function() {
var height = Math.floor(Math.random() * 100) + 50;
// we need to define getElement() method in header
ctrl.getElement().css({ height: height + 'px' });
scope.internalHeight = height;
scope.$apply();
});
},
restrict: 'C',
scope: {
internalHeight: '=height'
},
require: '^header' // or '?^header' - doesn't throw error if header is not found
});
app.directive('header', function() {
return {
controller: function($element) {
this.getElement = function() {
return $element;
}
}
});
app.value('$header', {})
.directive('pointerOffsetCreator', function($header) {
return {
restrict: "A",
scope: {
className: '@'
},
link: function(scope, elem, attrs) {
if (elem.prop("tagName") == 'STYLE') {
scope.$watch(function() {
return $header.height;
}, function(newValue) {
function createClassString() {
return '.' + attrs.className + "{" +
"top: -" + newValue + "px" + "!important" +
"}";
}
if (newValue) {
elem.empty();
elem.html(createClassString());
}
});
}
}
}
})
<head>
<style pointer-offset-creator class-name='pointer'></style>
</head>
BIG QR=)
angular.module('app')
.directive('alert', function () {
return {
restrict: 'EA',
controller: ['$scope', '$attrs', function ($scope, $attrs) {
$scope.closeable = 'close' in $attrs;
}],
template:
"<div class='alert alert-dismissable' ng-class='\"alert-\" + (type || \"warning\")'>\n" +
" <button ng-show='closeable' type='button' class='close' ng-click='close()'>×</button>\n" +
" <div ng-transclude></div>\n" +
"</div>\n",
transclude: true,
replace: true,
scope: {
type: '=',
close: '&'
}
};
})
<html ng-app>
<head>
<meta charset="utf-8" />
<link rel="stylesheet" href="style.css" />
<script src="https://code.angularjs.org/1.3.0-rc.2/angular.js"></script>
<script src="app.js"></script>
</head>
<body>
<alert type="danger">My message</alert>
</body>
</html>
// compiled markup
<div class="alert alert-dismissable ng-isolate-scope alert-danger"
ng-class="'alert-' + (type || 'warning')" type="'danger'">
<button ng-show="closeable" type="button" class="close ng-hide" ng-click="close()">×</button>
<div ng-transclude=""><span class="ng-scope">My message</span></div>
</div>
angular.module('myApp')
.service("$notification", ["$timeout", function ($timeout) {
var alerts = [];
var TIMEOUT_MS = 5000;
this.getAlerts = function() {
return alerts;
}
this.success = function(msg) {
addAlert(msg, 'success');
}
this.error = function(msg) {
addAlert(msg, 'danger');
}
this.warning = function(msg) {
addAlert(msg, 'warning');
}
var addAlert = function (msg, type) {
if (msg) {
var item = {msg: msg, type: type};
alerts.push(item);
$timeout(function() {
var index = alerts.indexOf(item);
if (index > -1) {
alerts.splice(index, 1);
}
}, TIMEOUT_MS);
}
};
}])
angular.module('appModule')
.run(function ($templateCache) {
$templateCache.put("whateverYouWant",
'<alert ng-repeat="alert in alerts" type="alert.type" close="closeAlert($index)">{{alert.msg}}</alert>\n');
})
.directive("globalNotification", ['$notification', function ($notification) {
return {
restrict: 'AC',
templateUrl: 'whateverYouWant',
link: function (scope, element, attrs) {
scope.alerts = $notification.getAlerts();
scope.closeAlert = function (index) {
scope.alerts.splice(index, 1);
};
}
}
}])
// put this div somewhere on your page
<div class="global-notification"></div>