Michele DC Michele DC - 1 month ago 26
JSON Question

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 can see 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
)

}

}


An the items.json loaded:

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

Answer

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

}
Comments