Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
174 views
in Technique[技术] by (71.8m points)

typescript - Angular Setting an Array[] before the view renders

Currently i'm trying to figure out a way to fill this array events: CalendarEvent[] = []; Before the view renders. As i'm trying to fill a calendar from some database entries I have, But I already have the data pulled In one function then I assign in another. The issue being that I cant seem to get the array to update before the render but can easily update it with a button click event afterwords.

So im diving into the different ways to approach this, Ive tried things like setting a loader and not rendering with a ngIf until the function is done, But when it comes to angular alot of this is still a miss for me, So im unsure what possible approaches there are in regards to this.

As I understand it currently the way my service returns the entries it just returns a promise and not the values so the view doesn't have the data, So I understand there are ways for me to manipulate the component state, But are there ways to achieve this without having to refresh the component after the render

I thought something like this would work but as I understand Async in Angular is different from that in c#/Xamarin ect

async ngOnInit(){
    await this.getEntries();
    await this.setCalendar();
  }

I fetch the entries:

  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");
  }

I then push them to the calendar array:

 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,
        },
      ];
    
    });

The Calendar Library StackBlitz More or less same thing StackBlitz

Please note: The Entires Array is Where I get the data from the database, The issue is the events: CalenderEvent[] = []; Is not getting filled before the view render So the calendar is empty And the only way to currently fill it is with a click event or something of the sorts after the initial render, I have tried the below answers they dont work for me

A small note I seem to have forgotten to add:

The Variables im using:

  entries: Entrymodel[] = [];
 events: CalendarEvent[] = [];

The Entry Model:

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;
}

I am trying to get the Entries, As they contain dates that Need to be pushed into the events Array to display on the calendar

question from:https://stackoverflow.com/questions/66058472/angular-setting-an-array-before-the-view-renders

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Considering this issue as a good example when smart and dumb components are useful.

As you wrote yourself - managing a state is important here and its super easy if you split your component into two - smart and dumb components.

Smart component will prepare all the data while dumb component will render it.

Example

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,
        },
      ];
    
    });

smart.component.html:

<app-dumb-component
  [entries]="entries | async"
  [loading]="loading"
></app-dumb-component>

dumb-component.ts:

@Input('entries') entries: any[];
@Input('loading'): boolean;

dumb.component.html:

<div *ngIf="!loading; else loading"> ... </div>

<ng-template #loading>
  // spinner
</ng-template>

p.s. here for simplicity I store state inside a smart component (while usually we use stores for this) and approach is in general just an example, since the whole architecture of this should be determined how data flows back and forward.

EDIT

What I just noticed you are you are using a real time database, right? (probably Firebase - will add a tag to your post. Please change it if it is not Firebase). And that changes the concept. With normal database like SQL, when you make a HTTP call it will return you once - response or error. While with real time database, you get a stream, like a web socket. Meaning you must treat that as a stream because it can return you values at any time in the future.

So what you asked - you need to get events based on results on entries stream. And that stream can push values at any time. So what you can do - map entries to events:

smart.component.ts:

  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,
        },
      ];
    
    });

Changes in setCalendar function - entries.forEach changed to entries.map and this.events = [ ...this.events, to return

smart.component.html:

<app-dumb-component
  [events]="events | async"
  [loading]="loading"
></app-dumb-component>

Now at this point, loading property will be useful only for first connection because later it will emit immediately.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...