1

There is chance of possible duplicate but my scenario is bit different.

I want to perform click event for my dynamic component.

here is my structure :

     <razor>
          <mvc-partial>
            <dynamic-html> // buttonPress(){console.log("Function called in dynamicHtml)
                          // Output() to call function in razor.ts
            </dynamic-html>
          </mvc-partial>
      <razor>

RenderingViewDynamic.ts file

import {
    Component,
    Directive,
    NgModule,
    Input,
    Output,
    EventEmitter,
    ViewContainerRef,
    Compiler,
    ComponentFactory,
    ModuleWithComponentFactories,
    ComponentRef,
    ReflectiveInjector, OnInit, OnDestroy, ViewChild
} from '@angular/core';

import { RouterModule } from '@angular/router';
import { CommonModule } from '@angular/common';
import { Http } from "@angular/http";
import 'rxjs/add/operator/map';

export function createComponentFactory(compiler: Compiler, metadata: Component): Promise<ComponentFactory<any> | undefined>{   
    console.log(compiler)
    console.log(metadata)
    class DynamicComponent {
        @Output() buttonType: EventEmitter<string> = new EventEmitter<string>()

        // button click operation
        buttonPress() {
            this.buttonType.emit();
        }
    };
    const decoratedCmp = Component(metadata)(DynamicComponent);

    @NgModule({ imports: [CommonModule, RouterModule], declarations: [decoratedCmp] })
    class DynamicHtmlModule { }

    return compiler.compileModuleAndAllComponentsAsync(DynamicHtmlModule)
        .then((moduleWithComponentFactory: ModuleWithComponentFactories<any>) => {
            console.log(decoratedCmp)
            console.log(moduleWithComponentFactory.componentFactories.find(x => x.componentType === decoratedCmp))
            return moduleWithComponentFactory.componentFactories.find(x => x.componentType === decoratedCmp);
        });
}

@Component({
    selector: 'mvc-partial',
    template: `<div #dynamicHtml></div>`
})
//@Directive({ selector: 'mvc-partial' })
export class RenderingViewDynamic implements OnInit {
    @ViewChild('dynamicHtml', { read: ViewContainerRef }) target: ViewContainerRef;
    html: string = '<p></p>';
    @Input() url: string;
    cmpRef: ComponentRef<any>;

    constructor(private vcRef: ViewContainerRef, private compiler: Compiler, private http: Http) { }

    ngOnInit() {
        this.http.get(this.url)
            .map(res => res.text())
            .subscribe(
            (html) => {
                this.html = html;
                if (!html) return;

                if (this.cmpRef) {
                    this.cmpRef.destroy();
                }

                const compMetadata = new Component({
                    selector: 'dynamic-html',
                    template: this.html,
                });

                createComponentFactory(this.compiler,compMetadata)
                    .then(factory => {
                        //const injector = ReflectiveInjector.fromResolvedProviders([], this.vcRef.parentInjector);
                        this.cmpRef = this.target.createComponent(factory!);
                    });
            },
            err => console.log(err),
            () => console.log('MvcPartial complete')
            );

    }

    ngOnDestroy() {
        if (this.cmpRef) {
            this.cmpRef.destroy();
        }
    }  

}

razor.component.html

<mvc-partial [url]="'/View/Index'" (buttonType)="click()"></mvc-partial>

razor.component.ts

click(){ console.log("In razor") }

My question is, button is in my dynamic-html want to bind it event to function in razor.ts.

like <dynamic-html>-<mvc-partial>-<razor> how could I achieve it?

update: Trying to communicate with service

class DynamicComponent {
        constructor(private appService: AppService) { }

        buttonPress() {
            this.appService.onButtonClickAction.emit()
        }
    };
    const decoratedCmp = Component(metadata)(DynamicComponent);

    @NgModule({ imports: [CommonModule, RouterModule], declarations: [decoratedCmp], providers: [AppService] })

Error pops: Can't resolve all parameters for DynamicComponent: (?).

enter image description here

Above Error solved by adding @Inject(forwardRef(() => AppService)

constructor( @Inject(forwardRef(() =>  AppService)) private appService: AppService) { } 
k11k2
  • 2,036
  • 2
  • 22
  • 36
  • You can't use `@Output()` or `@Input()` with dynamically added components. See the commented-out code in the first code block in my answer in https://stackoverflow.com/questions/36325212/angular-2-dynamic-tabs-with-user-click-chosen-components/36325468#36325468 to see how to communicate with dynamically added components. A shared service can be used as well. – Günter Zöchbauer Aug 24 '17 at 10:30
  • @GünterZöchbauer https://stackoverflow.com/questions/40725620/angular-2-dynamic-component-click-event how it going to help me ? – k11k2 Aug 24 '17 at 10:37
  • To add a click handler to dynamically added HTML – Günter Zöchbauer Aug 24 '17 at 10:47
  • but my question is about `DynamicComponent` having button I'm handling it in DynamicComponent. `buttonPress() { this.buttonType.emit(); }` check my question. `buttonType()` is the one in `razor.ts` need to call. as your first comment it may possible aka shared service but duplication not justify. – k11k2 Aug 24 '17 at 10:53
  • I see. I thought that was added after it was marked as duplicate. Sorry, for the mistake. – Günter Zöchbauer Aug 24 '17 at 10:56
  • @GünterZöchbauer In class `DynamicComponent` added service like this `class DynamicComponent { constructor(public appService: AppService){} buttonPress() { this.appService.onButtonClickAction.emit() //shared service } };` but at `return compiler.compileModuleAndAllComponentsAsync(DynamicHtmlModule)` pops error as `Can't resolve all parameters for DynamicComponent: (?).` what would be the reason? imported service `import { AppService } from './service';` – k11k2 Aug 24 '17 at 14:16
  • Where do you provide the AppService? – Günter Zöchbauer Aug 24 '17 at 14:19
  • @GünterZöchbauer like this `const decoratedCmp = Component(metadata)(DynamicComponent); @NgModule({ imports: [CommonModule, RouterModule], declarations: [decoratedCmp], providers: [AppService] })` – k11k2 Aug 24 '17 at 15:07
  • @k11k2, so as I understand your problem is solved. You can also read [What is `forwardRef` in Angular and why we need it](https://blog.angularindepth.com/what-is-forwardref-in-angular-and-why-we-need-it-6ecefb417d48) – Max Koretskyi Aug 27 '17 at 05:15

1 Answers1

0

You can bubble up the click event from your child to your parent by using EventEmitter:

clickEmitter = new EventEmitter();

clickEmitter.emit();

<your-child-component (clickEmitter)="functionInParent()"></your-child-component>

Edit: If you want to reach your child component as per your comment: <dynamic-html>-<mvc-partial>-<razor> you can reference the components using the hashtag (#) and name of the hashtag or by the component name

@ViewChild(MvcPartialComponent) mvcPartialComponent: MvcPartialComponent;

<mvc-partial #mvc-partial></mvc-partial>
@ViewChild('mvc-partial') mvcPartialComponent: MvcPartialComponent;

etc etc.

Carsten
  • 4,005
  • 21
  • 28
  • unable to do it for the above scenario. cause flow is `--` button is in `dynamic-html` and function should be called in `razor` – k11k2 Aug 24 '17 at 10:25
  • Then you can just set a reference on the components and call the function directly. See my updated answer – Carsten Aug 24 '17 at 10:42
  • yea. but the thing is my button is in `dynamic-html` I'm handling it in respective `DynamicComponent` so reference of `MvcPartialComponent` could n't work out. Parent`razor`-Child`mvc-partial`-subChild(DynamicComponent)`dynamic-html` – k11k2 Aug 24 '17 at 10:58
  • You can just create a named and check if it's defined . i really don't see what the problem is. – Carsten Aug 24 '17 at 11:19
  • Let me try.If you see my `RenderingViewDynamic ` I'm getting `Index.cshtml` from `mvc` which contains ` – k11k2 Aug 24 '17 at 11:37
  • Then use @Output event emitters to bubble the event up to the parent like i said... – Carsten Aug 24 '17 at 11:53
  • Once check Günter Zöchbauer first comment. Though Output() not working cause dynamicComponent is injected at reference and reference at razor.so razor to dynamicComponent communication making difficult. – k11k2 Aug 24 '17 at 11:57
  • So? You need to emit twice? dynamic -> emit to partial, partial -> emit to razor. – Carsten Aug 24 '17 at 12:11
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/152765/discussion-between-k11k2-and-carsten). – k11k2 Aug 24 '17 at 13:24