jsonangularobservablesanitizationdataservice

How can I sanitize css properties to use in template given from a data service


I need to generate sanitized css property to use with my component template to set the background image of the div:

<div *ngFor="let Item of Items"
    [style.background-image]="Item.imageStyle
    (click)="gotoDetail(Item.iditems)">
</div>

using data obtained through a data service. The component is:

  import { Component } from '@angular/core';
  import { Router } from '@angular/router';
  import { DomSanitizer } from '@angular/platform-browser';
  import { OnInit } from '@angular/core';

  import { Item } from '../models/Item';

  import { CollectionDataService } from '../services/CollectionData.service';

  @Component({
    selector: 'mainpage',
    templateUrl: 'app/mainpage/mainpage.component.html',
    styleUrls: ['app/mainpage/mainpage.component.css']
  })
  export class MainpageComponent implements OnInit {

     Items: Item[];

     ngOnInit() {

        this.collectionDataService.getItems().subscribe(
              Items => this.Items = Items
           );

        // Generates and sanitizes image links
        this.Items.map(
                 (LItem) => LItem.imageStyle = this.sanitizer.bypassSecurityTrustStyle("url(template/images/"+LItem.iditems+".jpg)")
              )
     }

     constructor(
           private router: Router,
           private sanitizer: DomSanitizer,
           private collectionDataService: CollectionDataService
        ) {

     }

     gotoDetail($iditems: number): void {
        this.router.navigate(['/viewer', $iditems]);
     }
  }

But it doesn't work because the statement that generates the sanitized property

this.Items.map(
                 (LItem) => LItem.imageStyle = this.sanitizer.bypassSecurityTrustStyle("url(template/images/"+LItem.iditems+".jpg)")
              )

doesn't find the loaded data. The error that I'm seeing in the browser console is:

core.umd.js:3070 EXCEPTION: Uncaught (in promise): Error: Error in  ./MainpageComponent class MainpageComponent_Host - inline template:0:0 caused by: Cannot read property 'map' of undefined
TypeError: Cannot read property 'map' of undefined

The data service is:

  import { Injectable } from '@angular/core'
  import { Http } from '@angular/http'
  import { Item } from '../models/Item';
  import { DomSanitizer } from '@angular/platform-browser';

  @Injectable()
  export class CollectionDataService {

     constructor(
           private http: Http,
           private sanitizer: DomSanitizer
        ) { }

     getItems() {

        return this.http.get('app/mocksdata/items.json').map(
                    response => <Item[]>response.json().items
              )

     }

  }

And the provided items.json:

  {
     "items": [{
           "iditems": 1,
           "imageStyle": ""
        }, {
           "iditems": 2,
           "imageStyle": ""
        }]
  }

If I set static data in the component, instead of using the data service, everything works:

  export class MainpageComponent implements OnInit {

     Items: Item[];

     ngOnInit() {

        this.Items = [{
           "iditems": 1,
           "imageStyle": ""
        }, {
           "iditems": 2,
           "imageStyle": ""
        }]

        // Generates and sanitizes image links
        this.Items.map(
                 (LItem) => LItem.imageStyle = this.sanitizer.bypassSecurityTrustStyle("url(template/images/"+LItem.iditems+".jpg)")
              )
     }

How can I force the sanitizer statement to wait that the async data are fully loaded? Alternatively how can I generate sanitized properties directly in the service?

EDIT The best answer comes from PatrickJane below:

     Items: Item[] = [];

      ngOnInit() {

       this.collectionDataService.getItems().subscribe(Items => {
            this.Items = Items;
            this.Items.map(LItem => LItem.imageStyle = this.sanitizer.bypassSecurityTrustStyle("url(template/images/"+LItem.iditems+".jpg)"))}
         });

     }

I also solved this problem working directly in the service method (credits), but it is more verbose:

  return this.http.get('app/mocksdata/items.json')
     .map( (responseData) => {
          return responseData.json().items;
        })
     .map(
        (iitems: Array<any>) => {
           let result:Array<Item> = [];
           if (iitems) {
              iitems.forEach((iitem) => {
                    iitem.imageStyle = this.sanitizer.bypassSecurityTrustStyle("url(template/images/"+iitem.iditems+".jpg)");
                    result.push(<Item>iitem);
                 });
           }
           return result;
        }
    )

Solution

  • The subscribe function is async so your map function called before the subscribe function run. So in this phase the array is undefined because you doesn't set any initial value.

    The solution is to do this inside the subscribe function and to initialize the Items with empty array.

     Items: Item[] = [];
    
     ngOnInit() {
    
      this.collectionDataService.getItems().subscribe(Items => {
           this.Items = Items;
           this.Items.map(LItem => LItem.imageStyle = this.sanitizer.bypassSecurityTrustStyle("url(template/images/"+LItem.iditems+".jpg)"))}
        });
    
    }