检索通过输入属性传递给子组件的值

Posted

技术标签:

【中文标题】检索通过输入属性传递给子组件的值【英文标题】:Retrieve value passed to child component via input attribute 【发布时间】:2020-02-20 13:13:54 【问题描述】:

我有一个大项目,我在其中对 API 进行了几次 HTTP GET 调用以获取不同的数据。我想将它们组合在一个调用中(我已经创建了它并且我已经将所有数据嵌套在 json 中),问题是我无法在组件之间传递数据。让我解释一下这个问题。这是电话:

get(entityType: string): Promise<any> 
    return this.http
        .get<any>(
            this.location.prepareExternalUrl("./assets/temp-data/LEV_page/organisation.json")
        )
        .take(1)
        .toPromise();

在我的主要组件中,我这样做:

public entityType:string;
public dataSource;

constructor(private levsService: LinkedEntityVocabulariesService) 

ngOnInit() 
    this.entityType = localStorage.getItem("currentEntityType");
    this.levsService.get(this.entityType).then(data => 
        this.dataSource = data;
    );

如果我想在该组件中使用dataSource,它可以正常工作,但是当我尝试像这样将它传递给子组件时:

&lt;app-properties [send]="dataSource"&gt;&lt;/app-properties&gt;

然后在那里访问它:

@Input("send") send;

constructor() 

ngOnInit() 
    console.log("test", this.send);

我得到的只是test undefined,因为它会在接收到数据之前传递它(尽管我认为使用Promise 而不是Observable 会阻止它)。

无论如何,我的问题是: 我可以调用获取数据然后在组件之间传递而不是再次调用 api 吗?如果是的话,我该怎么做上面的例子? 提前致谢!

【问题讨论】:

我在使用 @Input 装饰器时遇到了类似的问题。如果您必须等待数据并在数据到达后执行某些操作,请使用OnChanges 接口来检测数据何时到达。作为替代方案,您可以使用服务并将收到的数据存储在主题中,例如 BehaviourSubject 我将它与来自 rxjs 的 observable 结合使用。 ` mySubject: BehaviourSubject = new BehaviourSubject(null); myObservable$: Observable = mySubject.asObservable(); ` 你的意思是,就像在这个例子中一样? ***.com/a/42185519/12094093 跟帖子有点不同。请参阅我的最后评论。 【参考方案1】:

无论您使用 promise 配方还是 observable 配方,在数据可用之前尝试访问数据都会产生 undefined。例如,您可以仅在拥有数据时有条件地创建子组件

<app-properties [send]="dataSource" *ngIf="dataSource"></app-properties>

或者您可以在 app-properties 组件内部检查是否已定义 dataSource,然后再尝试对其进行任何操作,正确的做法是使用 ngOnChanges 生命周期方法。

ngOnChanges() 
    if (this.send) 
        console.log(this.send);
    

【讨论】:

【参考方案2】:

是什么让你的组件不显示新数据是 Angular change detection.

它没有被触发,幸运的是 Angular 内置了异步管道,可以为你做到这一点。此外,这个管道取消订阅事件,所以角度已经覆盖了你。

使用这种方法:

public entityType:string;
public dataSource$: Observable<yourInterface>;

constructor(private levsService: LinkedEntityVocabulariesService) 

ngOnInit() 
  this.entityType = localStorage.getItem("currentEntityType");
  this.datasource$ = this.levsService.get(this.entityType);

然后在你的 html 中使用这种方法:

<app-properties [send]="dataSource$ | async"></app-properties>

您将异步加载数据。

异步管道订阅 Observable 或 Promise 并返回它发出的最新值。当发出新值时,异步管道标记要检查更改的组件。当组件被销毁时,异步管道会自动取消订阅以避免潜在的内存泄漏。

如果你想了解更多,请看here

【讨论】:

这是不正确的。在OP的解决方案中,更改检测触发,但只有在更改实际发生(返回API调用)之后,他访问输入一次并且太早,在孩子的ngOnInit。从显式订阅更改为异步管道将无济于事,结果将是相同的。 我在 Angular 中使用这种方法 4 年多,在大型企业应用程序中。从来没有遇到过问题,很多课程也提供这种方式。官方 Angular 文档在这里展示了这种方法:angular.io/api/common/AsyncPipe 1. Angular 没有 4 年以上,它的初始版本是在 2016 年。并且 2. 如果异步管道在实际可用之前解决了在 ngOnInit 中访问数据的问题,那么我想在 stackBlitz 上查看。 啊,那他可以用@input() set(data:any) if(data) console.log(data) @mbojko 我是否正确访问 .ts 组件中的数据,最干净的解决方案?【参考方案3】:

我也遇到了同样的情况,我们有一个返回用户个人资料详细信息的 API 调用,并且应用程序中的大多数组件都需要该个人资料详细信息。

在 Angular 中,我们可以通过在服务(共享服务)中创建一个可共享的对象来减少这个 API 调用。我认为在这里您可以实现行为主题。一个对象需要在服务内部初始化或声明,并且需要通过调用组件中的服务在所有应用程序中调用该共享对象。为此,您需要从 'rxjs' 导入库

  private profileDataSource = new BehaviorSubject('');
  profileData = this.profileDataSource.asObservable();

上面将“profileDataSource”声明为具有“ ”值的 BehaviorSubject。我在登录后的第一个组件中调用了它,并使用配置文件详细信息对其进行了初始化。在我们的例子中,如果用户更新个人资料详细信息,我们也会更新此对象,此更新将在我们更新 BehavourSubject 对象时反映出来。您可以在组件中调用此 Object 并使用共享数据。

在此处深入学习主题https://rxjs-dev.firebaseapp.com/guide/subject

【讨论】:

以上是关于检索通过输入属性传递给子组件的值的主要内容,如果未能解决你的问题,请参考以下文章

将异步结果从父组件传递给子组件反应功能组件

Vue 组件间的传值(通讯)

通过属性指令将 CSS 样式传递给子 HTML 元素

【Flutter】多组件共用状态,父组件状态传递给子组件

Material - 将指令传递给子组件

Vue组件传值及插槽