Category Archives: angular-js

Some awesome snippets from angular-js source code.

I have started going through the angular-js source code trying to understand the internal workings of the same.
Its about 22000 lines of pure magic and an awesome learning experience for new and experienced developers.
Here are some of the gists that I have collected.
BTW, heres the source code : https://ajax.googleapis.com/ajax/libs/angularjs/1.2.24/angular.js
Please feel free to send me any pull requests if you find that the gist could be improved and comment if you have more awesome gists to share !

  1. Cloning an object with multiple nested functions or keys using javascript

  2. Simple function to remove special characters from a string.

  3. Angular: UID function creator

  4. Angular: Simple functions

  5. Simple function to remove special characters from a string.

  6. Angular: Function to calculate the length of string

  7. Angular: Regex to search for html tags

  8. Angular : The actual code behind the magical angular injector dependencies.

Also check out angular-js.in >> A curated list of angular directives.

svg-element

Understanding SVG elements using Angular-js

Scalable Vector Graphics are an important part in responsive web-design because instead of using different images for each device you can use svg vectors to define an image and it will automatically scale to the appropriate size.

I have made a small demo for understanding basic svg elements using Angular-js.
Drag the sliders and understand how the svg element as well as the code changes accordingly.

DEMO

angular-js router

Getting started with Angular-js Router

Angular-js is an awesome MVC framework for javascript.Routing and Deep-linking is an important part of any web application because just a state can be shared directly via the url.
Angular-js Router is one of the most important part an ng-app.

Important Links :-

Github link

Docs for ngRoute
Docs for $routeProvider
Docs for ngView
Download angular-route.min.js

1.Simple Start

Since Angular version 1.2 , you need to angular-route is in a different module and needs to be included seperately .
$routeProvider is angular module where we define the templateUrl and the controller.We need to define the routes inside App.config and give the template Url.The template can be defined inside script tags
<script type= text/ng-template>Your html Template</script>
or can be externally included.

ng-view is the directive that directly complements $route.

ngView is a directive that complements the $route service by including the rendered template of the current route into the main layout (index.html) file. Every time the current route changes, the included view changes with it according to the configuration of the $route service.

Here’s the most simple version of an angular app running ngRoute

App.config(function  ($routeProvider,$locationProvider) {
    $routeProvider.when("/hello",{ templateUrl : "hello.html"});
    $locationProvider.html5Mode(true);//We are using the html5 mode to make it work in plunkr
});

2.Multiple routes and otherwise

Here’s Another example that makes use of .otherwise() of $routeProvider.
From docs :
.otherwise() sets route definition that will be used on route change when no other route definition is matched.
ngRouter lets you define different paths and their corresponding templates and controllers at the end you can define a otherwise function to redirect or use a template when there is url hash which has not been defined.Its important for error handling.
The Route path is matched against $location.path and if $location.path contains redundant trailing slash or is missing one,the route will still match and the $location.path will be updated to add or drop the trailing slash to exactly match the route definition.

$routeProvider
	.when("/link1",{
		templateUrl : "js/hello.html" , controller : "ChildCtrl1"
	})
	.when("/link2",{
		templateUrl : "js/hello2.html" , controller : "ChildCtrl2"
	})
	.otherwise({
		templateUrl : "js/hello3.html"
	});

Here’s the plnkr for it

3.Using $routeParams

The path defined in $routeProvider can contain named groups starting with a colon and ending with a star: e.g.:name*. All characters are eagerly stored in $routeParams under the given name when the route matches.

For example /list/:id will match /list/1 and give $routeParams.id = 1.

The path can contain optional named group parameters with a question mark.FOr e.g.link1?search=hello will give
$routeParams.search = "hello"

Here’s is the plnkr for that

4.Loading a template according a condition

There are 2 simple ways to load or redirect to a particular route according to some variable or condition.

Here’s is the first way using resolve method :

According to docs ,

resolve - {Object.=} - An optional map of dependencies which should be injected into the controller.If any of these dependencies are promises,the router will wait for them all to be resolved or one to be rejected
 before the controller is instantiated.If all the promises are resolved successfully, the values of the resolved promises are injected and $routeChangeSuccess event is fired.If any of the promises are rejected the $routeChangeError event is fired.

To make use of it , lets say we have a boolean variable isCurrentTaskDone defined on $rootScope, which decides whether we should go to the next task.

If the user clicks on the next task , we can check the $rootScope for the boolean value , if true then the factory will return true , and if false the app gets redirected using $location service to /link1.

Here’s the code

App.run(function  ($rootScope) {
	$rootScope.isCurrentTaskDone = false;
});

App.config(function  ($routeProvider,$locationProvider) {
	$routeProvider
	.when("/link1",{
		templateUrl : "js/hello.html" ,
		controller : "ChildCtrl1",
		resolve : {
			factory : function  ($rootScope,$location) {
				if ($rootScope.isCurrentTaskDone) {
					return true;
				}else{
					$location.path('/link2');
					return false;
				}
			}
		}
	})
	.when("/link2",{
		templateUrl : "js/hello2.html",
		controller : "ChildCtrl2"
	})
	.otherwise({
		templateUrl : "js/hello3.html"
	});
	$locationProvider.html5Mode(true);//required to work in plnkr
});

Here’s is the plnkr demo

angular-todolist

Angular-js Todo-list with Sortable Tutorial

 

Aim : We are going to be making a sexy sortable Todo List with Angular-js.

AngularJS is a toolset for building the framework most suited to your application development.

Lets start.You can follow the steps from the github repo for this tutorial.

DEMO

DOWNLOAD

Step 1 : Start with a boilerplate.

I always start a project with a boilerplate to provide a significant jumpstart for faster development .This boilerplate contains bootstrap , jquery and angular libraries ,comments about licenses on the top and script for Google Analytics.

https://github.com/kanakiyajay/angular-js-template

Step 2 : Create a basic Model and View

Lets think about the model to be used for a todo-list.It it a List so an array will be the best option .Each item in the array will have an taskName and a boolean Value whether that particular task is remaining or completed.The boolean Value will bind to a native angular form checkbox .

Here is the todos model :

$scope.todos = [
{ taskName : "Write an Angular js Tutorial for Todo-List" , isDone : false },
{ taskName : "Update jquer.in" , isDone : false },
{ taskName : "Create a brand-new Resume" , isDone : false }
];

Its corresponding html

<ul>;
<li ng-repeat="todo in todos">;<input ng-model="todo.isDone" type="checkbox">; {{todo.taskName}}</li>;
</ul>;

You should always a debug script to understand how the underlying model is changing.

<div class="debug">;
<p class="text-info">;{{ todos | json}}</p>;
</div>;

Now check and uncheck the checkboxes and you will see how the underlying debug changes.

We will also need a function to add a new Todo to the List.Its extremely easy to do that in Angular-js.

html :

<input type="text" ng-model="newTodo">;
<button class="btn btn-default" ng-click="addTodo()">;Add</button>;

js :

$scope.addTodo = function () {
$scope.todos.push({taskName : $scope.newTodo , isDone : false });
$scope.newTodo = "";//Reset the text field.
};

You should now have following screenshot

todolist step1

Following the principles of progressive enhancement , we also add some Css styles for better looking.

.todoName{
	font-size: 20px;
	border-bottom: 1px lightgray solid;
	padding-bottom:5px;
}

#todoAdd {
	margin-left: 40px;
}

#todoField{
	padding-left: 5px;
	font-size: 20px;
	margin: 20px
}

.todoTask{
	margin-bottom: 20px;
}

todolist step 2

Browse the code on github

Step 3 : Add ui-sortable for sortable Lists

We will be using the awesome Sortable component from the Angular UI project.It is a directive that allows you to sort an array using drag and drop.

[Update: We have now included jQuery UI Touch Punch in order to support drag and drop on touch devices]

Note : We will be using the angular-js 1.2 branch

https://github.com/angular-ui/ui-sortable/tree/angular1.2

Add the necessary jquery ui and sortable js files , add ui.sortable as a dependency to the angular js app.

You can pass ui-sortable as a directive and give the options defined in app.js to variable name todoSortable.

  <ul ui-sortable="todoSortable" ng-model="todos">;
$scope.todoSortable = {
	containment : "parent",//Dont let the user drag outside the parent
	cursor : "move",//Change the cursor icon on drag
	tolerance : "pointer"//Read http://api.jqueryui.com/sortable/#option-tolerance
	};
  

Browser the step 3 code on github :-

https://github.com/kanakiyajay/Angular-js-todolist/tree/9bafde5b12d474ab35679163a469f5a0c1c0c95e

Step 4 : Add LocalStorage Support.

You should be able to store all your todos even when you close your browser.

Fortunately html5 has got LocalStorage to your help and Angular js has got a module for that https://github.com/grevory/angular-local-storage which gives you access to browser’s local storage.

Localstorage has got a simple key and value storage system.So we will have to also serialize the array when storage it and parse it when it is obtained again.

Note that we will be using the Angular js method toJson instead of the native to get rid of the unwanted $$haskey added by angular for ng-repeat.

The app should first check whether there already a todoList previously saved and if not give the initial list.Also this should happen on initialization.

Automatic saving the list is achieved by using $scope.$watch on the todos object.

We are storing the new value key “todoList” everytime there is a change in todoList.

html :

<body ng-controller="TodoCtrl" ng-init="init()">;

js :

$scope.init = function  () {
 if (localStorageService.get("todoList")===null) {
  $scope.todos = [
   { taskName : "Create an Angular-js TodoList" , isDone : false }
  ];
 }else{
   $scope.todos = localStorageService.get("todoList");
 }
};

$scope.$watch("todos",function  (newVal,oldVal) {
 if (newVal !== null &amp;&amp; angular.isDefined(newVal) &amp;&amp; newVal!==oldVal) {
  localStorageService.add("todoList",angular.toJson(newVal));
 }
},true);

I have added some additional checks for null ,notDefined.

You can refresh the page and find that all your Todos are saved in the same state.

Step 5: Editable todos and Delete

We should also be able to edit the todos present and then also be able to delete the old todos.We are going to be and edited version of this jsfiddle for the directive for editinplace editing of the task names.

I have updated the directive to use jQuery and enter editing phase when double-clicked.

Here’s the code :

App.directive( 'editInPlace', function() {
  return {
    restrict: 'E',
    scope: { value: '=' },
    template: '<input class="todoField" type="text" />;',
    link: function ( $scope, element, attrs ) {
      // Let's get a reference to the input element, as we'll want to reference it.
      var inputElement = angular.element( element.children()[1] );

      // This directive should have a set class so we can style it.
      element.addClass( 'edit-in-place' );

      // Initially, we're not editing.
      $scope.editing = false;

      // ng-dblclick handler to activate edit-in-place
      $scope.edit = function () {
        $scope.editing = true;

        // We control display through a class on the directive itself. See the CSS.
        element.addClass( 'active' );

        // And we must focus the element.
        // `angular.element()` provides a chainable array, like jQuery so to access a native DOM function,
        // we have to reference the first element in the array.
        inputElement.focus();
      };

      // When we leave the input, we're done editing.
      inputElement.on("blur",function  () {
        $scope.editing = false;
        element.removeClass( 'active' );
      });

    }
  };
});

The delete a todo part is easy because you just need to delete that todo from the model and it will get removed from the view.

$scope.deleteTodo = function (index) {
 $scope.todos.splice(index, 1);
};

Update: It should be deleted by item and not by index.:

$scope.deleteTodo = function (item) {
 var index = $scope.model[$scope.currentShow].list.indexOf(item);
 $scope.model[$scope.currentShow].list.splice(index, 1);
};
<button type="button" aria-hidden="true" ng-click="deleteTodo($index)">;&amp;times;</button>;

Github link for Step 5

Step 6 : Adding Filters for Search and showing Incomplete Tasks

If we have a lot of todos , it becomes difficult to search through them and to find a particular todo.It also becomes easier to see all the completed and incompleted tasks at once.Fortunately angular makes this task easier by the use of filters.Filters will only display a subset of todos.Note that we will also need ng-class directive to give class active according to current show.

Following is the code for the nav-pills :-

<ul class="nav nav-pills todo-filter">;
<li ng-class="{'active' : show == 'All' }" ng-click="show='All'">;<a href="#">;All</a>;</li>;
<li ng-class="{'active' : show == 'Incomplete' }" ng-click="show='Incomplete'">;<a href="#">;Incomplete</a>;</li>;
<li ng-class="{'active' : show == 'Complete' }" ng-click="show='Complete'">;<a href="#">;Complete</a>;</li>;
</ul>;

Here is the filter function

$scope.showFn = function  (todo) {
	if ($scope.show === "All") {
		return true;
	}else if(todo.isDone &amp;&amp; $scope.show === "Complete"){
		return true;
	}else if(!todo.isDone &amp;&amp; $scope.show === "Incomplete"){
		return true;
	}else{
		return false;
	}
};

You can then append this showFn filter function to ng-repeat using |.

<li ng-repeat="todo in todos | filter:showFn ">;

Searching

Searching is much more simpler using the native filter of angular-js.

It implements it automatically.Here’s the code

html :

<div class="input-group">;
<span class="input-group-btn">;
<button class="btn btn-default" type="button">;<span class="glyphicon glyphicon-search">;</span>;</button>;
</span>;
<input type="search" class="form-control" placeholder="Search" ng-model="todoSearch">;
</div>;

You can now append it to the ng-repeat using filter

<li class="todoTask" ng-repeat="todo in todos | filter:showFn | filter :todoSearch ">;

Step 7 : ngEnter

Our next step will be a adding a todo Field when enter is pressed.

As we are doing things the angular way we should have directive in order to capture the enter event on the #newTodoField.

This is the code for the directive for ngEnter

App.directive("ngEnter",function  () {
 return function  (scope,elem) {
  $(elem).keyup(function  (e) {
   //Enter Keycode is 13
   if (e.keyCode === 13) {
    /*Also update the Angular Cycle*/
    scope.$apply(function  () {
     scope.addTodo();//Call addTodo defined inside controller
    });
   }
  });
 };
});

Step 8 : Adding Multiple Todos

The next step will be the ability to add multiple todos.Fox example we might have a list that is used only for grocery lists , the other might be a list of planned blog posts or a list of the things to do today.Thats why its important to have ability to sort the todos in different lists.

For this to work to work seamlessly we will have delete the localStorage cache as we will be changing the model too.

In Chrome Devtools , Go to the Resources Tab , then to LocalStorage , then to url ->;.On the right side you will have all the Keys and values.

Click on the ls.todoList and press delete.

The model will be a list i.e. an array . Each element of the array will be a object having a key for name and list.The list value is an array of objects containing our todos similar to our previous todos.

Here is the js code

$scope.model = [
 {
  name : "Primary" , list : [
   { taskName : "Create an Angular-js TodoList" , isDone : false },
   { taskName : "Understanding Angular-js Directives" , isDone : true }
  ]
 },
 {
  name : "Secondary" , list : [
   { taskName : "Build an open-source website builder" , isDone : false },
   { taskName : "BUild an Email Builder" , isDone : false }
  ]
 }
];

currentShow is the variable which will store the current todos being shown.
On clicking a particular list we will change the currentShow to $index of the todos-name.
The todos-list should also update according to currentShow .Doing it the angular-way we will show all the todos using ng-repeat and then hide those that we don’t wanna display using ng-show.

According to the model , the view will change :

We are using the list-group-item class of bootstrap.
on clicking we want the current
html for the todos-names:

<div class="list-group">;
<a href="#" ng-repeat="todos in model" class="list-group-item" ng-class="{'active' : currentShow === $index}" ng-click="changeTodo($index)" >;
<span class="badge">;{{todos.list.length}}</span>;
{{todos.name}}
</a>;
</div>;

html for the todos lists :

<ul class="list-unstyled" ng-repeat="todos in model track by $index" ui-sortable="todoSortable" ng-model="todos.list" ng-show="$index === currentShow">
<li class="todoTask" ng-repeat="todo in todos.list | filter:showFn | filter :todoSearch ">;
</ul>

Update 20th Oct 2014:

ng-model should be not be todos because it should refer to the new array, the new ng-model should be todos.list
Thanks Alex !

Update 21st June 2015:

- Included jQuery UI Touch Punch to support touch devices
- deleteTodo() should be by item and not by index

App.js Code :-

(I have not used inline ng-click code because we will be requiring the func changeTodo later on.)

//addTodo()
$scope.model[$scope.currentShow].list.splice(0,0,{taskName : $scope.newTodo , isDone : false });
//deleteTodo()
$scope.model[$scope.currentShow].list.splice(index, 1);
//changeTodo(i)
$scope.currentShow = i;

github commit