Angular 在视图渲染之前设置数组[]的角度

Angular 在视图渲染之前设置数组[]的角度,angular,typescript,firebase-realtime-database,Angular,Typescript,Firebase Realtime Database,目前,我正试图找出一种填充此数组的方法events:CalendarEvent[]=[] 在视图渲染之前。当我试图从一些数据库条目中填充日历时,我已经将数据拉入一个函数,然后分配给另一个函数。问题是,我似乎无法在渲染之前更新阵列,但可以通过单击按钮事件后置词轻松更新阵列 所以我尝试了不同的方法,比如设置一个加载器,在函数完成之前不使用ngIf进行渲染,但是当涉及到角度时,我仍然没有做到这一点,所以我不确定在这方面有什么可能的方法 据我目前的理解,我的服务返回条目的方式只是返回一个承诺,而不是值,

目前,我正试图找出一种填充此数组的方法
events:CalendarEvent[]=[]
在视图渲染之前。当我试图从一些数据库条目中填充日历时,我已经将数据拉入一个函数,然后分配给另一个函数。问题是,我似乎无法在渲染之前更新阵列,但可以通过单击按钮事件后置词轻松更新阵列

所以我尝试了不同的方法,比如设置一个加载器,在函数完成之前不使用ngIf进行渲染,但是当涉及到角度时,我仍然没有做到这一点,所以我不确定在这方面有什么可能的方法

据我目前的理解,我的服务返回条目的方式只是返回一个承诺,而不是值,因此视图没有数据,所以我知道有一些方法可以让我操作组件状态,但是有没有方法可以在渲染后不必刷新组件就可以实现这一点

我原以为这样可以,但据我所知,Angular中的异步与c#/Xamarin等中的异步不同

async ngOnInit(){
    await this.getEntries();
    await this.setCalendar();
  }
我会取回参赛作品:

  getEntries(){
    this.entryservice.getAventry().subscribe(data => {
      this.entries = data.map(e => {
        return {
          id: e.payload.doc.id,
          ...e.payload.doc.data() as {}
        } as Entrymodel;
      })
    });
    console.log("Get Done");
  }
然后我将它们推送到日历阵列:

 setCalendar(){
     this.entries.forEach(element => {
       
      let start = element.startDate.year + "-" + element.startDate.month + "-" + element.startDate.day;
      let end = element.endDate.year + "-" + element.endDate.month + "-" + element.endDate.day;

      var startDate = new Date(start); 
      var endDate = new Date(end); 
   
      var title = element.info + " " + element.surname;
       this.events = [
        ...this.events,
        {
          title: title ,
                                                                                                                                                       
          start: startOfDay(startDate),
          end: endOfDay( endDate),
          color: {
            primary: element.color,
           secondary: element.color
          },
          draggable: false,
          resizable: {
            beforeStart: false,
            afterEnd: false,
          },
          cssClass: element.surname,
        },
      ];
    
    });
日历库StackBlitz或多或少都是一样的东西

请注意:Entires数组是我从数据库获取数据的地方,问题是
事件:CalenderEvent[]=[]在视图渲染之前没有填充,因此日历是空的,当前填充日历的唯一方法是在初始渲染之后使用单击事件或类似的方式,我尝试了以下答案它们对我不适用

我似乎忘了补充一点:

我使用的变量包括:

  entries: Entrymodel[] = [];
 events: CalendarEvent[] = [];
进入模式:

export class Entrymodel {
    id: string;
    surname: string;
    color: string;
    startDate: Startdate;
    endDate: Enddate;
    info: string;
    status?: string;
    inbet?: Inbetween[];
    editing?: boolean;
}

export class Startdate{
    day: string;
    month: string;
    year: string;
}

export class Enddate{
    day: string;
    month: string;
    year: string;
}

export class Inbetween{
    day: string;
    month: string;
    year: string;
}

我正在尝试获取条目,因为它们包含需要推送到事件数组中以显示在日历上的日期

考虑到这个问题是智能和非智能组件有用的一个很好的例子

正如您自己所写的,管理一个状态在这里很重要,如果您将组件分为两个,那么它非常容易——智能组件和哑组件

智能组件将准备所有数据,而哑组件将渲染数据

范例

smart.component.ts:

  entries: Observable<any>;
  loading: false;

  getEntries(){
    this.loading = true;
    this.entries = this.entryservice.getAventry().pipe(
      map(data => {
        return data.map(e => {
          return {
            id: e.payload.doc.id,
            ...e.payload.doc.data() as {}
          } as Entrymodel;
        })
      }),
      map(this.setCalendar),
      finalise(() => this.loading = false)
    );
  }

 setCalendar(entries){
     return entries.forEach(element => {
       
      let start = element.startDate.year + "-" + element.startDate.month + "-" + element.startDate.day;
      let end = element.endDate.year + "-" + element.endDate.month + "-" + element.endDate.day;

      var startDate = new Date(start); 
      var endDate = new Date(end); 
   
      var title = element.info + " " + element.surname;
       this.events = [
        ...this.events,
        {
          title: title ,
                                                                                                                                                       
          start: startOfDay(startDate),
          end: endOfDay( endDate),
          color: {
            primary: element.color,
           secondary: element.color
          },
          draggable: false,
          resizable: {
            beforeStart: false,
            afterEnd: false,
          },
          cssClass: element.surname,
        },
      ];
    
    });
@Input('entries') entries: any[];
@Input('loading'): boolean;
  entries: Observable<any>;
  loading: false;

  getEntries(){
    this.loading = true;
    this.events = this.entryservice.getAventry().pipe(
      map(data => {
        return data.map(e => {
          return {
            id: e.payload.doc.id,
            ...e.payload.doc.data() as {}
          } as Entrymodel;
        })
      }),
      map(this.setCalendar),
      finalise(() => this.loading = false)
    );
  }

 setCalendar(entries){
     return entries.map(element => {
       
      let start = element.startDate.year + "-" + element.startDate.month + "-" + element.startDate.day;
      let end = element.endDate.year + "-" + element.endDate.month + "-" + element.endDate.day;

      var startDate = new Date(start); 
      var endDate = new Date(end); 
   
      var title = element.info + " " + element.surname;
       return {
          title: title ,
                                                                                                                                                       
          start: startOfDay(startDate),
          end: endOfDay( endDate),
          color: {
            primary: element.color,
           secondary: element.color
          },
          draggable: false,
          resizable: {
            beforeStart: false,
            afterEnd: false,
          },
          cssClass: element.surname,
        },
      ];
    
    });
dumb.component.html:

<app-dumb-component
  [entries]="entries | async"
  [loading]="loading"
></app-dumb-component>
<div *ngIf="!loading; else loading"> ... </div>

<ng-template #loading>
  // spinner
</ng-template>
<app-dumb-component
  [events]="events | async"
  [loading]="loading"
></app-dumb-component>
setCalendar函数中的更改-
entries.forEach
更改为
entries.map
this.events=[…this.events,
返回

smart.component.html:

<app-dumb-component
  [entries]="entries | async"
  [loading]="loading"
></app-dumb-component>
<div *ngIf="!loading; else loading"> ... </div>

<ng-template #loading>
  // spinner
</ng-template>
<app-dumb-component
  [events]="events | async"
  [loading]="loading"
></app-dumb-component>


此时,
加载
属性只对第一次连接有用,因为稍后它会立即发出。

因此我同意@Bitman的回答,即您应该考虑利用智能/哑组件。但首先让我们解决您的问题

背景 首先,我建议您充分利用typescript来帮助您找出问题所在。例如,我可以看到
getEntries()
没有返回类型,但您正在尝试等待它。(您可能需要执行类似
getEntries():void
getEntries():Entrymodel[]

RxJS的工作方式是使用Observable,它允许您使用
.subscribe()
异步跟踪值更改。订阅中的值仅在该范围内可用(并且无法返回),因为它使用回调方法。如果您不想学习RxJS(我完全可以理解)如果您对async/await更熟悉,我建议您使用
.ToPromise()
而不是subscribe,因为您可以等待承诺。这篇文章解释了承诺/回调/可观察性

问题 我在您的代码中看到的问题是,这将无法按照您的预期工作:

async ngOnInit(){
    await this.getEntries();
    await this.setCalendar();
}
getEntries()
将从您的服务中获取数据,但它不会在调用
setCalendar()
之前等待数据返回

解决 在调用set calendar之前,有多种方法可以获取数据:

使用带承诺的异步/等待

async getEntries(): Entrymodel[] {
  const data = await this.entryservice.getAventry().toPromise();
  ...  // Do your data manipulation stuff here
  return this.entries;
}

ngOnInit() {
 this.getEntries();
 // this.getCalendar(); <-- Moved inside getEntries()
}

getEntries(): void {
  this.entryservice.getAventry().subscribe(data => {
     ...  // Do your data manipulation stuff here and 
     this.getCalendar();
  });
}

使用可观测数据

async getEntries(): Entrymodel[] {
  const data = await this.entryservice.getAventry().toPromise();
  ...  // Do your data manipulation stuff here
  return this.entries;
}

ngOnInit() {
 this.getEntries();
 // this.getCalendar(); <-- Moved inside getEntries()
}

getEntries(): void {
  this.entryservice.getAventry().subscribe(data => {
     ...  // Do your data manipulation stuff here and 
     this.getCalendar();
  });
}

结论 这绝不是实现你正在做的事情的最佳方式,正确使用智能/哑组件和正确使用可观察对象将是,你可以参考@Bitman的答案,了解更理想的方法可能是什么样子


希望这能让您的代码在当前配置下运行,并且只需进行最小的更改。

您是否尝试在构造函数中调用函数?请告诉我。我还不知道您可以这样做。您能在stackblitz中给我一些代码示例吗?这可能有助于我检查。:)嗯,我可以共享库StkBLITZ,它或多或少相同地可以考虑AdvEntEnter()与我的StalCalEnter()相同的东西。模板是什么样子的?另外,您可以通过将
映射
放入
管道
来组合这两个函数,并在
订阅
中从setCalendar执行操作。此外,您不需要
异步/等待
getAventry
+
订阅
已经是异步的。嗯,我明白您的意思我们使用父组件和子组件,通常这将在存储中处理,但遗憾的是这是以后的集成。我想知道的是,为什么要在两者之间传递条目?以及在哪里传递
setCalendar(条目)的条目
Entry和Events在我的案例Entry:EntryModel和Events:CalendarEvent中是两种不同的类型,所以我尝试了你提到的方法,但没有得到任何数据返回..但是我可能有点困惑,如果你在
ngOnInit()中调用
getEntries()
,这些嵌套的组件父/子输入输出关系看起来很有趣
然后在生成模板时,
在sm中输入此项