So I've been meaning to learn AngularJS for some time now and I've watched a few videos here and there but kept on dropping and never building anything concrete. This repo will help me track my progress through two curriculums I've found on thinkster.io and codeschool.com:
- A Better Way to Learn AngularJS
- Shaping up with AngularJS
- AngularJS Tutorial: Learn to build Modern Webapps
This repository will also include code from other sources which I will cite in the comments and below when I find them. There are also basic code example as alternative to demos shown in the videos so that we can see more than one example to be able to accurately wrap ones head around.
This README will also track some codes, gotchas, comments and other shenanigans to help others like me learning AngularJS for the first/second/third time. Hope this helps!
#####Prerequisites
- HTML
- CSS
- JS (OOPs, Prototyping, functions, events, error handling)
- Idea of the Model-View-Controller technique
- The Document Object Model
#####Requirements
- Web browser
- Acccess to the O'Rielly AngularJS Book. If you are a student you can access it here using your university VPN. [AngularJS by Brad Green and Shyam Seshadri (O’Reilly). Copyright 2013 Brad Green and Shyam Seshadri, 978-1-449-34485-6.]
###00-Concepts AngularJS relies heavily on the MVC approach:
- a Model which contains the data shown to the user in the view and with which the user interacts. (think of it as the place where we store the data)
- a View this is what the user sees. (the user interface or the DOM)
- the controller which contains all the logic.
There are some buzzwords used in AngularJS:
- Data Binding is the sync of data between the model and the view
- Two way Data Binding is a feature of angular that says that everything in the document is live. Whenever the input changes the expression is automatically recacluated and the DOM is updated as well.
- Scope this is where the variables and data (known as model) are stored, think of it as the traditional scope that allows all other components to access the data in the model.
- Templates all HTML files with angular code are known as templates because angular must fill in expressions and other gadgematics.
- Directives apply special behaviour to HTML elements. For example the
ng-appattribute in our 00-concepts.html file is linked to a directive that automatically initializes the application. When we defineng-modelattributes to our<input>element, we create a directive that stores and updates the value from the<input>field to the scope. - filters format the value of an expression for display to the user. Filters are very useful and in our 00-concepts.html file we use
currencyto format the output of the expression to resemble a bill or currency.
The image summarizes 00-1-concepts.html

-
Services contain all the 'independant logic', which is other words is a way to supply and manipulate data to our application. We use services to allow us to resue it in other parts of the application. For example in 00-1-concepts.html all the logic was stored within
InvoiceController. In 00-2-concepts.html we will refactor the code to move the conversion logic into a service in another module. -
Dependency Injection In 00-2-concepts.html we see that
ng-appdefinesinvoice-srv-demoas the main module to use in application. In the defintiion of this module we state thatfinanceis a dependancy to the main module. We also define the constructor for the controller after passing in the dependancycurrencyConverterfrom the finance module. This is known as dependency injection
###00-Spin ####The Dot. Found this gotcha thanks to this video.
<script>
function FirstCtrl($scope) {}
function SecondCtrl($scope) {}
</script>
<div ng-app="">
<div ng-controller="myCtrl">
<input type="text" ng-model="message">
<br /><h1><b>{{message}}</b></h1>
</div>
<div ng-controller="yourCtrl">
<input type="text" ng-model="message">
<br /><h1><b>{{message}}</b></h1>
</div>
</div>If we set up our code as above, scope inheritence is broken Each new ng-model that we set up for the message is creating a new instance of message which means there is no longer communication between the two messages. To rectify this we place a data model on the $scope which has the message property. I like to think of this as each controller getting its own scope because recitifying the code as shown below also doesn't fix the issue and no data is shared between the two controllers.
<!--Script remains the same-->
<div ng-app="">
<div ng-controller="myCtrl">
<input type="text" ng-model="data.message">
<br /><h1><b>{{data.message}}</b></h1>
</div>
<div ng-controller="yourCtrl">
<input type="text" ng-model="data.message">
<br /><h1><b>{{data.message}}</b></h1>
</div>
</div>However editing the code in a way (below) such that there is a scope set up for the entire application will ensure that the scope can sync data between the controllers.
<!--Script remains the same, we initialize data.message with 'hi'-->
<div ng-app="" ng-init="data.message='hi'">
{{data.message}}
<div ng-controller="myCtrl">
<h1>{{2+2}}</h1>
Type in a message: <input type="text" ng-model="data.message">
<br /><h1><b>{{data.message}}</b></h1>
</div>
<div ng-controller="yourCtrl">
Type in a message: <input type="text" ng-model="data.message">
<br /><h1><b>{{data.message}}</b></h1>
</div>(this observation is indeed true, we need to have a parent to allow this kind of inheritence). We now look at a different way to do this.
####Data sharing between controllers
This can be done by creating a factory which will supply the data. We then bind the local $scopes to the Data factory where we can then sync data between the controllers. See 00-2-spin.html.
var myApp = angular.module('myApp', []);
myApp.factory('Data', function(){return{message:"this is important"};});
function myCtrl($scope,Data){$scope.data = Data;}
function yourCtrl($scope,Data){$scope.data = Data;}###Shopping Cart (01-1-shopping and 01-1-forms)
The shopping cart example demonstrates some of the other functionality of the AngularJS framework. You may have noticed that we have seen two ways to creating controllers:
//method 1
function Ctrl($scope) {};
//method 2
var myApp = angular.module('myApp', []);
myApp.controller('Ctrl', function($scope){});We can compare the two using our shopping cart app in this commit. Method 2 ensures that we keep our controllers out of the global namespace using the module we defined.
The docs page regarding forms we find a whole load of neat tricks and tips that are demonstrated in 01-01-forms.html. The page also demonstrates the abilities of ng-show, ng-hide, and validation techniques that AngularJS provides.
###02-Filters
####Custom filters
There's only syntax to learn here, watched this [video] and see the (http://www.thinkster.io/angularjs/EnA7rkqH82/angularjs-filters) dummy code I typed up. Another example is shown below using extra aruments that can be used as text|decimal2binary:true:
//converts decimals to binary form (i don't think this really works)
app.filter('decimal2binary', function(){
function convert(num,bool){
if(bool){ console.log("you said true as argument!");}
num = parseInt(num);
if(num === 0){
return 1;
}else{
remainder = num % 2;
return convert(num/2)+""+remainder;
}
}
return function(input, bool){
return convert(input, bool);
}
});####Searching and filters We can see an implementation of the search technique in 02-2-filters.html. Some other in-built AngularJS filters are summarized below:
limitTowill limit the number of results shownorderBytakes a string and will order the results using the property it maps tolowercaseanduppercaseturn the results to lower and upper case (these are best applied to the data binding as shown below)date,currencyformat our data and are self explanatorynumbertakes a integer and rounds off the data to that decimal place. (eg: 0 would round off to 0dp)
<span ng-repeat="element in list | filter: search | orderBy: 'lastname' | limitTo: 4">
{{element.lastname | uppercase}}, {{element.firstname | lowercase}}
</span>###03-Directives
####Custom elements
Directives are one of the most impressive features AngularJS has to offer. In our first example we create a directive which is of restriction 'E' which means that it is an element. Our directive is simple in that it just shows an image of a calculator created by the <caclulator> element. The commit shows the code in detail.
####Custom Attributes and Classes
This commit demonstrates a custom attribute we build. Here instead of setting restrict:"E" we set restrict:"A" and provide a link function which is executed whenever the attribute is attached. If we set restrict:"C" then the directive is executed whenever we have the class with that name (see commit). We can also have directives in comments using restrict:"M" and using the following:
<!--directive:myDirective-->####Useful Directives
Directives default to restrict:"A". The directive will also pass in the scope and element which we can use. As shown in 03-2-directives.html. The directive also passes the attrs which is an object with all the attributes of the element. We can exploit this to make our code abstract when we want to add/remove classes. This is demonstrated in this commit. We also see the use of $apply() to evaluate functions in this example.
####Directive Communication (03-4-readinglist.html)
Directives can communicate with each other as shown in the Reading List Example. Here we create a directive called <book> and a couple of attributes that are dependant on the book directive.Each <book> has a local scope as defined. <book> also has an API which can control the local scope. Each attribute we define accesses the <book> API to manipulate its properties. The properties can be viewed when we roll over it. A streamlined way (albeit without the romance,thriller etc. properties) is shown in 03-6-elements.html.
- Transclusion We also have a
transcludeproperty which is demonstrated in 03-5-transclusion.html that allows whatever is inside our custom element to be placed inside it and not overwritten by the template. - Nested elements and how they communicate with each other are demonstrated in 03-7-nested.html
####Directive properties Here is a summary of the properties I see are most useful in directives:
restrict:"E"||"A"||"C"||"M"this declares how the directive behaves, as an element, attribute, class or comment respectively. We can use any combination of these.template:"string"allows your directives to have a template.templateUrl:"string"allows your directive to load a URL as a template.replace:boolif true it will replace the current element and by default it is false and will append to the element.transcludeLets you move the children of the directive into a place inside the template. We do this by specifiyingng-transcludeattribute of the target location. example (here it targets the<span>and not the other<div>s:
//...
transclude:true,
template:"<div><div><span ng-transclude></span></div></div>",
//...link:function()executes when the directive sets up. Can be useful in settingtimeouts, or bindingevents to the elementcontroller:function()allows you to set up scope properties and create an interface for other directives to interact with. -scope:{}allows you to create a new scope for the element rather than inherit from the parentrequire:['^directives']makes it mandatory for the current directive to be nested in the parent to ensure it functions correctly.
A quick gotcha to note, when you name your directivedatePickerwhen declaring it, you can refer to it asdate-pickerin your HTML.
####Pre-link, Post-link and Compile From the readings it seems there are two important phases when an angular application is being created.
- The Loading of the Script (not so important for us right now)
- The compile phase is when the directives are registered, the templates are applied and the compile function is executed.
- The link phase occurs once the templates have been applied and the compile functions have been executed. In this phase the link functions are executed whenever the data in the view is updated.
The main difference I see in them is that the compile function is executed once only during the compilation stage while thelinkfunction is executed once for each instance of the directive. At the moment I do not see an immediate use case for the compile function, however the book suggests that it is useful when you need to modify something in relation to all cases. Note the difference between the function defintions below, thecompilefunction doesn't have access to the scope because it is not created yet.
return {
restrict:"",
require:"",
controller:function(scope,element,attrs){},
compile: function(templateElement, templateAttrs, transclude){},
link: function(scope, instanceElement, instanceAttrs, controller){}
}If we take a look at 03-8-compilevslink.html, we see the log executes the controller and the compile's pre and post but not the link. Trying different combinations it seems that if you have a compile, you cannot have a link but you can get the effect of having a compile and link by setting a pre and post function to the compile function.
###04-Scopes
Scopes can be nested. These nested scopes can either be child scopes (which inherits properties from its parents) or isolate scopes (which doesn't inherit anything). Everything that we try to evaluate for example {{name}} which is input via ng-model='name' will be held on the scope. Thus we can think of scopes as the link between the controllers/directives and the view.
####Isolate Scope
We can demonstrate a simple way to create an isolate scope for each directive we create as shown 04-0-scope.html. When we define a directive, a property we can return is scope. This can be set to be true, false, or {/*stuff inside*/}. When we set to false (by default), it will use the existing scope in the directive. true will inherit the scope from the parent. {} will create an isolate scope for each directive. When you define an isolate scope there are binding strategies or methods of getting data into that scope. They are discussed below.
We demonstrate two methods of using attributes to store data into the directives isolate scope in 04-1-scope.html
The @ operator will expect a string to bind the information we want to pass, however, the = will expect an object, this allows us to set up a two way binding between the parent scope and our directive scope. 04-2-scope.html demonstrates how updating the directive scope updates the parent. Note that in our attribute we are now passing objects and not strings.
The & operator will evaluate an expression that you pass to it. So it seems that if you are passing values between the controller and the directive you need it to be in an object, and the object must map its properties to the function. This is demonstrated in 04-3-scope.html.
04-4-review.html encapsulates all the concepts we have discussed so far. Note that when we use =, we assume that the user is going to order from the same bookstore and thus we would like reflect the change in all other directives.
####Some Comments
- We see that there is ***one root scope, and Angular will first look in the current scope before going up to the parent until it reaches the root scope. A demonstration follows:
<script>
function MyParentCtrl($scope){
$scope.name='John Doe';
$scope.home='Canada';
}
function MyChildCtrl($scope){
$scope.name='Jane Doe';
}
</script>
<div ng-app>
<dig ng-controller="MyParentCtrl">
Hello {{name}}! You are from {{home}}
<div ng-controller="MyChildCtrl">
Hello {{name}}! You are from {{home}}
</div></div></div>Which will print out Hello John Doe! You are from Canada Hello Jane Doe! You are from Canada. This demonstrates the point.
- Broadcasting and Emmiting
$emit(name, args)will allow an event to be executed in current and parent scopes.$broadcast(name,args)will allow an event to be executed in current and child scopes. This will be demonstrated in future pages but can be seen very nicely on this page Scope Events Propagation. Controller asSyntax refers to the following:
<script> function MyLongCtrlName(){
this.clickMe = function(){
alert("You clicked me!!");
}
this.name = "Type your name here...";
}</script>
<div ng-app ng-controller="MyLongCtrlName as ctrl1">
<input type="text" ng-model="ctrl1.name">
{{ctrl1.name}}
<button ng-click="ctrl1.clickMe()">Click this button</button>
</div>It seems to add clarity to the code and in larger applications I think I will definietly be going to use it more often!
###05-Other Paradigms Some alternative ways of thinking of Controllers and different ways of organizing angular applications is also provided on the thinkster.io page. I summarize them here very briefly:
- Think of setting up a controller like this:
app.controller("Ctrl", function($scope){
this.hi = 'Hello';
this.sayHi = function(){
alert(this.hi);
}
return $scope.Ctrl = this;
});```
We can acess this now using the following html:`<div ng-click="Ctrl.sayHi()">Click</div>`. This serves to make the controller explicit and closely mimics the `Controller as` syntax above.
* Organization. We can organize and initialize our controllers and directives like this. (however this doesn't work for filters)
```javascript
var app = angular.module('myApp', []);
var myAppComponents = {};
myAppComponents.controllers = {};
myAppComponents.controllers.AppCtrl1 = function($scope){/*stuff here*/};
myAppComponents.controllers.AppCtrl2 = function($scope){/*stuff here*/};
myAppComponents.directives = {};
myAppComponents.directives.mydirective = function(){return {/*stuff here*/}};
app.directive(myAppComponents.directives);
app.controller(myAppComponents.controllers);