0

I have an Angular 4 app and a component, which generate some markup dynamically. And I need to bind this component's method to some element in this markup. The only way, that I found is to generate this element with onclick attribute. And so now my component's constructor looks like that:

constructor(private _zone: NgZone) { 
    window['Component'] = {
      myMethod: this.myMethod.bind(this),
      zone: _zone
    }
}

...and the peice of my generated markup looks like that:

<button onclick="window.Component.myMethod()">I was generated dynamically after Angular compilation, so you can't use '(click)="method(params)"' on me! Haha!</button>

And it works! Almost... After clickin the button I need some event on window to be fired (e.g. focus or blur), only then my method runs. Please, anybody, help.

2 Answers2

0

Looks like the problem was that when I run the method outside the Angular zone, UI douesn't refresh. So i did this:

(<any>window).myMethod = (params) => this._zone.run(
    () => {
        this.myMethod(params);
    });

Don't know how good this solution is, but it works fine.

UPD: Oops, not fine. The method doesn't run the first time I click the button, after that everything's ok.

0

you have to use : this.zone.runOutsideAngular

i have attach sample to illustrate where you have button out of angular and event Binding inside component. As illustrate on my sample, don't forget to unbind your event onDestroy.

this.zone.runOutsideAngular(() => {
  document.querySelector('#btn').addEventListener('click', () => {
    this.foo();
  });      
});

https://stackblitz.com/edit/angular-xtkw3z?file=app%2Fapp.component.ts


update 1:

I have create custom directive dedicated to bind click event on your button.

I have remove DOMSanitizer because we add Event.stopPropagation() automatically. So you have to do your own control if string is safe or not.

@Directive({
  selector: '[profil]'
})
export class UserProfilDirective implements OnInit, AfterViewInit {
    @Input('profil') html: string;

    constructor(
        private el: ElementRef,
        private zone: NgZone,
        private renderer: Renderer2
    ) {
    }
    ngOnInit() {
        //Take your html and add it as child html if current directive instance.
        this.el.nativeElement.innerHTML = this.html;
    }
    ngAfterViewInit() {
        //Out of angular.
        this.zone.runOutsideAngular(() => {
            // For each .profil of current instance.
            [].forEach.call(this.el.nativeElement.querySelectorAll('.profil'), (el:Element, index: number) => {
                //Ask rendering to add onClick event.
                this.renderer.listen(el, 'click', (e) => {
                  //Do what ever you want with data profil such as output Event
                  console.log(e.srcElement.getAttribute('data-profile'));
                });
            });
        });
    }
}

Source code here : https://stackblitz.com/edit/angular-cua3is?file=app/app.component.ts

Yanis-git
  • 7,737
  • 3
  • 24
  • 41