【问题标题】:How to make sure constructor/ngOnInit are done before calling functions from a class?如何确保在从类调用函数之前完成构造函数/ngOnInit?
【发布时间】:2017-06-23 21:26:31
【问题描述】:

我有一个类,它在初始化时从服务中检索数据并填充其属性之一,即数组。这个类有一个排序、过滤和返回这个数组的函数。 当我实例化这个类的一个对象并调用这个函数时,我意识到它是在它的构造函数和 ngOnInit() 函数完成之前被调用的(可能是因为我使用了服务返回的 Observables 中的异步内容)。在外部调用我的类的任何函数之前,如何保证构造函数和 init 已经完全执行?

    export class BaseChoice implements PickAppraiser, OnInit {
weight = 0;
options = new Array<PickQuality>();

constructor(private championService: ChampionService) {}

ngOnInit() {
    // Iterates through the list of champions adding them to the current object
    this.championService.getChampions()
        .subscribe(champions => {
            // Iterates through the list of champions adding them to the current object
            Object.keys(champions).map(key => this.options.push(new PickQuality(champions[key], 0)))
        })
}

choose(n?: number): PickQuality[] {
    var sorted = this.options.sort((a, b) => a.score - b.score);
    return sorted;
}

}

我也尝试过做类似的事情

    choose(n?: number): PickQuality[] {
    // Iterates through the list of champions adding them to the current object
    this.championService.getChampions()
        .subscribe(champions => {
            // Iterates through the list of champions adding them to the current object
            Object.keys(champions).map(key => this.options.push(new PickQuality(champions[key], 0)))
            this.reevaluate(this.options);

            var sorted = this.options.sort((a, b) => a.score - b.score);
            var chosen;
            if(n) chosen = sorted.slice(1, n);
            else chosen = sorted.slice(1, 2);
            return chosen;
        });
}

我在 choose() 方法本身中运行异步请求的地方,但它不会让我这样做,我假设是因为返回变量不能保证存在。

【问题讨论】:

  • 这取决于内容是如何被外部调用的。它是否被父组件、指令、服务等调用?有什么原因你不能在 map 函数之后对列表进行排序?
  • 看看Is it bad practice to have a constructor function return a Promise?。不要在初始化实例时做任何异步操作(直接通过构造函数或角度钩子),在创建实例之前做。
  • 在类中创建实例之前如何做任何事情?构造函数不是在类中运行的第一件事吗?
  • 您可以做的最简单(虽然不是最优雅)的事情就是在您制作课程时不初始化选项。然后在您的模板中,您可以执行 options?.doSomething()*ngIf(options)

标签: angular promise angular-promise angular2-services


【解决方案1】:

我认为,你应该看看你是如何从根本上布局你的组件的。您可以利用 observables 的方式,将它们用作模板中的角度支持,使用异步管道。

我不确定您的组件的详细信息,但我会这样做:

export class BaseChoice implements PickAppraiser, OnInit {
    weight = 0;
    options$: Observable<PickQuality>;
    champions$ : Observable<Champion>;

    constructor(private championService: ChampionService) {}

    ngOnInit() {
        this.champions$ = this
            .championService.getChampions();

        this.options$ = this.champions$.map((champion, index) => {
          return new PickQuality(champion, 0)))
      })
    }
}

在您的模板中,如果您执行*ngFor="let option in options$ | async),它将自动运行该流并为您提供结果,然后在您的choose() 函数中,我假设这是用户单击时执行的操作,您可以直接传递选项来处理它。

如果比这更复杂,您可以将其映射到点击流,例如 championClicked$,然后将这些点击映射到选项流中的正确选项。

要记住的是,您正在使用操作管道设置这些可观察对象,并且该管道每个观察者(订阅者)运行一次,这意味着每次使用 | async 管道时,它都会订阅并运行整件事。

花一些时间学习 RxJS 将为您的 Angular 2 开发带来巨大的回报。

【讨论】:

    【解决方案2】:

    既然您将options 属性初始化为一个空数组options = new Array&lt;PickQuality&gt;();,为什么不在choose 方法中进行检查?

    choose(n?: number): PickQuality[] {
        if (this.options && this.options.length !== 0) {
           var sorted = this.options.sort((a, b) => a.score - b.score);
           return sorted;
        } else {
           /* do something else if options is empty */
        }
    }
    

    --- 编辑---

    这是我在 cmets 中提到的粗略版本:

    export class BaseChoice implements PickAppraiser, OnInit {
    weight = 0;
    options = new Subject<PickQuality[]>();
    
    constructor(private championService: ChampionService) {}
    
    ngOnInit() {
        this.championService.getChampions()
            .subscribe(champions => {
                Let arr = [];
                Object.keys(champions).map(key => arr.push(new PickQuality(champions[key], 0)));
                 this.options.next(arr);
            })
    }
    
    choose(n?: number): PickQuality[] {
        return this.options.take(1).sort((a, b) => a.score - b.score);
    }
    }
    

    【讨论】:

    • else 语句我该怎么做?我不能仅仅因为异步内容还没有运行就关闭 choose() 函数调用。我也不能一直忙着等待(比如 while(1)),因为我的整个应用程序都会停止。
    • 这将取决于触发选择方法的原因以及它正在做什么(您传入 'n' 但未使用它)。在我的应用程序中,我实际上会创建一个 RXJS 主题。选项 = 新主题 ();然后在选择中应用您的排序并返回可观察对象,以便您的选择方法响应的接收者将等待(异步)数组。
    • 我实际上使用它来拆分我的数组,如 options.split(0,n)。我会看看这个主题的东西,谢谢。
    • 我可以稍后修改上面的答案,但我现在在手机上。
    【解决方案3】:

    如果您从模板中调用 choose(n),您可以在模板中的标签中添加 *ngIf="options"。您必须更改声明“选项”的方式,以便在加载数据之前未定义。像这样的:

    options: PickQuality[];
    

    如果您从服务中调用 choose(n),我会将其更改为返回 Observable 或 Promise。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-09-28
      • 2015-12-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多