Angular.js: Os controladores capturam a ordem ($ onChanges chamado antes de $ onInit)

Criado em 16 dez. 2016  ·  9Comentários  ·  Fonte: angular/angular.js

Você quer solicitar um recurso ou relatar um bug ?
Não sei se é desejado ou se é um bug

Qual é o comportamento atual?
A primeira $onChanges chamada é feita antes de $onInit one.
Com a configuração padrão do angular 1.6 (preassign = false), é possível inicializar os estados do controlador na função $onInit , visto que as ligações ainda não estão acessíveis no construtor.

Qual é o comportamento esperado?
Acho que seria mais lógico chamar $onInit primeiro.
O Angular não deve fazer nada enquanto o controlador não estiver totalmente inicializado.

Qual é a motivação / caso de uso para mudar o comportamento?
Se usarmos objetos que devem ser criados na função $onInit na função $onChanges ; teremos um erro na primeira chamada, pois o controlador ainda não foi inicializado.
Uma solução poderia ser inicializar esses objetos dentro do construtor, mas vamos inicializar o controlador em dois lugares diferentes ...

Quais versões do Angular e quais navegador / sistema operacional são afetados por esse problema? Teste também com as versões mais recentes estáveis ​​e instantâneas (https://code.angularjs.org/snapshot/).
versão angular 1.6

works as expected

Comentários muito úteis

Isso é pretendido (principalmente para corresponder ao comportamento do Angular 2+). Caso você tenha perdido, você pode verificar se é a primeira chamada (pré- $ onInit) de $onChanges , verificando o valor de retorno do método isFirstChange() de qualquer um dos SimpleChange objetos:

{
  ...
  bindings: {foo: '<'},
  controller: function SomeController() {
    this.$onChanges = function(changes) {
      if (changes.foo.isFirstChange()) {
        // `$onInit()` has not been called yet...
      }
    };
  }
}

#

Admito que minha primeira reação foi a mesma. Do ponto de vista do modelo mental, é mais fácil pensar que $onInit() é a primeira coisa que acontece no controlador; depois $onChanges() , $onChanges() , $onChanges() e finalmente $onDestroy() .

Mas se você pensar sobre isso, as ligações precisam ser avaliadas e atribuídas à instância do controlador antes de chamar $onInit . E ao fazer isso, uma alteração nos valores (anteriormente indefinidos) é detectada, que por sua vez precisa ser relatada por meio de $onChanges .

Fechando, já que está funcionando conforme o esperado (ou pelo menos como pretendido: wink :).

Todos 9 comentários

Isso é pretendido (principalmente para corresponder ao comportamento do Angular 2+). Caso você tenha perdido, você pode verificar se é a primeira chamada (pré- $ onInit) de $onChanges , verificando o valor de retorno do método isFirstChange() de qualquer um dos SimpleChange objetos:

{
  ...
  bindings: {foo: '<'},
  controller: function SomeController() {
    this.$onChanges = function(changes) {
      if (changes.foo.isFirstChange()) {
        // `$onInit()` has not been called yet...
      }
    };
  }
}

#

Admito que minha primeira reação foi a mesma. Do ponto de vista do modelo mental, é mais fácil pensar que $onInit() é a primeira coisa que acontece no controlador; depois $onChanges() , $onChanges() , $onChanges() e finalmente $onDestroy() .

Mas se você pensar sobre isso, as ligações precisam ser avaliadas e atribuídas à instância do controlador antes de chamar $onInit . E ao fazer isso, uma alteração nos valores (anteriormente indefinidos) é detectada, que por sua vez precisa ser relatada por meio de $onChanges .

Fechando, já que está funcionando conforme o esperado (ou pelo menos como pretendido: wink :).

Muito obrigado pela sua resposta, está mais claro agora :)

Eu entendo que isso é intencional, que tal registrarmos a definição da função $ onChanges () dentro de $ onInit ().

@bharatpatil , deve funcionar em AngularJS (1.x.), mas não em Angular (2+).

if (changes.foo.isFirstChange ()) {
// $onInit() ainda não foi chamado ...
}

@gkalpak Acho que esse argumento está errado. binding.isFirstChange() método controller.$onInit deve ser chamado antes do método controller.$onChanges . Estou errado?

Uma associação sempre tem um valor no início (pode ser undefined ), que é sempre diferente de seu valor pré-inicializado. Assim, $onChanges() sempre será chamado com changes.foo antes de chamar $onInit() .

Não faz sentido lógico que uma associação possa ter um valor antes de ser inicializada. Essa é a definição do dicionário para a palavra inicializar (definir valores iniciais). Você não pode mudar algo se não tiver um valor inicial. Acho que o nome está errado aqui e não é natural.

Não me lembro de uma situação em que não fui forçado a escrever algo como este código:

$onChanges(changesObj: { [index: string]: angular.IChangesObject; })  {
   if (!this.isInitialized) return;
   ...
}

Chamar "alterações" antes da "inicialização" não faz sentido.

Par para o curso com Angular ... por que alguém esperaria que o init acontecesse antes das mudanças?

Esta página foi útil?
0 / 5 - 0 avaliações