Пользовательский интерфейс не обновляется для событий за пределами ngZone Angular - PullRequest
0 голосов
/ 20 октября 2018

В моем проекте я получаю электронную почту через Google Contact API.Моя проблема в том, что после получения электронной почты угловой интерфейс не обновляется.

Вот мой код для получения электронных писем в ContactComponent.ts

import { Component, OnInit, EventEmitter, Output, ViewChild } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { ContactService } from './contacts.service';
import { Contact } from './contact.model';
import { Subject } from 'rxjs';
import { ContactListComponent } from './contact-list/contact-list.component';
declare var gapi: any;

@Component({
  selector: 'app-contacts',
  templateUrl: './contacts.component.html',
  styleUrls: ['./contacts.component.css'],
  providers: [ContactService]
})
export class ContactsComponent implements OnInit {
  constructor(private http: HttpClient, private contactService: ContactService) {}
  authConfig: any;
  contactsList: Contact[] = [];
  ContactsFound = true;
  ngOnInit() {

    this.ContactsFound = false;

    this.authConfig = {
      client_id:
        '<my_client_id>',
      scope: 'https://www.googleapis.com/auth/contacts.readonly'
    };

  }

  fetchGoogleContacts() {
    gapi.client.setApiKey('<my_key>');
    gapi.auth2.authorize(this.authConfig, this.handleAuthorization);
  }

  handleAuthorization = authorizationResult => {
    if (authorizationResult && !authorizationResult.error) {
      const url: string =
        'https://www.google.com/m8/feeds/contacts/default/thin?' +
        'alt=json&max-results=500&v=3.0&access_token=' +
        authorizationResult.access_token;
      console.log('Authorization success, URL: ', url);
      this.http.get<any>(url).subscribe(response => {
        if (response.feed && response.feed.entry) {
          // console.log(response.feed.entry);
          this.saveContacts(response.feed.entry);
        }
      });
    }
  }

  saveContacts(ContactEntry) {

    this.contactsList = [];

    ContactEntry.forEach((entry) => {
      // tslint:disable-next-line:prefer-const
      let contact: Contact = { email: '', name: '' };

      if (entry.gd$name !== undefined) {
        contact.name = entry.gd$name.gd$fullName.$t;
        // console.log('Name of contact: ' + contact.name);
      }

      if (Array.isArray(entry.gd$email)) {
        entry.gd$email.forEach((emailEntry) => {
          if (emailEntry.address !== undefined) {
           // console.log('Email of contact: ' + emailEntry.address);
            contact.email = emailEntry.address;
          }
        });
      }

      this.contactsList.push(contact);
    });

    this.ContactsFound = true;
     // Calling next in ContactService for propagating the events
    this.contactService.contactsArrived(this.contactsList);
    console.log(`Contacts List Length ${this.contactsList.length}`);

  }



} 

Я использую сервис для подписки на событие длядобавление электронных писем

import { Injectable } from '@angular/core';
import { Contact } from './contact.model';
import {  BehaviorSubject, Subject } from 'rxjs';
import { TouchSequence } from 'selenium-webdriver';



@Injectable()
export class ContactService {
  contacts: Contact[] = [];
  private contactsSubject = new Subject<Contact[]>();
  contactArrived$ = this.contactsSubject.asObservable();

  constructor() {
  }

  contactsArrived(contacts: Contact[]) {
     console.log(`Contacts Arrived in Contact Service`);
     if (contacts) {
        this.contactsSubject.next(contacts);
     }
  }
}

Вот мой contact.component.html

<button class="btn btn-primary" (click)="fetchGoogleContacts()">Import Contacts</button>
<app-contact-list [contacts]="contactsList"></app-contact-list>

Код для компонента списка контактов

import { Component, OnInit, Input, OnDestroy, OnChanges, SimpleChanges } from '@angular/core';
import { ContactService } from '../contacts.service';

import { Contact } from '../contact.model';
import { Subscription } from 'rxjs';

@Component({
  selector: 'app-contact-list',
  templateUrl: './contact-list.component.html',
  styleUrls: ['./contact-list.component.css']
})
export class ContactListComponent implements OnInit, OnDestroy {
  @Input() contacts: Contact[];
  subscription: Subscription;

  constructor(private contactService: ContactService) {
  }

  ngOnInit() {
    this.subscription = this.contactService.contactArrived$.subscribe(data => {
      console.log(data);
      this.contacts = data;
    });
  }

  ngOnDestroy() {
    this.subscription.unsubscribe();
  }


}

ContactList Component.html

<div>
  <div>
    <br>
    <h4 class="card-title">Your Contacts</h4>
    <div class="card">
      <div class="card-header">Contact Names
        <span class="float-right">Emails Ids</span>
      </div>
      <ul *ngFor="let contact of contacts" class="list-group list-group-flush">
        <app-contact-item [contact]="contact"></app-contact-item>
      </ul>
    </div>
  </div>
</div>

Я получаю данные в Компоненте списка контактов после остального вызова из API контактов Google.Но сумма, как мой пользовательский интерфейс не обновляется.Список не обновляется.После поиска в Google я узнал, что Angular ведет себя по-разному для событий за пределами ngZone.Как я могу обновить пользовательский интерфейс после получения данных.

1 Ответ

0 голосов
/ 20 октября 2018

Основная причина

Похоже, что contacts не захватывается как часть механизма обнаружения изменений Angular.

Исправление: у вас есть несколько вариантов

Во-первых, необходимо иметь сеттер и геттер для contacts

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

в тс

import { Component, OnInit, Input, OnDestroy, OnChanges, SimpleChanges } from '@angular/core';
import { ContactService } from '../contacts.service';

import { Contact } from '../contact.model';
import { Subscription } from 'rxjs';

@Component({
  selector: 'app-contact-list',
  templateUrl: './contact-list.component.html',
  styleUrls: ['./contact-list.component.css']
})
export class ContactListComponent implements OnInit, OnDestroy {
  @Input("contacts") _contacts: Contact[];
  subscription: Subscription;

  constructor(private contactService: ContactService) {
  }

  ngOnInit() {
    this.subscription = this.contactService.contactArrived$.subscribe(data => {
      console.log(data);
      this._contacts = data;
    });
  }

    get contacts(){
        return this._contacts;
    }

    set contacts(contacts){
        this._contacts = contacts;
    }

  ngOnDestroy() {
    this.subscription.unsubscribe();
  }

}

Второй вариант использования setTimeout при настройке контактов

ngOnInit() {
        this.subscription = this.contactService.contactArrived$.subscribe(data => {
          console.log(data);
          setTimeout(()=>this.contacts = data);
        });
      }
...