Кто-нибудь знает, как получить элемент, определенный в шаблоне компонента? Полимер делает это очень легко с $
и $$
.
Мне просто интересно, как это сделать в Angular.
Возьмем пример из учебника:
import {Component} from '@angular/core'
@Component({
selector:'display'
template:'
<input #myname(input)="updateName(myname.value)"/>
<p>My name : {{myName}}</p>
'
})
export class DisplayComponent {
myName: string = "Aman";
updateName(input: String) {
this.myName = input;
}
}
Как я могу уловить ссылку элемента p
или input
из определения класса?
Вместо того, чтобы вводить ElementRef
и использовать querySelector
или аналогичный оттуда, вместо этого можно использовать декларативный способ для прямого доступа к элементам в представлении:
<input #myname>
@ViewChild('myname') input;
элемент
ngAfterViewInit() {
console.log(this.input.nativeElement.value);
}
@ViewChildren('var1,var2,var3')
).<ng-content>
проецируемых элементов).потомки
@ContentChildren()
- единственный, который позволяет также запрашивать потомков
@ContentChildren(SomeTypeOrVarName, {descendants: true}) someField;
{descendants: true}
должны быть по умолчанию, но не в версии 2.0.0, и это считается ошибкой
Это было исправлено в 2.0.1
читать
Если есть компонент и директивы, параметр read
позволяет указать, какой экземпляр должен быть возвращен.
Например ViewContainerRef
который требуется динамически созданным компонентам вместо стандартного ElementRef
@ViewChild('myname', { read: ViewContainerRef }) target;
подписаться на изменения
Несмотря на то, что дети с просмотром задаются только при ngAfterViewInit()
а дети содержимого устанавливаются только после ngAfterContentInit()
, если вы хотите подписаться на изменения результата запроса, это должно быть сделано в ngOnInit()
https://github.com/angular/angular/issues/9689#issuecomment-229247134
@ViewChildren(SomeType) viewChildren;
@ContentChildren(SomeType) contentChildren;
ngOnInit() {
this.viewChildren.changes.subscribe(changes => console.log(changes));
this.contentChildren.changes.subscribe(changes => console.log(changes));
}
прямой доступ DOM
может запрашивать только элементы DOM, но не компоненты или директивные экземпляры:
export class MyComponent {
constructor(private elRef:ElementRef) {}
ngAfterViewInit() {
var div = this.elRef.nativeElement.querySelector('div');
console.log(div);
}
// for transcluded content
ngAfterContentInit() {
var div = this.elRef.nativeElement.querySelector('div');
console.log(div);
}
}
получить произвольный прогнозируемый контент
Вы можете получить дескриптор элемента DOM через ElementRef
, вставив его в свой конструктор компонента:
constructor(myElement: ElementRef) { ... }
Документы: https://angular.io/docs/ts/latest/api/core/index/ElementRef-class.html
ElementRef
ушел.
ElementRef
доступен (снова?).
import { Component, ElementRef, OnInit } from '@angular/core';
@Component({
selector:'display',
template:`
<input (input)="updateName($event.target.value)">
<p> My name : {{ myName }}</p>
`
})
class DisplayComponent implements OnInit {
constructor(public element: ElementRef) {
this.element.nativeElement // <- your direct element reference
}
ngOnInit() {
var el = this.element.nativeElement;
console.log(el);
}
updateName(value) {
// ...
}
}
Пример обновлен для работы с последней версией
Подробнее о собственном элементе здесь
Angular 4 +:
Используйте renderer.selectRootElement
с помощью селектора CSS для доступа к элементу.
У меня есть форма, которая первоначально отображает ввод электронной почты. После ввода электронной почты форма будет расширена, чтобы они могли продолжать добавлять информацию, относящуюся к их проекту. Однако, если они являются не существующим клиентом, форма будет содержать адресный раздел над секцией информации о проекте.
На данный момент часть ввода данных не была разбита на компоненты, поэтому секции управляются с помощью директив * ngIf. Мне нужно установить фокус на поле проектных заметок, если они являются существующим клиентом или поле имени, если они новы.
Я пробовал решения без успеха. Однако Update 3 в этом ответе дал мне половину возможного решения. Другая половина - ответ MatteoNY в этот поток. В результате получится следующее:
import { NgZone, Renderer } from '@angular/core';
constructor(private ngZone: NgZone, private renderer: Renderer) {}
setFocus(selector: string): void {
this.ngZone.runOutsideAngular(() => {
setTimeout(() => {
this.renderer.selectRootElement(selector).focus();
}, 0);
});
}
submitEmail(email: string): void {
// Verify existence of customer
...
if (this.newCustomer) {
this.setFocus('#firstname');
} else {
this.setFocus('#description');
}
}
Поскольку единственное, что я делаю, это установка фокуса на элемент, мне не нужно заниматься детекцией изменений, поэтому я могу фактически запустить вызов renderer.selectRootElement
вне Angular. Поскольку мне нужно предоставить время для рендеринга новых секций, секция элемента завершается в тайм-аут, чтобы разрешить время потоков рендеринга до того, как будет предпринята попытка выбора элемента. После того, как все это будет установлено, я могу просто вызвать элемент с помощью основных селекторов CSS.
Я знаю, что этот пример касается в основном фокусного события, но мне трудно, что это нельзя использовать в других контекстах.
Для людей, пытающихся захватить экземпляр компонента внутри *ngIf
или *ngSwitchCase
, вы можете выполнить этот трюк.
Создать директиву init
.
import {
Directive,
EventEmitter,
Output,
OnInit,
ElementRef
} from '@angular/core';
@Directive({
selector: '[init]'
})
export class InitDirective implements OnInit {
constructor(private ref: ElementRef) {}
@Output() init: EventEmitter<ElementRef> = new EventEmitter<ElementRef>();
ngOnInit() {
this.init.emit(this.ref);
}
}
Экспортируйте свой компонент с именем, например myComponent
@Component({
selector: 'wm-my-component',
templateUrl: 'my-component.component.html',
styleUrls: ['my-component.component.css'],
exportAs: 'myComponent'
})
export class MyComponent { ... }
Используйте этот шаблон для получения ElementRef
И myComponent
экземпляра
<div [ngSwitch]="type">
<wm-my-component
#myComponent="myComponent"
*ngSwitchCase="Type.MyType"
(init)="init($event, myComponent)">
</wm-my-component>
</div>
Используйте этот код в TypeScript
init(myComponentRef: ElementRef, myComponent: MyComponent) {
}
импортируйте декоратор ViewChild из @angular/core, например:
<form #f="ngForm"> ... </form>
import { ViewChild } from '@angular/core';
class TemplateFormComponent {
@ViewChild('f') myForm: any;
.
.
.
}
теперь вы можете использовать объект myForm для доступа к любому элементу внутри него в классе.
источник: https://codecraft.tv/courses/angular/forms/template-driven/
*/
import {Component,ViewChild} from '@angular/core' /*Import View Child*/
@Component({
selector:'display'
template:`
<input #myname (input) = "updateName(myname.value)"/>
<p> My name : {{myName}}</p>
`
})
export class DisplayComponent{
@ViewChild('myname')inputTxt:ElementRef; /*create a view child*/
myName: string;
updateName: Function;
constructor(){
this.myName = "Aman";
this.updateName = function(input: String){
this.inputTxt.nativeElement.value=this.myName;
/*assign to it the value*/
};
}
}
inputTxt
к значению в конструкторе приведет к неопределенному значению для inputTxt
.
Я хотел бы добавить, что если вы используете ElementRef
, как рекомендовано всеми ответами, то вы сразу столкнетесь с проблемой, что ElementRef
имеет ужасное объявление типа, которое выглядит как
export declare class ElementRef {
nativeElement: any;
}
Это глупо в среде браузера, где nativeElement является HTMLElement
.
Чтобы обойти это, вы можете использовать следующую технику
import {Inject, ElementRef as ErrorProneElementRef} from '@angular/core';
interface ElementRef {
nativeElement: HTMLElement;
}
@Component({...}) export class MyComponent {
constructor(@Inject(ErrorProneElementRef) readonly elementRef: ElementRef) { }
}
item
должен быть ElementRef, даже если вы устанавливаете его для другого ElementRef: let item:ElementRef, item2:ElementRef; item = item2; // no can do.
, Очень запутанно. Но это нормально: let item:ElementRef, item2:ElementRef; item = item2.nativeElement
из-за реализации, которую вы указали.
let item: ElementRef, item2: ElementRef; item = item2
терпит неудачу из-за анализа определенного присваивания. Ваш второй сбой по тем же причинам, но оба успешны, если item2
инициализируется по рассмотренным причинам (или как полезная быстрая проверка назначаемости, которую мы можем использовать, declare let
здесь). Независимо от того, действительно позорно видеть any
на общедоступном API как этот.
Выбор целевого элемента из списка. Легко выбрать конкретный элемент из списка тех же элементов.
код компонента:
export class AppComponent {
title = 'app';
listEvents = [
{'name':'item1', 'class': ''}, {'name':'item2', 'class': ''},
{'name':'item3', 'class': ''}, {'name':'item4', 'class': ''}
];
selectElement(item: string, value: number) {
console.log("item="+item+" value="+value);
if(this.listEvents[value].class == "") {
this.listEvents[value].class='selected';
} else {
this.listEvents[value].class= '';
}
}
}
html-код:
<ul *ngFor="let event of listEvents; let i = index">
<li (click)="selectElement(event.name, i)" [class]="event.class">
{{ event.name }}
</li>
Код css:
.selected {
color: red;
background:blue;
}
чтобы получить ближайшего следующего брата, используйте этот
event.source._elementRef.nativeElement.nextElementSibling
input
также являетсяElementRef
, но вы получаете ссылку на элемент, который вам действительно нужен, вместо того, чтобы запрашивать его у хостаElementRef
.