Angular Обнаружение изменений не работает с ChangeDetectionStrategy.OnPu sh в HttpCl inet. Подписка - PullRequest
3 голосов
/ 11 июля 2020

Я воспроизвел простой stackblitz , демонстрирующий проблему, с которой я столкнулся. Проблема в том, что у меня есть родительский компонент, который передает логическое значение дочернему компоненту. Это логическое значение - @Input дочернего компонента. Важно отметить, что родительский компонент использует ChangeDetectionStrategy.OnPu sh. Для дочернего компонента это явно не установлено.

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

Однако, когда я изменяю логическое свойство ввода дочернего компонента вне метода подписки, дочерний компонент, который правильно обнаруживает изменение, и все работает должным образом (1 щелчок, чтобы дочерний компонент распознал изменение).

App.Component.ts (Parent Component)

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AppComponent  {
  constructor(private http: HttpClient) {

  }
  public isHelloVisible: boolean;
  public useHttpGet: boolean;

  showHello() {
    if (this.useHttpGet) {
    this.http.get('https://cors-anywhere.herokuapp.com/https://api.darksky.net/forecast/cc0e3799790b0b34bdeb6fef28c3daf7/17.447409200000003,-78.3724573?units=si').subscribe(data => {
      this.isHelloVisible = true;  
    });
    } else {
      this.isHelloVisible = true;
    }
  }

  closeHello() {
    this.isHelloVisible = false;    
  }

Child Component ( Hello.component.ts)

@Component({
  selector: 'hello',
  template: `<div *ngIf="showHello">
    Hello
    <div (click)="closeHello()">Click me to close Hello</div>
  </div>
  
  `,
  styles: []
})
export class HelloComponent  {
  @Input() showHello: boolean;
  @Output() close: EventEmitter<any> = new EventEmitter();

  ngOnChanges(changes: SimpleChanges): void {
    console.log(this.showHello);
  }

  closeHello() {
    this.close.emit(null);
  }

если useHttpGet истинно, он не работает, а если false, все работает.

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

Вероятно, лучший способ увидеть это в действии - это проследить демонстрацию stackblitz.

Ответы [ 3 ]

1 голос
/ 04 сентября 2020

Для родительского элемента установлено значение OnPu sh, что означает, что он запускает обнаружение изменений только при определенных обстоятельствах:

  1. Ссылка на вход изменяется. У вас нет @Input в родительском
  2. Выполнить обнаружение изменений явно. Вы этого не делаете.
  3. Если наблюдаемый объект, связанный с шаблоном через асинхронный канал c, выдает новое значение. Нет признаков асинхронного c конвейера.
  4. Событие возникло из компонента или одного из его дочерних элементов. Вы делаете это, щелкая.

Итак, когда вы используете http-вызов и нажимаете кнопку, он запускает запрос и запускает обнаружение изменений. Но вызов api асинхронный, это требует времени. К тому времени, когда он вернется, обнаружение изменений уже запущено и не будет запускаться снова, потому что ни один из 4 критериев выше не соблюден.

Если вы не используете http-вызов, вы нажимаете кнопку изменяет значение на true и запускает обнаружение изменений. Все синхронно, поэтому представление обновляется сразу.

Итак, ваш пример не имеет ничего общего с дочерним компонентом. Вы можете изменить теги hello на обычный div и вставить несколько букв между div, и вы увидите такое же поведение.

Я обновил ваш StackBlitz с более реактивным подходом, используя asyn c труба.

0 голосов
/ 11 июля 2020

При изменении u ChangeDetectionStrategy.OnPush Компоненты обнаруживают изменения только при изменении свойства @input.

в методе подписки вы просто меняете внутреннее состояние компонента. Каким-то образом вы должны указать angular для обнаружения изменений, поэтому используйте ChangeDetectorRef.markforcheck ()

для получения дополнительной информации

и другие вещи использовать BehaviorSubject и asyn c pipe для обнаружения изменений.

, но в этом случае лучше использовать первый. это просто

0 голосов
/ 11 июля 2020

В случае использования onPu sh вы должны переключиться на использование BehaviorSubject, это происходит потому, что ваш родительский компонент также имеет onPu sh, поэтому вы должны использовать Subject или вручную вызывать detectChanges

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AppComponent  {
  constructor(private http: HttpClient) {

  }
  public isHelloVisibleSubject: BehaviorSubject<boolean> = new BehaviorSubject(false);
  public useHttpGet: boolean;

  showHello() {
    if (this.useHttpGet) {
    this.http.get('https://cors-anywhere.herokuapp.com/https://api.darksky.net/forecast/cc0e3799790b0b34bdeb6fef28c3daf7/17.447409200000003,-78.3724573?units=si').subscribe(data => {
      this.isHelloVisibleSubject.next(true);  
    });
    } else {
      this.isHelloVisibleSubject.next(true);
    }
  }

  closeHello() {
    this.isHelloVisibleSubject.next(false);
  }



<hello [showHello]="isHelloVisibleSubject | async"> </hello

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...