【问题标题】:Angular List Component not updating after List Array sorted in Service在服务中对列表数组进行排序后,角度列表组件未更新
【发布时间】:2018-10-02 14:17:30
【问题描述】:

我目前正在通过制作一个小项目来学习如何正确使用 Angular(版本 5+)。我已经设置了一些基本路由,通过选择导航选项卡来加载 ContactList 组件。另一个选项卡允许使用表单添加联系人,然后将表单数据发送到联系人数据服务,其中联系人在数组中进行管理。 ContactList 组件如下:

import { Component, OnInit, Input, Output,
   OnChanges, DoCheck, SimpleChanges} from '@angular/core';
import { ContactDataService } from '../ContactData.service';
// import { FilterPipe } from '../../../src/pipes';
import { Contact } from '../contact.model';

@Component({
  selector: 'app-contact-list-component',
  templateUrl: './contact-list-component.component.html',
  styleUrls: ['./contact-list-component.component.css']
})
export class ContactListComponent implements OnInit, OnChanges, DoCheck {

    contactList: Contact[] = [];
    searchQuery: string; // alternate subscribed data from contactDataService


  contactServObj: ContactDataService;


  constructor(contactServObj: ContactDataService) {
    this.contactServObj = contactServObj;
  }

  ngOnChanges(changes: SimpleChanges) {
    console.log('ngOnChanges called!');
    console.log(changes);
    for (const key of Object.keys(changes)) {
        console.log(`${key} changed.Current: ${changes[key].currentValue}.Previous: ${changes[key].previousValue}`);
      }
  }

  ngOnInit() {
    this.contactList = this.contactServObj.getContacts();
    // console.log('ngOnInit called!');
    this.contactServObj.queryString.subscribe((query: string) => {
      this.searchQuery = query;
    });
  }

  ngDoCheck() {
    console.log('ngDoCheck called!');
  }
}

联系人数据服务(其中管理联系人数组)如下:

import { EventEmitter } from '@angular/core';
import { Contact } from './contact.model';

export class ContactDataService {
  private contactList: Contact[] = [];

  getContacts() {
    return this.contactList;
  }

  sortContactHandler = (value) => {
    console.log(value);
    const field = value === 'default' ? null : value;
    if (this.contactList.length > 1 && field !== null) {
      this.contactList.sort(this.compareValues(field, 'asc'));
      console.log(this.contactList);
    }
  }

  compareValues = (key, order= 'asc') => {
    return function(a, b) {
        if (!a.hasOwnProperty(key) ||
          !b.hasOwnProperty(key)) {
          return 0;
        }
        const varA = (typeof a[key] === 'string') ?
          a[key].toUpperCase() : a[key];
        const varB = (typeof b[key] === 'string') ?
          b[key].toUpperCase() : b[key];
        let comparison = 0;
        if (varA > varB) {
          comparison = 1;
        } else if (varA < varB) {
          comparison = -1;
        }
        return (
          (order === 'desc') ?
          (comparison * -1) : comparison
        );
    };
  }



  addContactHandler(sentContact: Contact) {
    let field = '';
    const findExistingContact = this.contactList.findIndex((el, i) => {
      // return (name === el.name) || (phone === el.phone) || (email === el.email);
      if (sentContact.name === el.name) {
        field = 'name';
        return true;
      } else if (sentContact.phone === el.phone) {
        field = 'phone';
        return true;
      } else if (sentContact.email === el.email) {
        field = 'email';
        return true;
      }
      return false;
    });
    console.log(findExistingContact);
    if (findExistingContact === -1) {
      const newContact: Contact = sentContact;
      this.contactList.push(newContact);
    } else {
      alert(`Contact with field ${field} already exists!`);
    }
  }


}

Contact Data Service 在根级别注入 - 在 app.module.ts 中,如下所示:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { Routes, RouterModule } from '@angular/router';

import { AppComponent } from './app.component';
import { ContactComponent } from './contact-component/contact-component.component';
import { ContactListComponent } from './contact-list-component/contact-list-component.component';
import { AddContactComponent } from './add-contact-component/add-contact-component.component';
import { SearchContactComponent } from './search-contact-component/search-contact-component.component';
import { BackdropComponent } from './backdrop/backdrop.component';
import { ModalComponent } from './modal/modal.component';
import { EditContactComponent } from './edit-contact/edit-contact.component';
import { ContactDataService } from './ContactData.service';
import { ModalShowService } from './modal-show.service';
import { CockpitComponentComponent } from './cockpit-component/cockpit-component.component';
import { FilterContactPipe } from './filter-contact.pipe';
import { SortComponent } from './sort/sort.component';
import { ContactDetailComponent } from './contact-detail/contact-detail.component';

const appRoutes: Routes = [
  { path: '' , component: AddContactComponent },
  { path: 'contactList', component: ContactListComponent },
  { path: 'contactList/:name', component: ContactDetailComponent }
];

@NgModule({
  declarations: [
    AppComponent,
    ContactComponent,
    ContactListComponent,
    AddContactComponent,
    SearchContactComponent,
    BackdropComponent,
    ModalComponent,
    EditContactComponent,
    CockpitComponentComponent,
    FilterContactPipe,
    SortComponent,
    ContactDetailComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    RouterModule.forRoot(appRoutes)
  ],
  providers: [ContactDataService, ModalShowService],
  bootstrap: [AppComponent]
})
export class AppModule { }

最后,我有一个排序组件,它只是一个简单的下拉选择框,其中包含用于对联系人进行排序的 3 个字段。它所做的只是将这个字段发送到联系人数据服务中的 sortContactHandler 函数,我在 console.log(this.contactList) 语句中看到了这个结果。但是这种变化并没有反映在呈现数组的 ContactList 组件中。

问题:将这个排序后的contactList 数组放入我的ContactList 组件的最佳方法是什么(它只在ngOnInit() 函数中检索这个数组)?

我认为这可以使用生命周期钩子来完成,所以我试图理解它们,但也许我做错了什么。 ngOnChanges() 永远不会被调用(我从未在该函数中看到过 console.log()),而且我不确定如何使用 ngDoCheck()。另外,从我对上述生命周期方法的阅读中可以看出,它们会对组件输入属性的变化做出反应,所以我不确定它们是否可以在我的情况下使用。

这是StackBlitz的问题

【问题讨论】:

  • 你知道 Angular 中的依赖注入吗? angular.io/guide/dependency-injection
  • 是的,我知道它的要点。我将编辑我的问题以显示服务的注入位置。但这不是问题所在;问题是,我们如何将服务中最新更新的数组检索到组件中?
  • 当您在根模块的 providers 数组中设置 DataService 时,它​​会成为整个应用程序的 Singleton。这意味着,当有人在其中更改 contactList 时,通过依赖注入指向该对象的任何其他组件的属性都将具有它的最新值。所以这里一切都很好。您可以在 StackBlitz.com 中展示您的 gitHub 或重现问题吗?
  • 用 StackBlitz 中的代码更新问题

标签: javascript arrays angular


【解决方案1】:

NgOnChanges - 对使用 @Input 装饰器声明的属性值的更改做出反应,但您的组件类中有一个普通属性。它不会检测到更改。

据我了解您的情况,您在 UI 上有两个兄弟组件,您正在从一个组件更改服务类的属性值,并期望在另一个组件中进行更改,但是您无法读取该更改除了 OnInit 中的组件(加载组件时执行一次),因此如果您的组件保留在 DOM 中并且您期望更改会被反映。

您可以使用 Observable 数据流,因此基本想法是在您的服务类中拥有 Observable 数据,并且您应该在其他地方(在您的组件类中)订阅该数据,以便每当您在 Observable 上发出任何更改流,订阅者将收到通知。

您可以尝试在您的服务中使用可观察的“contactList”,如下所示,

private contactList = new Subject<Contact[]>();
contactList$ = this.contactList.asObservable();

然后您可以在您的联系人列表组件中订阅它,您希望更改如下所示。

serviceObj.contactList$.subscribe(
      contactList => {
        this.contactList= contactList;
      })

要执行您在订阅函数中编写的代码,您需要在服务类中包含此代码

this.contactList.next(contactList)

上面一行是执行一个可观察对象的下一个方法,你已经将它的定义写成订阅函数的第一个参数。

如果您可以将代码上传到某个地方,我可以更好地查看和建议。

附: - 即使我是 Angular/RxJs 的新手,也可以使用更好的技术术语,请随时修改。

查看您的代码后

我想出了这个最简单的解决方法。

  1. 在排序组件中添加一个事件。
  2. 在您更新联系人列表时发出事件。
  3. 拥有更新联系人列表组件中的联系人列表的代码。

这就是您需要添加的全部内容。

contact-list.component.html

<app-sort (contactListUpdate)="fetchUpdatedList()"></app-sort>

contact-list.component.ts

fetchUpdatedList(){
this.contactList = this.contactServObj.getContacts();

}

sort-contact.component.ts

   @Output() contactListUpdate:EventEmitter<any>=new EventEmitter<any>();

  constructor(private contactServObj: ContactDataService) { }

  onSelectChange(event) {
    this.contactServObj.sortContactHandler(event.target.value);
    this.contactListUpdate.emit();
  }

我认为这很不言自明,您想根据子组件中的某些条件在父组件中执行一些代码,即我们可以利用事件发射器从子组件发出事件,并在父组件中监听该事件.

请试试这个,如果您想上传任何解释或代码,请告诉我。

【讨论】:

  • 感谢faizan,是的,您正确理解了场景。我理解你所说的大部分内容(我会试一试),我只是不确定在我的联系人数据服务文件中的哪个位置,我会输入以下行:this.contactList.next(contactList)。有没有你推荐的在线 JS 编辑器,我可以在其中上传我的代码(我可以添加多个 .ts 文件的地方)?
  • 您认为您正在更改/更新联系人列表的任何位置。它或多或少与contactList 的setter 方法相同,但next 方法的主要工作是通知订阅者。您可以将代码上传到stackblitz.com
  • 当我设置 contactList = new Subject() 时,它会在我的方法和其他组件中破坏与数组函数相关的代码(ngFor、.find()、.forEach())。我如何还能使用 .forEach()、.findIndex()、.length、.slice() 等?
  • 是的,因为这些方法对 Observables 无效,我确实查看了您的代码,它需要在方法数量上进行更改,然后才能完全使用 observables
  • Observable 是处理这类场景的正确方法,你应该花点时间学习响应式编程
猜你喜欢
  • 2019-09-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多