How to assign an array to object property in Angular Subscribe method?

Viewed 1230

I am trying to get array value from en.json translation file in angular and try to bind it to an object property as shown below code snippet.

typescript code:

  ngOnInit() {
    this.en = {
      dayNamesMin: this.translateSvc
                  .get(['calendar.day_names_min.Sun', 'calendar.day_names_min.Mon', 'calendar.day_names_min.Tue', 'calendar.day_names_min.Wed',
                    'calendar.day_names_min.Thu', 'calendar.day_names_min.Fri', 'calendar.day_names_min.Sat'])
                  .subscribe(translated => {
                    console.log(Object.keys(translated).map(key => translated[key]));
                    return Object.keys(translated).map(key => translated[key]);
                  })
    };
  };

en.json file looks like this:

{
    "calendar" : {
        "day_names_min": {
            "Sun": "SUN",
            "Mon": "MON",
            "Tue": "TUE",
            "Wed": "WED",
            "Thu": "THU",
            "Fri": "FRI",
            "Sat": "SAT"
        }
    }
}

I am using ngx translator service to get the data from en.json file and then subscribe and assign value to dayNamesMin property of this.en object.

When I log the value Object.keys(translated).map(key => translated[key]); value in console, I am getting the proper array ["SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"]. But it is not binding to the object property dayNamesMin.

Can someone please help here ?

1 Answers

When you're dealing with asynchronous data using observables, it's better to assign values inside it's subscription instead of trusting that the variable will be assigned when it's accessed. In your case, you could do something like the following

ngOnInit() {
  this.translateSvc.
    .get([
      'calendar.day_names_min.Sun', 
      'calendar.day_names_min.Mon', 
      'calendar.day_names_min.Tue', 
      'calendar.day_names_min.Wed',
      'calendar.day_names_min.Thu', 
      'calendar.day_names_min.Fri', 
      'calendar.day_names_min.Sat'
    ])
    .subscribe(translated => {
      this.en = {
        dayNamesMin: Object.keys(translated).map(key => translated[key])
      };
    });
}

Now you have an idea that the this.en variable isn't assigned value until the this.translateSvc.get() observables emits. So you need to remember that this.en is asynchronous when it's accessed.

More info on async data here.


Or say if you only want to use the this.en variable in the template to display the values, you could map the output from this.translateSvc.get() using RxJS map operator and use Angular async pipe.

Controller

en$: Observable<any>;

ngOnInit() {
  this.en$ = this.translateSvc.     // <-- assign the observable
    .get([
      'calendar.day_names_min.Sun', 
      'calendar.day_names_min.Mon', 
      'calendar.day_names_min.Tue', 
      'calendar.day_names_min.Wed',
      'calendar.day_names_min.Thu', 
      'calendar.day_names_min.Fri', 
      'calendar.day_names_min.Sat'
    ])
    .pipe(       // <-- transform the response here
      map(translated => ({ dayNamesMin: Object.keys(translated).map(key => translated[key]) }))
    );
}

Template

<ng-container *ngIf="(en$ | async) as en">
  {{ en.dayNamesMin | json }}
  {{ en.dayNamesMin[0] }}
  ...
  <p *ngFor="let day of en.dayNamesMin">
    {{ day }}
  </p>
</ng-container>

Update: Use with PrimeNg calendar

As shown in the second variant, use RxJS map operator to transform it to the required object format and use it as the input in the HTML template.

<ng-container *ngIf="(en$ | async) as en">
  <p-calendar 
    dateFormat="dd/mm/yy" 
    [(ngModel)]="value" 
    [locale]="en">   <!-- use `en` from the async pipe -->
  </p-calendar>
</ng-container>
Related