The scope is not the model itself—it's just a way to reach it. Thus, the view and controller layers are absolutely free to share any kind of information, even those that are not related to the model, and they only exist to fulfill specific layout matters such as showing or hiding a field under a determined condition.
Be careful about falling into a design trap! The freedom provided by the scope can lead you to use it in a wrong way. Keep the following advice in mind:
"Treat scope as read-only inside the view and write-only inside the controller as possible."
Also, we will go through some important advice about using the scope:Avoid making changes to the scope directly from the view This means that though it is easy, we should avoid making changes to the scope by creating or modifying its properties directly inside the view. At the same time, we need to take care about reading the scope directly everywhere inside the controller.
The following is an example from the
faq.html file where we can understand these concepts in more detail:
snippet
<button ng-click="faq = true">Open</button>
<div ng-modal="faq">
<div class="header">
<h4>FAQ</h4>
</div>
<div class="body">
<p>You are in the Frequently Asked Questions!</p>
</div>
<div class="footer">
<button ng-click="faq = false">Close</button>
</div>
</div>
In the previous example, we changed the value of the dialog property directly from the ngClick directive declaration. The best choice in this case would be to delegate this intention to the controller and let it control the state of the dialog, such as the following code in the
faq.html file:
snippet
<button ng-click="openFAQ()">Open</button>
<div ng-modal="faq">
<div class="header">
<h4>FAQ</h4>
</div>
<div class="body">
<p>You are in the Frequently Asked Questions!</p>
</div>
<div class="footer">
<button ng-click="closeFAQ()">Close</button>
</div>
</div>
Consider the following code snippet in the
controllers.js file:
snippet
parking.controller("faqCtrl", function($scope) {
$scope.faq = false;
$scope.openFAQ = function() {
$scope.faq = true;
}
$scope.closeFAQ = function() {
$scope.faq = false;
}
});
The idea to spread a variable across the whole view is definitely dangerous. It contributes to reducing the flexibility of the code and also increases the coupling between the view and the controller.
Avoid reading the scope inside the controller Reading the $scope object inside the controller instead of passing data through parameters should be avoided. This increases the couple between them and makes the controller much harder to test. In the following code snippet of the login.html file, we will call the login function and access its parameters directly from the $scope object:
snippet
<div ng-controller="loginCtrl">
<input type="text" ng-model="username" placeholder="Username" />
<input type="password" ng-model="password" placeholder="Password" />
<button ng-click="login()">Login</button>
</div>
Consider the following code snippet in the controllers.js file:
snippet
parking.controller("loginCtrl", function($scope, loginService) {
$scope.login = function() {
loginService.login($scope.username, $scope.password);
}
});
Do not let the scope cross the boundary of its controller We should also take care about not allowing the $scope object to be used far a way from the controller's boundary. In the following code snippet from the
login. html file, there is a situation where loginCtrl is sharing the $scope object with loginService.
snippet
<div ng-controller="loginCtrl">
<input type="text" ng-model="username" placeholder="Username" />
<input type="password" ng-model="password" placeholder="Password" />
<button ng-click="login()">Login</button>
</div>
Consider the following code snippet in the
controllers.js file:
snippet
parking.controller("loginCtrl", function($scope, loginService) {
$scope.login = function() {
loginService.login($scope);
}
});
Consider the following code snippet in the
services.js file:
snippet
parking.factory("loginService", function($http) {
var _login = function($scope) {
var user = {
username: $scope.username,
password: $scope.password
};
return $http.post('/login', user);
};
return {
login: _login
};
});
Use a '.' inside the ngModel directive
The framework has the ability to create an object automatically when we introduce a period in the middle of the ngModel directive. Without that, we ourselves would need to create the object every time by writing much more code. In the following code snippet of the
login.html file, we will create an object called user and also define two properties, username and password.
snippet
<div ng-controller="loginCtrl">
<input type="text" ng-model="username" placeholder="Username" />
<input type="password" ng-model="password" placeholder="Password" />
<button ng-click="login()">Login</button>
</div>
Consider the following code snippet of the
controllers.js file:
snippet
parking.controller("loginCtrl", function($scope, loginService) {
$scope.login = function(user) {
loginService.login(user);
}
});
Consider the following code snippet of the services.js file:
services.js
snippet
parking.factory("loginService", function($http) {
var _login = function(user) {
return $http.post('/login', user);
};
return {
login: _login
};
});
The login method will be invoked just by creating a user object, which is not coupled with the
$scope
object anymore. Avoid using scope unnecessarily. The framework keeps the view and the controller synchronized using the two-way data binding mechanism. Because of this, we are able to increase the performance of our application by reducing the number of things attached to
$scope
. We should use
$scope
only when there are things to be shared with the view; otherwise, we can use a local variable to do the job.