0

I'm trying to update an observable which is returned in html from an API call.

I'm wondering if anyone can help me with this.

The html (on another component)

<common-content [theme]="theme" ></common-content>

and the component is:

import { Component, OnInit, Input } from '@angular/core';
import { Http, Response } from '@angular/http';
import { ThemeModel } from '../../models';
import 'rxjs/add/operator/toPromise';

@Component({
  selector: 'common-content',
  template: `<div innerHTML = "{{innerHtml}}"></div>`
})

export class CommonContentComponent implements OnInit {
    @Input() page: string;
    @Input() theme: ThemeModel;
    innerHtml: string;

    constructor(private http: Http) {
    }

    ngOnInit() {
        this.populatePage();
    }

    populatePage(){
        let thisUrl = 'myPage.html';
        this.http.get(thisUrl).subscribe(f => {
            var content = <string>f['_body'];
            this.innerHtml = content.replace("{{theme.Name}}", this.theme.name);
            }, (error) => {
                let e = error;
            }, () => {
        });
    }
}

so instead of doing a "replace" the observable should just update automatically.

I've tried to use a subscribe and I also tried a promise, however I don't seem to be able to get the syntax to behave.

Can anyone help?

Thanks in advance

Carsten
  • 4,005
  • 21
  • 28
Mick
  • 1
  • Could you show the contents of f – Carsten Oct 18 '17 at 05:19
  • just a chip in, instead of ` f['_body'];` change to `f.text()` safer – Theophilus Omoregbee Oct 18 '17 at 05:27
  • 2
    I don't understand what you're asking. What are you trying to achieve. Your code sends an HTTP request, and when the response comes back, initializes the innerHtml field of your component with the body of the response (after some replacement). What else would you like to achieve? – JB Nizet Oct 18 '17 at 05:49
  • use
    in template instead of
    – Chandru Oct 18 '17 at 06:31
  • Thanks Theophilus, I've used f.text() instead. What I'm trying to achieve Chandru is to not need to do a replace on the returned html. It returns it as a string instead of as a usable observable. – Mick Oct 18 '17 at 06:59

2 Answers2

1

1) What you want to achieve is not clear. What I can make out is on success you want to update the dom. 2) Dont use inner html for that and use interpolation or ngModel for the same with a sanitizer. 3) Another approach would be to create a custom reusable directive for the same.

An approach could be:

1) Make a pipe for sanitization:

import { Pipe, PipeTransform } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
/**
 *
 * @export
 * @class SafeHtmlPipe
 * @implements {PipeTransform}
 */
@Pipe({
  name: 'safeHtml'
})
export class SafeHtmlPipe implements PipeTransform {
  /**
   *
   * @param {DomSanitizer} sanitizer
   * @memberof SafeHtmlPipe
   */
  constructor(private sanitizer: DomSanitizer) { }
  /**
   *
   * @param {any} style
   * @returns
   * @memberof SafeHtmlPipe
   */
  transform(style) {
    // return this.sanitizer.bypassSecurityTrustStyle(style);
    return this.sanitizer.bypassSecurityTrustHtml(style);
    // return this.sanitizer.bypassSecurityTrustXxx(style); - see docs
  }
}

2) Use it like :

<div class="card_description" [innerHTML]="scenarioStepDataDesc | safeHtml"></div>

where scenarioStepDataDesc is your HTML content.

3) Use a shared module for pipes and other reusable components/directives

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MaterialModule } from '../material/material.module';
import { BlockUIModule } from 'ng-block-ui';
import { AutoCompleteComponent } from './components/autoComplete/autoComplete.component';
import { DialogDataComponent } from './components/dialog/dialog.component';
import { SafeHtmlPipe } from './pipes/safeHtml.pipe';
/**
 *
 * @export
 * @class SharedModule
 */
@NgModule({
  imports: [CommonModule, FormsModule, MaterialModule, BlockUIModule, ReactiveFormsModule],
  exports: [
    CommonModule,
    FormsModule,
    MaterialModule,
    BlockUIModule,
    ReactiveFormsModule,
    AutoCompleteComponent,
    DialogDataComponent,
    SafeHtmlPipe
  ],
  declarations: [AutoCompleteComponent, DialogDataComponent, SafeHtmlPipe]
})
export class SharedModule { }

Enjoy :)

Shubhendu Vaid
  • 567
  • 3
  • 17
  • Hi Shubhendu, I've tried to write a service, but I can't seem to get it to work either. – Mick Oct 18 '17 at 07:12
  • please define the problem statement again as it is not clear, then i will try to help. – Shubhendu Vaid Oct 18 '17 at 07:14
  • Sure, the problem is that I load some html from another page. I'd like the interpolation to work on the html that I just loaded so I can see the interpolated result in the view rather than the plain text. In other words I'm seeing {{ theme.name }} in the view, but what I want to see in the view is the data configured in the theme configuration file which could be "Yellow Theme" instead. – Mick Oct 18 '17 at 07:17
  • I've never used plnkr before, but I've put most of the code (not working) in http://plnkr.co/edit/X2jYniwfVs0SK7WQPMm7 . Nothing I've tried has actually worked so far – Mick Oct 19 '17 at 02:29
0

I suggest you update your <string> f['_body']; change to <string>f.text(), and also innerHTML = "{{innerHtml}}" to [innerHTML]="view" anyway check the below plnkr link as it's doing exactly what you are trying to perform

this._http.get(link).subscribe(f => {
     this.loading = false;
            var content = <string>f.text();
            this.view = content.replace("{{theme.Name}}", this.theme.name);
            }, (error) => {
              this.loading = false;
              console.error(error);
                alert(error);
            });

the template is like this

content <button (click)="open('external.html')">Open Page</button>
      <strong *ngIf="loading">LOADING...</strong>
      <div [innerHTML]="view"></div>

the external.html is simple as below

me playing around with this theme with name 
<b>
  {{theme.Name}}
</b>

here is the running Plnkr

But for String interpolation processing as if the content was in the same template as the parent loading it and bind this to the template scope, which is similar to angular 1 ng-include check this answer as it helps in solving that (instead of re doing it), and note this is for angular 4 and above

using Angular 4.0.0-beta.6's ngComponentOutlet.

Theophilus Omoregbee
  • 2,463
  • 1
  • 22
  • 33
  • Thanks for the feedback Theophilus, unfortunately when I use "[innerHTML]="view" " as you describe above I get {{theme.Name}} on the view instead of the data, so I still need to pretend I'm doing the interpolation using a "replace" command whereas I'd prefer that the observable handle the replacement as it does normally. So instead of seeing {{theme.Name}} I'd like to see "Yellow Theme" (or whatever I put in the config file). – Mick Oct 18 '17 at 07:10
  • check the plnkr link, it replaces it, or you are trying to make angular take the loaded innerHTML content to work as the way `ng-include` works? – Theophilus Omoregbee Oct 18 '17 at 07:30
  • yes. The idea is that I have multiple themes. I have some files that are common among those themes. So I want to load in components without loading a common theme, because I want to use the originating theme and not change it. – Mick Oct 18 '17 at 07:38
  • I had a look at the plunkr code, it still has "this.view = content.replace("{{theme.Name}}", this.theme.name);" which is the line of code I want to remove, but still have it look the same as what you have in the plunkr – Mick Oct 18 '17 at 07:42
  • In other words, both your code and my code work, but with magic strings. I'd prefer not to have magic strings in the code. It's for a big project, so if I can get this one page working I can refactor the whole solution. – Mick Oct 18 '17 at 07:44
  • updating the answer to help you solve this, with reference too @Mick – Theophilus Omoregbee Oct 18 '17 at 09:40
  • Thanks, I tried using a pipe, but that didn't do the interpolation either. So it seems the problem is with interpolation not working, and trapping it with Middleware doesn't seem to achieve that. – Mick Oct 18 '17 at 23:50
  • checkout my answer, i added the possible way by using **ngComponentOutlet.** – Theophilus Omoregbee Oct 18 '17 at 23:52