Обновлять:

Оригинал опубликован в марте 2018 года, когда я поддерживал проект Angular, он все еще может быть полезен для тех, кто не поддерживает проекты React.

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

Используйте controllerAs вместо $scope для директив

По умолчанию все функции в directive подключаются к $scope. Пока функции находятся внутри области directive в HTML, приложение будет работать.

// sample.js
var sample = () => {
  return {
    controller: sampleCtrl,
    restrict : 'A',
    scope: false
  };
};

sampleCtrl.$inject = ['$scope'];

function sampleCtrl($scope) {
  $scope.acc = 0;
  $scope.add = () => {
    $scope.acc++;
  };
}

// app.js
var app = angular.module('app', []);
app.directive('sample', sample);
<!DOCTYPE html>
<html>
<body>
<div ng-app="app">
  <div sample>
    <button ng-click="add()">click here</button>
    <h1>here goes {{acc}}</h1>
  </div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
</body>
</html>

Это разумное решение, когда нам нужно поставить только простые и понятные компоненты. Проблемы возникнут, когда мы добавим дочерний компонент

// sample.js
var sample = () => {
  return {
    controller: sampleCtrl,
    restrict : 'A',
    scope: false
  };
};

sampleCtrl.$inject = ['$scope'];

function sampleCtrl($scope) {
  $scope.acc = 0;
  $scope.add = () => {
    $scope.acc++;
  };
}
// word.js
var word = () => {
  return {
    controller: wordCtrl,
    restrict : 'A',
    scope: true
  };
};

function wordCtrl($scope) {
  $scope.text = 'here';
  $scope.update = () => {
    $scope.text = $scope.text === 'here' ? 'there' : 'here';
  };
}
// app.js
var app = angular.module('app', []);
app.directive('sample', sample)
.directive('word', word);
<!DOCTYPE html>
<html>
<body>
<div ng-app="app">
  <div sample>
    <button ng-click="add()">+1</button>
    <div word>
      <button ng-click="update()">switch</button>
      <h1>{{text}} goes {{acc}}</h1>
    </div>
  </div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<script src="index.js"></script>
</body>
</html>

Теперь сложнее найти, где находятся функции add() и update(), так как границы sample и word не ясны.

Чтобы сделать это более удобным для чтения и сопровождения, мы могли бы ввести controllerAs

// sample.js
var sample = () => {
  return {
    controller: sampleCtrl,
    controllerAs: 'sampleCtrl',
    restrict : 'A',
    scope: false
  };
};

sampleCtrl.$inject = ['$scope'];

function sampleCtrl($scope) {
  let ctrl = angular.extend(this, {});
  $scope.acc = 0;
  ctrl.add = () => {
    $scope.acc++;
  };
}

// word.js
var word = () => {
  return {
    controller: wordCtrl,
    controllerAs: 'wordCtrl',
    restrict : 'A',
    scope: true
  };
};

function wordCtrl($scope) {
  let ctrl = angular.extend(this, {});
  $scope.text = 'here';
  ctrl.update = () => {
    $scope.text = $scope.text === 'here' ? 'there' : 'here';
  };
}

//app.js
var app = angular.module('app', []);
app.directive('sample', sample)
.directive('word', word);
<!DOCTYPE html>
<html>
<body>
<div ng-app="app">
  <div sample>
    <button ng-click="sampleCtrl.add()">+1</button>
    <div word>
      <button ng-click="wordCtrl.update()">switch</button>
      <h1>{{text}} goes {{acc}}</h1>
    </div>
  </div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<script src="index.js"></script>
</body>
</html>

С этим обновлением проще найти функцию, которую вы хотите обновить, по имени контроллера.

Состояние ведения журнала, например, реакция

Теперь пришло время улучшить читаемость раздела рендеринга => {{text}} и {{acc}}. используя Инспектор Ng, мы можем проверить данные с живой страницы.

Это эквивалент «инструмента разработчика React» для React. Говоря о React, я обнаружил, что this.state в реакции — это гораздо лучший способ регистрации угловых изменений, поэтому давайте реализуем это для angular 1.

// sample.js
var sample = () => {
  return {
    controller: sampleCtrl,
    controllerAs: 'sampleCtrl',
    restrict : 'A',
    scope: false
  };
};

function sampleCtrl() {
  let ctrl = angular.extend(this, {});
  ctrl.state = {
    acc: 0
  };
  ctrl.add = () => {
    ctrl.state.acc++;
  };
}

// word.js
var word = () => {
  return {
    controller: wordCtrl,
    controllerAs: 'wordCtrl',
    restrict : 'A',
    scope: true
  };
};

function wordCtrl() {
  let ctrl = angular.extend(this, {});
  ctrl.state = {
    text: 'here'
  };
  ctrl.update = () => {
    ctrl.state.text = ctrl.state.text === 'here' ? 'there' : 'here';
  };
}

//app.js
var app = angular.module('app', []);
app.directive('sample', sample)
.directive('word', word);
<!DOCTYPE html>
<html>
<body>
<div ng-app="app">
  <div sample>
    <button ng-click="sampleCtrl.add()">+1</button>
    <div word>
      <button ng-click="wordCtrl.update()">switch</button>
      <h1>{{wordCtrl.state.text}} goes {{sampleCtrl.state.acc}}</h1>
    </div>
  </div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<script src="index.js"></script>
</body>
</html>

С ng-инспекцией и состоянием наш код теперь является самостоятельным руководством для поддержки директив в angular 1.

Реализовать презентационную и контейнерную структуру

Вещи могут быть более сложными, чем это; скажем, вместо here goes скажем here comes. Настало время разобраться с директивными функциями с функциями представления и контейнерами.

Презентационные функции

  • Беспокоится о том, как все выглядит.
  • Не иметь зависимостей от остальной части приложения
  • Не указывайте, как данные загружаются или изменяются.
  • Являются чистыми функциями

Контейнеры

  • Беспокоятся о том, как все работает.
  • Предоставьте данные и поведение презентационным или другим компонентам контейнера.
  • Часто имеют состояние, поскольку они, как правило, служат источниками данных.

Теперь наши презентационные функции являются чистыми функциями, поэтому, когда у нас есть ошибка в рендеринге и state данные верны, это должны быть ошибки в презентационных функциях. С другой стороны, когда state данные неверны, мы сможем найти ошибки в контейнерах.

// word.js
var word = () => {
  return {
    controller: wordCtrl,
    controllerAs: 'wordCtrl',
    restrict : 'A',
    scope: true
  };
};

function wordCtrl() {
  let ctrl = angular.extend(this, {});
  // Presentational
  ctrl.wording = (acc, text = ctrl.state.text) => {
    return text === 'here' ? 'here comes ' + acc : 'there goes ' + acc;
  };

  // Container
  ctrl.state = {
    text: 'here'
  };
  ctrl.update = () => {
    ctrl.state.text = ctrl.state.text === 'here' ? 'there' : 'here';
  };
}
<!DOCTYPE html>
<html>
<body>
<div ng-app="app">
  <div sample>
    <button ng-click="sampleCtrl.add()">+1</button>
    <div word>
      <button ng-click="wordCtrl.update()">switch</button>
      <h1>{{wordCtrl.wording(sampleCtrl.state.acc)}}</h1>
    </div>
  </div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<script src="index.js"></script>
</body>
</html>

Вывод

С controllerAs, state ведением журнала и сортировкой «функции представления и контейнера» у нас есть кодовая база с большей уверенностью в поддержке угловой.

Первоначально опубликовано на http://github.com.