AngularJS – Good Practices



AngularJS – Good Practices

0 3


angularjs-good-practices

AngularJS Good Practices

On Github mgonto / angularjs-good-practices

AngularJS

Good Practices

Created by Martin Gontovnikas / @mgonto

Let's understand how this works

Page Structure

We should divide pages into several controllers and directives

Each controller should have only ONE main functionality

We should leverage directives for reusable code

Controllers

The objective is to react to user inputs and watch models for changes

Processing or working on models should be done either in Services or transformer filters

Each controller can use models and variables from the parent controller

If a controller accesses to a DOM element, you're doing things wrong

ui-router for Controller and page structure

						$stateProvider
.state('main.alerts.buildings', {
    url: '/buildings',
    templateUrl: '/js/frontend/app/mescenter/controllers/alerts/buildings/alertBuildings.html',
    controller: 'AlertBuildingsCtrl'
})
.state('main.alerts.buildings.types', {
    url: '/:buildingId/types',
    templateUrl: '/js/frontend/app/mescenter/controllers/alerts/types/alertTypes.html',
    controller: 'AlertTypesCtrl'
})
.state('main.alerts.buildings.types.meters', {
    url: '/meters',
    templateUrl: '/js/frontend/app/mescenter/controllers/alerts/meters/alertMeters.html',
    controller: 'AlertMetersCtrl'
})
					

AlertTypesCtrl inherits from AlertBuildingCtrl

Services

Services are singleton. Once instantiated, the same is always returned.

Services should almost never access DOM values

Services should aid controller with Logic to be run over models

module.provider('helloService', function() {
	
	var name = "Default Name";
	this.setName = function(nameParam) {
		name = nameParam;
	}
	
	this.$get = function() {
		var service = {};
		service.sayHello = function() {
			console.log("Hello " + name);
		}
	};
})
					

Using filters

Filters should be used for converting what needs to be displayed in screen

They can receive additional parameters and they can be chained

{{price | numberFormat:'$0.00'}}
module.filter('numberFormat', function() {
	return function(input, format) {
		// Format is $0.00
	}
})
					

Models

Angular gives us liberty with Models. They are just simple JS object

Most of the time, our models will be either returned from the server or a regular JS object

We can share models by using Controller's scopes prototipal inheritance

Or we can create a factory for a model if we need it to be everywhere

$scope

$scope is the ViewModel of our app. It's the glue between the controller and the model

We should ONLY put in the scope the things that are needed to be used in the view (HTML) or that we need to watch, nothing else

We should NEVER call $parent. If we need something from our parent, we either inherit it from Parent Controller or we receive it as parameter in our directive

The dot rule

Controllers use prototipal inheritance

Using primitives as model ($scope.number = 3) doesn't work well with Prototipal inheritance

With primitive change in child, reference is lost in parent. No more bidirectional connection

$watch

A watch is always called a first time to initialize. That's why undefined checking is needed

						var myObject = {
   name: 'Gonto'
};

$scope.$watch('myObject', function() {
  // Watcher for object reference change
});

$scope.$watch('myObject', function() {
  // Watcher for object equality change
}, true);

$scope.$watch('[myObject, myObject2]', function() {
  // Watcher for 2 different objects for any change
}, true);					
				

When to use $apply?

$apply is called by AngularJS in ng-click, ng-keydown, etc.

$apply should be called manually when an event not handled by AngularJS is received

$apply should never be called without a function parameter

If you're checking for $$phase running, you're doing it wrong

$element.bind('plotselected', function(e) {
    var stuff = 123;
    $scope.$apply(function() {
    	$scope.selectedValue = e.ranges;
    });
});
					

ngModel ain't just a word

Angular provides an ngModelController when using ng-model directive

ngModelController provides all this neat stuff

It lets us set formatters and parsers to transform results and to set validity or not of the current value

module.directive('userSelector', function() {
    return {
        restrict : 'E',
        replace: true,
        require: 'ngModel',
        templateUrl: '/js/frontend/app/mescenter/directives/userSelector/userSelector.html',
        scope: {user: '=ngModel'},
        link: function ($scope, $element, $attrs, ngModelController) {
            ngModelController.$parsers.push(function(value) {
            	return value.toLowerCase();
            });
        }
    }
});
					

Form validation

Giving the form a name allows us to use AngularJS validations

You can check documentation by clicking here

We can use existing validations like `required` or we can create our own based on ngModel.$setValidity

<form name="myForm" ng-controller="Ctrl">
  <label>User Type</label><input name="input" ng-model="userType" required="">
  <span class="error" ng-show="myForm.input.$error.required">Required!</span>
  <input type="submit" ng-click="submit()" ng-disabled="!myForm.$dirty || !myForm.$valid">
</form>
					

THE END

By Martin Gontovnikas / @mgonto