Search code examples
web-componentangularshadow-dom

Angular 2: How to call the method on a nested component?


I'm trying to call functions on an element declared in my Angular 2 component. The issue is that I don't know how to retrieve the element from my JS code. If I can pass the element from the template to the JS code, it works, but using document.querySelector does not return anything.

Example code (plunk):

@View({
  template: `
    <div>
      <span id="p1">{{name}}</span>
      <span #p2>{{name}}</span>
    </div>
  `,
  encapsulation: ViewEncapsulation.Native
})
export class Person {
  sayHello(e) {
    p1 = document.querySelector('p1');
    console.log('p1:', p1)
    p2 = document.querySelector('p2');
    console.log('p2:', p2)

    alert('VanillaId: ' + (p1 ? p1.innerHTML : 'null') +
          '\nAngularId: ' + (p2 ? p2.innerHTML : 'null'));
  }
}

I suspect that it has something to do with shadow dom, but I don't know how to get the shadow root and use it to do the query. this doesn't seem to expose anything useful to access the dom.


Solution

  • Use ElementRef see it here http://plnkr.co/edit/LISYnq?p=preview

    I did play around with your plunker:

    I don't know how to retrieve the element from my JS code

    It strikes me you might be able to just setup your component state in your js code and then mutate/display it usingg property binding, and communicate in/out with events.

    If you provide a more specific use case maybe we can offer more advice. Anyway, heres the code:

    person.ts

    //a simple person component
    import {Component, View, ViewEncapsulation, Input, ElementRef} from 'angular2/angular2'
    
    @Component({
      selector: 'my-person',
      inputs: ['name'],
      template: `
        <pre>
          <span> (Unsuspecting shadow dom node minding its own business)</span>
          <span #p0el> Name      : {{name}}</span>
          <span #p1el> Passed in : {{p1}}</span>
        </pre>
      `,
      encapsulation: ViewEncapsulation.Native
    })
    
    export class Person {
      public p1:string = "...";
      @Input('name') name:string;
      constructor (elementRef: ElementRef) {
        this.elementRef = elementRef;
      }
      sayHello(str) {
        this.p1 = str;
        this.elementRef.nativeElement.shadowRoot.querySelector('span').textContent = "BAM!"
    
    
      }
    }
    

    app.ts

    //our root app component
    import {Component, View, CORE_DIRECTIVES, ViewEncapsulation} from 'angular2/angular2'
    import {Person} from './person'
    
    @Component({
      selector: 'my-app',
      template: `
        <div>
          <!-- Passing the element here works fine -->
          <button (click)="person.sayHello('Clicked!') && person.name = 'Clicky name'">Test</button>
    
          <my-person #person [name]="'World'"></my-person>
        </div>`,
      directives: [CORE_DIRECTIVES, Person],
      encapsulation: ViewEncapsulation.Native
    })
    export class App {
      test(personComponent: Person) {
      }
    }