34

I am writing a component where I need access to the <audio controls> native element. I am doing it like this now by getting it in ngOnInit() by using ElementRef like this this.elementRef.nativeElement.querySelector("audio");

While it works I think it is very unelegant and Angular2 also warns of the risks when using ElementRef..

Is there really no simpler way?

Can I mark it as a local variable with <audio controls #player> and somehow access the native element through this.player or something similar from the controller?

import {Component, OnInit, ElementRef, Input} from 'angular2/core';

@Component({
    selector: 'audio-preview',
    template: `
        <audio controls>
            <source [src]="src" type="audio/mpeg">
            Your browser does not support the audio element.
        </audio>
    `
})

export class AudioPreview implements OnInit {

    @Input() src: string;

    constructor(public elementRef: ElementRef) {}

    ngOnInit() {
        var audioElement = this.getAudioElement();
        audioElement.setAttribute('src', this.src);
    }

    getAudioElement() : HTMLAudioElement {
        return this.elementRef.nativeElement.querySelector("audio");
    }
}
skovmand
  • 4,372
  • 5
  • 25
  • 30

1 Answers1

59
  1. Use @ViewChild to access some element in the view.
  2. Use [attr.src] to creating binding to 'src' attribute of an element.
  3. Use Renderer if for some reason you need to change the DOM imperatively.

See this plunk.

import {Component, Input, ViewChild, Renderer} from 'angular2/core';

@Component({
  selector: 'audio-preview',
  template: `
    <audio controls #player [attr.src]="src">
      <source [src]="src" type="audio/mpeg">
      Your browser does not support the audio element.
    </audio>
    `
})
export class AudioPreview {
  @Input() src: string;
  @ViewChild('player') player;

  constructor(public renderer: Renderer) {}

  ngAfterViewInit() {
    console.log(this.player);

    // Another way to set attribute value to element
    // this.renderer.setElementAttribute(this.player, 'src', this.src);
  }
}
alexpods
  • 47,475
  • 10
  • 100
  • 94
  • Thanks!! I just tried it, and ``` – skovmand Dec 29 '15 at 21:52
  • 1
    @skovmand [attr.src] create binding to the attribute of an element, using [`setAttribute()`](https://developer.mozilla.org/en/docs/Web/API/Element/setAttribute) method of DOM (`element.setAttribute('src', src)`). `[src]` creates binding to the property of an element (just set it by `element.src = src`) – alexpods Dec 29 '15 at 21:56
  • I have to ask... where did you learn about using `#player` in the template, and then being able to get a reference to it using `ViewChild('player')`? (I need to start reading whatever you're reading, because I can't find this kind of info in the Angular docs. The [ViewChild API page](https://angular.io/docs/ts/latest/api/core/ViewChild-var.html) doesn't explain this.) – Mark Rajcok Dec 30 '15 at 00:06
  • 1
    I found it... eventually. The [source code for ViewChild](https://github.com/angular/angular/blob/2.0.0-beta.0/modules/angular2/src/core/metadata.ts#L1147-L1147) says "Supports the same querying parameters as QueryMetadata, except `descendants`." The [QueryMetadata API doc](https://angular.io/docs/ts/latest/api/core/QueryMetadata-class.html) explains the different selectors supported. – Mark Rajcok Dec 30 '15 at 05:28
  • 1
    @MarkRajcok Actually I don't read some "secret" special books or articles about angular2. Almost all things about angular2 I've learnt from the source codes and their tests. For example [here](https://github.com/angular/angular/blob/9e44dd85ada181b11be869841da2c157b095ee07/modules/angular2/test/core/linker/query_integration_spec.ts#L341) are the tests for `@Query('something')`. – alexpods Dec 30 '15 at 13:49