- WHAT IS ANGULARJS AND WHY USE IT?
- DATA-BINDING EXAMPLE
- EXAMPLE OVERVIEW
- STEP 1: GIVE YOUR BLOG A NAME
- STEP 2: CREATE YOUR FIRST MODEL (READ)
- STEP 3: MAKE YOUR POSTS EDITABLE (UPDATE)
- STEP 4: POSTS REMOVAL (DELETE)
- STEP 5: ADDING NEW POST (CREATE)
- SUMMARY AND FURTHER STUDIES
WHAT IS ANGULARJS AND WHY USE IT?
Every framework or design pattern tries to address a common problem in a generic way. ANGULARJS, one of the GOOGLE project, aims at addressing problem associated with manipulating DOM when writing web applications - its goal is to make dynamic web page development as declarative as a static one. This framework is particular designed for client-side dynamic web pages and apply the Model-View-Whatever design pattern with JavaScript. The reason it calls itself MVW instead of either MVC / MVVM / MVP etc is because it doesn't require you to follow a particular pattern strictly when using it. Simply speaking, it aims at separating the Template from your data and business logic. If you hate manipulating DOM, if you hate data being stored in DOM and believe it should be separated from the display, if you want to create dynamic webpage quickly and easily maintained - ANGULARJS is definitely one of the framework you are looking for.EXAMPLE OVERVIEW
In this tutorial, we are going to create a simple personal blog. This example requires three files: (1) index.html (2) angular.min.js and (3) app.js. The index.html is the view (or you may think it is a template if you are not familiar with what view means). Along completion, it should look something like this:<!DOCTYPE html>
<html ng-app="blog">
<head>
<script src="angular.min.js"></script>
<script src="app.js"></script>
<title>{{blogName}}</title>
</head>
<body ng-controller="PostsCtrl">
<!-- Something to add here later -->
</body>
</html>
This is a simple one-page html file that describes the web page - extra contents will be added in the <!-- something to add here later --> section during our workshop. You may realize there are some attributes that start with 'ng' for some HTML nodes. These kind of attributes are called directive within the framework terminology. It 'tells' the framework what the corresponding tag should behave during runtime.At this point of time, there are two ng directive being used: (1) ng-app and (2) ng-controller; The ng-app is the entry point for bootstrapping an angular application. It tells the framework 'starting from this node, it should be considered as an angular application and will be controlled by the framework'. In our example, it means the scope between <html> and </html> is an angular application. Indeed, you can put this ng-app directive in any other HTML tag such as <body> or even a <div> - effectively, the scope of the application will be changed. The value assigned to the ng-app, e.g. 'blog' in our case, is the module name that we will define later.
Another directive 'ng-controller' tells the framework what controller function will be used to control the scope starting from that tag. In our example, it means the controller 'PostsCtrl' defined within the 'blog' module will be used to handle the scope from <body> to </body>. Similar to the 'ng-app' directive, you can put ng-controller in any html tag and the scope will be changed correspondingly
STEP 1: GIVE YOUR BLOG A NAME
Lets start with giving your blog a name. Now, create the app.js file in the same directory with your index.html and add the following content://Declare a module 'blog'
var blog = angular.module('blog', []);
//This block runs after the module is initialized and before your controller works
blog.run(function($rootScope){
$rootScope.blogName = "Tony's Blog";
});
//This creates a new controller 'PostsCtrl' for your module, which will be matched with the view
blog.controller('PostsCtrl', function($scope){
});
The blog.run() is a function that will be executed when the module is initiated. We passed a callback function, function($rootScope), to the run() function and define a new variable 'blogName' within the $rootScope when the module is initiated. $rootScope is a type provided within the framework. In ANGUARJS, you may think SCOPE as a kind of ViewModel within the MVVM pattern. If you are not familiar with that terminology, you can think SCOPE as a kind of common area that can be accessed by the View (i.e. HTML) and the controller function (i.e. Backend function in the JavaScript).
The blog.controller('PostsCtrl', function($scope)) defined here is a built-in function that allow you to create a new controller function for the blog module defined earlier. We passed in a callback function again and will be executed when the controller is initiated. We will cover the differences between $scope and $rootScope in next section. Now, add the following content within the <body> tag of our index.html:
<h1 style="text-align: center">{{blogName}}</h1>
The expression {{}} here is an ANGULAR expression that reference the variable within the SCOPE. In our case, {{blogName}} means reference the value stored in the blogName variable within the SCOPE that we defined earlier. Now, your application should display the blogName in the page. STEP 2: CREATE YOUR FIRST MODEL (READ)
To add a first model, add the following content to the controller callback function:blog.controller('PostsCtrl', function($scope){
$scope.posts = [{
'title':'Hello World',
'content':'This is my first post in ' + $scope.blogName,
'editing':false
},{
'title':'I love AngularJS',
'content':'How come it is so handy!',
'editing':false
}];
});
Similar to the previous step, we injected the $scope object of the framework and define the posts variable with an array of json within the variable. It is just a pure JavaScript object and this is one of the good feature of ANGULAR that doesn't require you to extend a framework object in order to create a model. Back to index.html, lets add the following after the title we added in step 1:$scope vs $rootScope
In the previous step, we added the variable in the rootScope instead of scope that we do in this step. This is because there is a hierarchic structure for the SCOPE concept. This tries to mimic the DOM structure and allow you to tell what scope it is referring to when you are looking at the html view page. As the name tells, rootScope is the mother of SCOPE and there is only one rootScope within a module. All child scope is inherited from rootScope and scope can access variable that is defined in parent scope (i.e. in our example, we use $scope.blogName to reference the variable we defined earlier in the $rootScope). Whether a new scope is created depends on the directive. Some directive will generate a new child scope while some of them will not. In our case, ng-controller will create a new child scope that inherit from the rootScope.
<ul>
<!-- list all posts -->
<li ng-repeat="post in posts">
<!--Display board-->
<div>
<p>{{post.title}} [<a ng-click="post.editing = true" href="#">Edit</a>][<a ng-click="remove($index)" href="#">Remove</a>]</p>
<p>{{post.content}}</p>
</div>
</li>
</ul>
Here, we added a new <ul> tag. The <li> tag is with a directive ng-repeat. This directive tells the framework to repeatedly generate this tag and the corresponding child nodes based on the expression 'post in posts' - it means that for every element within the posts variable defined, generate a new <li> with the corresponding content for each element. We give a name of 'post' for each element within posts to reference the corresponding data so that we can access stuff like {{post.title}} etc. This is how the framework become declarative and you may now realize how it define the dynamic behavior for the tag instead of manipulating DOM element. You are telling that the tag will be repeated based on some condition. Now, your posts should be displayed within the application and we have already bind the data to the corresponding area we want. Lets see how we can edit it.{{variable}} or not?
You realized that some part require the {{}} expression to reference the variable in scope while sometimes we can simply reference them by ng-click="post.editing = true". The general rule is anything that is standard HTML element will require the {{}} expression for referencing the data while you don't need this expression if it is referenced within the angular directive.
STEP 3: MAKE YOUR POSTS EDITABLE (UPDATE)
Consider the html code we added previously together with the following new code snippet:<!-- list all posts -->
<li ng-repeat="post in posts">
<!--Display board-->
<div>
<p class="title">{{post.title}} [<a ng-click="post.editing = true" href="#">Edit</a>][<a ng-click="remove($index)" href="#">Remove</a>]</p>
<p>{{post.content}}</p>
</div>
<!--Editing board-->
<div ng-show="post.editing==true">
<form ng-submit='post.editing = false'>
<p><input ng-model="post.title"/></p>
<p><textarea ng-model="post.content"></textarea></p>
<p><input type="submit" value="Done"/></p>
</form>
</div>
</li>
Here, we added a new 'editing board' to make a new form that allow us to input text for updating the posts. The ng-show tag tells the framework the tag is shown when the condition is fulfilled. For example, the editing form <div> will be displayed when 'post.editing == true' is satisfied. When we click the Edit hyperlink in line 6, the ng-click will tells the framework to listen to the onClick event when the element is clicked and execute the expression - which turns the post.editing flag to true. After that, the ng-show condition for the editing board will be fulfilled and the corresponding content will be displayed such as:'Magic' happens here, if you try to type and change the content in the input area, you will realize the display part of the post will change automatically. The ng-model directive makes this trick - it tries to bind the data within the model at that corresponding input element. This is a two way data binding, meaning that when you update the data within the input element, the model will be directly updated. . As data we referenced within the display board and the data we binded with the input elements come from the same mode, it updates strict away.
When you click Done, the ng-submit directive will listen to the submit event and turn the post editing flag back to false. You can see we have successfully complete the 'Update' part.
STEP 4: POSTS REMOVAL (DELETE)
Lets add the following code within our controller callback function:$scope.remove = function(index){
$scope.posts.splice(index,1);
}
As mentioned, Scope is more like the ViewModel concept within the MVVM pattern and you are allowed to put any 'command' that can be called by the view. Here, we try to add a new function remove(index) that can be accessed by the View template file. Let's back to our index.html code snippet:<!-- list all posts -->
<li ng-repeat="post in posts">
<!--Display board-->
<div>
<p class="title">{{post.title}} [<a ng-click="post.editing = true" href="#">Edit</a>][<a ng-click="remove($index)" href="#">Remove</a>]</p>
<p>{{post.content}}</p>
</div>
<!--Editing board-->
<div ng-show="post.editing==true">
<form ng-submit='post.editing = false'>
<p><input ng-model="post.title"/></p>
<p><textarea ng-model="post.content"></textarea></p>
<p><input type="submit" value="Done"/></p>
</form>
</div>
</li>
Consider line 6, ng-click directive is used with the Remove button and whenever it is clicked it will call remove($index), which we defined in the scope. $index is a special variable defined by the ng-repeat directive for accessing the current element index during the iteration. When remove() is executed, it calls the splice method, which is a JavaScript standard method for handling array elements and remove the element from the posts variable. Again, it shows how great the framework is when the model is a pure JavaScript object.The remove functionality should work as expected now.
STEP 5: ADDING NEW POST (CREATE)
Now, lets go for the final step of our simple CRUD example. Lets add the following addPost command to the scope within the controller callback function:$scope.isAddingPost = false;
$scope.addPost = function(title, content){
$scope.posts.push({
'title': title,
'content': content,
'editing':false
})
$scope.newPost = [];
}
Similar to the remove command, we add the addPost(title,content) function in the scope and allow the View page to call it. We use the standard push method for JavaScript array object and add the new element to our model and default the editing flag as false. The newPost variable will be used later in the view and will cover more in a sec. Another isAddingPost flag is added to indicate whether the new post editing board should be displayed or not. It is defaulted as false initially.Consider adding this to the <ul> tag:
<h1 style="text-align: center">{{blogName}}</h1>
<a ng-show="isAddingPost == false" ng-click="isAddingPost = true" href="#">New post</a>
<ul>
<!-- New post editing board -->
<li ng-show="isAddingPost == true">
<div>
<form ng-submit='isAddingPost = false; addPost(newPost.title, newPost.content)'>
<p><input ng-model="newPost.title" placeholder="title here!"/></p>
<p><textarea ng-model="newPost.content" placeholder="content here!"></textarea></p>
<p><input type="submit" value="Done"/></p>
</form>
</div>
</li>
<!-- list all posts -->
<li ng-repeat="post in posts">
<!-- covered in previous steps -->
</li>
</ul>
Here, we do add two things:(1) In line 3, we add a new 'New post' link at the beginning after the title together with the ng-click directive turning the isAddingPost flag to true. This hyperlink will be displayed only if the isAddingPost state is false (as defined by the ng-show directive).
(2) We also add a new post editing board that will be shown when the isAddingPost state is true. As before, we bind the input element with the newPost model. We the form is submitted, it turns the isAddingPost back to false and call the addPost command with the value stored in the newPost model. Eventually, the addPost will add the newPost content to the posts variable as we defined in out app.js. After adding the newPost to the posts variable, we initialize the newPost to empty by $scope.newPost=[] to prevent the data is being cached next time when you try to create a new post again.
SUMMARY AND FURTHER STUDIES
We have now completed our personal blog as expected. Along the example, we highlighted:
- ANGULAR directives are used to tell the framework the runtime behavior of the corresponding tag
- ANGULAR application is bootstrapped by ng-app and ng-controller directives.
- Scope concept of ANGULAR is hierarchic and is the common area being accessed by View and the Controller function
- Ways to define model and command in SCOPE and how it behaves like a ViewModel
- ANGULAR model is a vanilla JavaScript object and doesn't require you to extend any framework object for creating a new model
- Common ANGULAR directive usage including ng-repeat, ng-show, ng-submit, ng-click
- Two-way data binding with the ng-model directives
Comparing to traditional way in implementing dynamic web pages or web application, using ANGULARJS is much quicker and easier than the traditional ways of using vanilla JavaScript or jQuery. The cool part of the framework is, instead of being manipulated by a backend JavaScript and doesn't contain any knowledge of the DOM manipulation within the HTML page, by simply reading the HTML template now we can tell what exact behavior is expected based on the directives. This is great for debugging when you see some weird part appears in the outlook and you will not have to go through those bloody DOM manipulation in order to understand why the tag is being manipulated wrongly. By de-coupling the view and the model, you can also easily change your view without heavily manipulate your backend javascript.
This tutorial is just a 'helloworld' one and doesn't utilize most part of the framework. If you are interested in the framework and want to investigate more in this, you may try to go through topic like: dependency injection in Angular, testability, concept of Angular Service, factory and provider etc.



No comments:
Post a Comment