С большой силой приходит и большая ответственность — техника безопасности в AngularJS

от автора

image

Несомненно, ангуляр даёт вам силу. Но распределяться ей нужно с умом. Я постарался сформулировать три простых правила, которые я много раз нарушал и страдал от этого.

1. Делайте копию объекта, если он может подвергнуться нежелательным изменениям.

В ангуляре данные по умолчанию едины, и если вы измените их намеренно, случайно или в результате ошибки, под угрозой окажутся все места его использования. Например, у нас есть фабрика пустых сущностей, которые будут использованы для заполнения форм.

module.factory('emptyEntity', function() { 	var emptyObject = { 		name:"", 		surname:"", 		address:{ 			city:"" 			street:"", 		} 	};      return {         createEmptyEntity: function(){             return emptyObject;         }     };  }); 

И далее, в контроллере формы мы создаём в $scope этот пустой объект и используем его как модель формы.

   $scope.model = mapper.createEmptyPetition(); 

Что будет, если при вводе формы эта модель изменится, а потом вызвать mapper.createEmptyPetition() снова для другой формы? Так, как везде используется один и тот же экземпляр объекта emptyObject, изменения будут отражены в нём, и при следующем вызове mapper.createEmptyPetition() мы получим грязный и использованный объект. Подобных моментов при разработке может возникать великое множество, и нужно осторожно относиться к раздаче ссылок на объекты направо и налево. В данном случае следовало бы сделать вот так — возвращать копию объекта, чтобы её изменения не касались оригинального объекта:

 createEmptyEntity: function(){      return angular.copy(emptyObject);  } 

2. Не теряйте ссылку на объект/массив, если не хотите потерять синхронизацию данных

Простой пример.
У нас есть контроллер, в $scope которого лежит массив, и есть функция для очищения массива:

module.controller("NewPetitionController", ['$scope', function($scope) {         $scope.myArray = [1,2,3,4];          $scope.cleanArray = function(){              $scope.myArray = [];        }     } ]); 

И где-то во вьюшке вы отдаёте массив в какую-нибудь директиву, например, которая его отрисует.

<div my-array-viewer array="myArray"></div> 

Что будет, если вызвать функцию cleanArray? Директива спокойно продолжит отображать старый добрый полный массив, потому что у неё осталась ссылка на него. А кодом "$scope.myArray = []" мы только создали новый массив и записали ссылку на него в свойство myArray, на что директиве my-array-viewer абсолютно параллельно. Чтобы занулить массив, не потеряв на него ссылку, нужно просто вызвать $scope.myArray.length = 0;
То же касается объектов. Нельзя просто взять и присвоить переменной новый объект, нужно изменить старый, чтобы остальные части прилоежния, имеющие ссылку на этот объект, не потеряли её.

module.controller("NewPetitionController", ['$scope', function($scope) {         $scope.myObj = {foo: "bar"};          $scope.setObj = function(newObj){              //$scope.myObj = newObj; //Так делать нельзя, это приведёт к утере ссылки              angular.extend($scope.myObj, newObj); //нужно вот так, чтобы изменился исходный объект        }     } ]); 

3. Будьте внимательны с дочерними $scope

Многие директивы, такие как ng-if, ng-include создают дочерний $scope. Что это значит? У этих директив будет создан новый экземпляр $scope, в свойстве prototype которого будет родительский скоуп — стандартной javascript-наследование. Из этого следует, что изменение простых свойств (string, number, boolean etc.) в дочернем скоупе НЕ БУДЕТ затрагивать родительский скоуп, так как простые свойства при наследовании копируются. В отличие от них, объекты при прототипном наследовании передаются ссылками, поэтому изменение свойств объектов будет отображаться в родительском скоупе.
Поэтому так делать не следует, это не будет работать:

<div ng-if="true">    <a ng-click="showSecondBlock = true">Показать второй блок</a> </div>  <div ng-if="showSecondBlock"> Второй блок отображается! </div> 

Вместо этого, нужно иметь для таких дел специальный объект в $scope, назовём его viewModel

app.controller('MainCtrl', function($scope) {   $scope.viewModel = {}; }); 

<div ng-if="true">    <a ng-click="viewModel.showSecondBlock = true">Показать второй блок</a> </div>  <div ng-if="viewModel.showSecondBlock"> Второй блок отображается! </div> 

ссылка на оригинал статьи http://habrahabr.ru/post/230761/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *