-Джон Бойд

Первоначально размещенный на https://medium.com/@unbreakabler, разработчик Biznas Джон Бойд делится своим опытом перехода с Angular 1.x на Angular 2, а также проблемами порядка и времени инициализации, с которыми он столкнулся.

В настоящее время мы находимся в процессе преобразования большого (сотни экранов) приложения Angular 1.x в Angular 2. В ходе этого процесса мы столкнулись со многими проблемами, которых не ожидали.

Просмотр документации дает вам простые примеры того, как использовать upgradeAdapter. Все наши разработчики прочитали его, и мы подумали, что хорошо понимаем, как будет проходить процесс.

Это было не так просто, как мы ожидали.

Мы начали с загрузки нашего приложения с помощью Angular 2. Хорошо документированная и простая вещь для работы. Затем мы рассмотрели, что имеет смысл делать дальше, и решили полностью преобразовать наши сервисы Angular 1 в сервисы Angular 2, это было бы намного проще, чем преобразование компонентов/директив, и нам показалось, что это хорошее место для начала.

Здесь мы столкнулись с нашим первым серьезным препятствием, нам пришлось преодолеть проблемы с порядком и временем инициализации. Внедрение зависимостей в компоненты/сервисы Angular 1 и Angular 2 вовсе не было простым.

В частности, мы преобразовали все наши сервисы из сервисов javascript Angular 1.5 в сервисы Typescript Angular 2. Это привело к тому, что мы столкнулись с проблемами, которые редко упоминаются в чьем-либо процессе преобразования:

Инжектор Angular 1 $ готов раньше, чем инжектор Angular 2. Это означает, что любые компоненты/службы/директивы, написанные на Angular 1 и зависящие от службы Angular 2, потерпят неудачу, если они вызовут эту службу Angular 2 в первом цикле приложения.

Скорее всего, вы увидите такую ​​ошибку:

Cannot read property 'injector' of null

Эта ошибка означает, что инжектор не был готов к службе, когда он использовался для внедрения зависимостей при первоначальной настройке приложения. В зависимости от того, где и как вы используете службу Angular 2, вы не всегда можете получить такую ​​​​идентифицируемую ошибку, как эта, возможно, внедренная зависимость на самом деле не вернет ошибку и будет просто неопределенной переменной.

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

function invoke(fn, self, locals, serviceName) {
  if (typeof locals === 'string') {
    serviceName = locals;
    locals = null;
  }
var args = injectionArgs(fn, locals, serviceName);
  if (isArray(fn)) {
    fn = fn[fn.length - 1];
  }
if (!isClass(fn)) {
    // http://jsperf.com/angularjs-invoke-apply-vs-switch
    // #5388
    try {
      // if (path[0] === 'cadAlerts')
      //   console.warn(path.join(' <- '));
      return fn.apply(self, args);
    } catch (err) {
      if (path.length && path[0] !== 'ng2.Injector') {
        console.error(err.toString() + " --- " + path.join(' <- '));
      }
      return {};
    }
  } else {
    args.unshift(null);
    return new (Function.prototype.bind.apply(fn, args))();
  }
}

В сочетании с этим протоколированием ошибок try/catch мы добавили сообщение в @angular/upgrade/src/upgrade_adapter.js, чтобы показать, когда инжектор Angular 2 готов.

@ строка 406 console.warn('NG2 INJECTOR READY');

С помощью этого журнала вы сможете легко увидеть, какие зависимости пытаются внедрить до того, как инжектор Angular 2 будет готов, отследить их и легко внести изменения, необходимые для их функционирования.

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

Вместо :

function ng1Controller(ng2Service) {
  ng2Service.call();
};
ng1Controller.$inject =['ng2Service'];

У вас будет:

function ng1Controller($injector) {
  var ng2Service;
  setTimeout(function() {
    ng2Service = $injector.get('ng2Service');
    ng2Service.call();
  }
}
ng1Controller.$inject =['$injector'];

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

Например, если у вас есть какая-то функция инициализации, которая автоматически срабатывает в контроллере или, возможно, вызывается из внешнего контекста (например, .run()), служба будет недоступна.

function ng1Controller($injector) {
  var ng2Service;
  setTimeout(function() {
    ng2Service = $injector.get('ng2Service');
  }
init();
function init() {
    ng2Service.initialize();
  }
}
ng1Controller.$inject =['$injector'];

Становится:

function ng1Controller($injector) {
  var ng2Service;
  setTimeout(function() {
    ng2Service = $injector.get('ng2Service');
    init();
  }
function init() {
    ng2Service.initialize();
  }
}
ng1Controller.$inject =['$injector'];

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

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

Решение этого было точно таким же, как я показал выше. Мы завернули каждое из наших решений в тайм-аут и вернули его.

resolve: {
  ng2: /*@ngInject*/ function (api) {
    return api.useNg2Service();
  }
}

Становится:

resolve: {
  ng2: /*@ngInject*/ function(api, $timeout) {
    return $timeout(function() {
      return api.useNg2Service();
    })  
  }
}

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

Следуйте за нами, чтобы узнать больше о разработке Angular 2.

Biznas.io
Biznas в Twitter

Biznas в Facebook
Biznas в LinkedIn