3

I want to show a popover as the user clicks on the input field which works fine but I want the data-content attribute of that popover be coming from the template of a child component. Here is an example:

parent.ts

import {Component,AfterViewInit} from 'angular2/core';
import {bootstrap} from 'angular2/platform/browser';
import {ChildComponent} from './child_test.ts';


@Component({
    selector: 'app',
    template: `<input type='text' data-toggle="popover" data-trigger="focus" data-placement="bottom" [attr.data-content]="getPopoverContent()" />`,
    providers: [ChildComponent]
})
class AppComponent implements AfterViewInit{
    constructor(private _child: ChildComponent) {}

    getPopoverContent(){
        return this._child; //returning empty object instead of child template
    }
    ngAfterViewInit(){
        $("input").popover();
    }

}

bootstrap(AppComponent);

child.ts

import {Component} from 'angular2/core';

@Component({
    selector: "child-component",
    template: "<div>Popover content from child.</div>"
})
export class ChildComponent{};

Should I use DynamicComponentLoader instead of dependency injection? if so then how can I achieve this?

Eesa
  • 2,762
  • 2
  • 32
  • 51
  • 1
    Why do you have the Component in providers? It should be on directives and you should query for it, not inject it like that. – Eric Martinez Feb 28 '16 at 14:09

2 Answers2

0

Here's a workaround:

Assign the a temporary variable to the component you want to display

<transaction-filter #popoverComponent></transaction-filter>

Important: The component above must expose an ElementRef in its definition

constructor(public el: ElementRef) {}

Create the element that will show the popover

<button class="btn-sm btn-link text-muted"
    data-animation="true"
    data-placement="bottom"
    title="Create Rule"
    [popover]="popoverComponent">

    Create Rule...
</button>

Now the popover directive itself:

/// <reference path="../../typings/tsd.d.ts"/>

import 'bootstrap'

import { Directive, ElementRef, Input} from 'angular2/core'

declare var $: JQueryStatic;

@Directive({
    selector: '[popover]',
})
export class PopoverDirective {
    @Input('popover') _component: any
    _popover: JQuery

    constructor(private _el: ElementRef) { }

    ngOnInit() {
        // Hide the component
        this._component.el.nativeElement.style.display = "none"

        // Attach it to the content option
        this._popover = $(this._el.nativeElement)
            .popover({
            html: true,
            content: this._component.el.nativeElement
        })

        // When the below event fires, the component will be made visible and will remain
        this._popover.on('shown.bs.popover', () => {
            this._component.el.nativeElement.style.display = "block"
        })
    }
}
John Wheeler
  • 797
  • 1
  • 7
  • 21
-1

One problem is that binding to an attribute stringifies the value

 [attr.data-content]

therefore this approach won't work.

It seems the Bootstrap popover expects a string, therefore this would be fine but stringifying an Angular component won't you give it's HTML.

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • So you're saying that there is no way you can bind the value of an attribute to some child template? – Eesa Feb 28 '16 at 14:07
  • You'd need to add the component to the DOM and then read it's inner/outer-HTML in `ngAfterViewInit()`. That doesn't seem to be a proper solution. – Günter Zöchbauer Feb 28 '16 at 14:09
  • Why does it need to be a component. If you'd pass the HTML as string in the first place it would work out of the box. – Günter Zöchbauer Feb 28 '16 at 14:11
  • There might be a way to get the template string from the component instance but you can't expect any Angular bindings in it to work from HTML added this way. – Günter Zöchbauer Feb 28 '16 at 14:14
  • It needs to be a component because I want to give some input to it and use the resulting template as value of data-bnding attribute. – Eesa Feb 28 '16 at 15:09
  • I got it, but then you can't use an attribute to pass it. I don't know the Bootstrap popover well enough to tell if there are other ways to pass content. – Günter Zöchbauer Feb 28 '16 at 15:10
  • There is one other way we can pass content to the popover by using jquery as follows `$("input").popover({ content: "I want resulting child template here." })` – Eesa Feb 28 '16 at 15:18
  • If it needs a string here it still won't work with Angular binding. If you want to add an Angular component dynamically you have to use `DynamicComponentLoader`. You need a position from Bootstrap for DCL where to add the component. – Günter Zöchbauer Feb 28 '16 at 15:20
  • Is there any way to bind an angular2 function to the native event for example what if I want to do this `$("input").popover({ content: "callFunc` where someFunc() is defined in the parent component. – Eesa Feb 28 '16 at 17:38
  • You would need the popover to be shown, then you can query the ` – Günter Zöchbauer Feb 28 '16 at 17:42
  • But I don't want to pollute the global namespace by defining a function globally. Isn't there any way to call the function of parent component using native event? – Eesa Feb 28 '16 at 17:48
  • An event might work. `@HostBinding('some-event', [$event] ) myEventHandler(event) {...} ` or `@HostBinding('window:some-event', [$event] ) myEventHandler(event) {...} ` in case the popover content is added somewhere to `body`. I'm not sure if the syntax about is correct (just from my memory, I'm on my phone) – Günter Zöchbauer Feb 28 '16 at 17:53