It provides a two way data binding which leads to less custom JavaScript. Two way data binding means that you would not have to redisplay model data each time you change it. For instance, if you have a user model and the user changes his name in your app – all instances which display his name on the page will be changed to the new name automatically.
Introduction
Hello guys.
In the following two tutorials we are going to rebuild the application that we built in the tutorial for creating offline applications with the HTML5 Application Cache – a note-keeping app. This time, users would not be able to access the website offline but the note keeping application would be built with Angular.js and have all CRUD (create, read, update, delete) features and a design built on Bootstrap. We would not be using jQuery in building the app.
Angular.js is particularly useful for single page applications and applications which involve a lot of data.
It provides a two way data binding which leads to less custom JavaScript. Two way data binding means that you would not have to redisplay model data each time you change it. For instance, if you have a user model and the user changes his name in your app – all instances which display his name on the page will be changed to the new name automatically.
Modules, controllers and factories
We define a module by entering angular.module(‘noteTaker’, []); in a .js file. The empty array could be filled with the dependencies that our module has in order to run. If you just typeangular.module('name')
without a second argument then the module with that name is returned instead of created anew, which is useful for chaining.
Then to add a controller to that module in another file you could do:
angular.module("noteTaker")
.controller("NotesController", function($scope, NoteFactory ) {
$scope.notes = NoteFactory;
})
We would not be providing any arguments to the $scope and NoteFactory parameters and we would not be calling that function ourselves. The arguments are injected into the function when the controller is loaded. $scope
is built-in with Angular and we would not be creating it ourselves. It passes the current scope to the controller. Scopes are discussed below.
To pinpoint the module our app would be using we type: ng-app="noteTaker"
as an attribute to any html element (you can see we defined our noteTaker module above).
Each app has a $rootScope (which you could inject) and various other child scopes. Whenever you use a controller you create a child scope. If you use a controller for a particular <div>
and use two other sibling controllers within that then they both will have access to the properties and methods of that parent <div>
scope ($scope
) but each sibling would not have access to the scope of the other sibling.
To use a controller you can type:
ng-controller="NotesController"
as an attribute to any element.
Here is how the header of our app looks like:
<html>
<head lang="en">
<meta charset="UTF-8">
<title>The Note Taker</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css"/>
<link rel="stylesheet" href="css/styles.css"/>
</head>
<body ng-app="noteTaker">
<div class="container">
<div ng-init="showForm = 0; editMode = 0" class="row" ng-controller="NotesController">
</code>
We load Angular, Bootstrap and our custom stylesheet, define our main module and create a scope for the NotesController.
In NotesController you could inject $scope and set properties on it that would be accessible in the markup. ng-init just initializes variables that we are going to use later on.
angular.module("noteTaker")
.factory("NoteFactory", function() {
var allNotes = localStorage.notes || [{title: "Hello", body: "Edit or delete this note and start writing", id:0}];
if (typeof allNotes === 'string') {
allNotes = JSON.parse(allNotes);
}
return {
getNotes: allNotes,
saveNote: function(note) {
allNotes.push(note);
localStorage.notes = JSON.stringify(allNotes);
},
deleteNote: function(index) {
var confirmDelete = confirm("Do you really want to delete this note?");
if (confirmDelete) {
allNotes.splice(index,1)
localStorage.notes = JSON.stringify(allNotes);
}
},
editNote: function(index, note) {
allNotes[index] = note;
localStorage.notes = JSON.stringify(allNotes);
},
getNoteText: function(index) {
var noteParent = document.getElementsByClassName("noteText")[index];
var title = noteParent.getElementsByTagName("h1")[0].innerText;
var body = noteParent.getElementsByTagName("p")[0].innerText;
return {title: title, body: body};
}
}
})
The CRUD API is simple:
We create a factory which is a function that will be called in whichever controller we inject the factory (through the parameters of the controller’s function) and create some methods. We return an object with methods that save, delete and edit the note and we create one helper method that gets the text of the note that we have edited and clicked on its save icon. We also have a property which stores all notes. We persist the notes in localStorage again.