Comment puis-je sélectionner un élément dans un modèle de composant?
quelqu'un sait comment obtenir un élément défini dans un modèle de composant? Le polymère le rend très facile avec les $
et $$
.
je me demandais comment procéder en angle.
Prenez l'exemple du tutoriel:
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;
}
}
Comment puis-je saisir une référence de l'élément p
ou input
dans la définition de la classe?
10 réponses
au lieu d'injecter ElementRef
et d'utiliser querySelector
ou similaire à partir de là, une méthode déclarative peut être utilisée à la place pour accéder directement aux éléments de la vue:
<input #myname>
@ViewChild('myname') input;
élément
ngAfterViewInit() {
console.log(this.input.nativeElement.value);
}
- @ViewChild () supporte la directive ou le type de Composant paramètre, ou le nom (chaîne de caractères) d'une variable de modèle.
- @ViewChildren () supporte aussi une liste de noms séparés par des virgules (actuellement pas d'espaces permis
@ViewChildren('var1,var2,var3')
). - @ContentChild() et @ContentChildren() faire la même chose mais dans la lumière DOM ( "1519100920 projetées" éléments).
descendants
@ContentChildren()
est la seule qui permet également de rechercher des descendants
@ContentChildren(SomeTypeOrVarName, {descendants: true}) someField;
{descendants: true}
devrait être la valeur par défaut mais n'est pas en 2.0.0 final et c'est considéré comme un bug
Ce montant a été fixé au 2.0.11519200920"
lire
S'il y a un composant et des directives Le paramètre read
permet de spécifier quelle instance doit être retournée.
par exemple ViewContainerRef
qui est requis par les composants créés dynamiquement au lieu de la valeur par défaut ElementRef
@ViewChild('myname', { read: ViewContainerRef }) target;
abonnez-vous changements
même si les enfants de vue ne sont définis que lorsque ngAfterViewInit()
est appelé et les enfants de contenu ne sont définis que lorsque ngAfterContentInit()
est appelé, si vous voulez vous abonner les modifications apportées au résultat de la requête, il doit être fait dans les 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));
}
accès DOM direct
peut seulement interroger des éléments DOM, mais pas des composants ou des instances de directive:
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);
}
}
obtenir de l'arbitraire contenu projeté
vous pouvez obtenir une poignée à L'élément DOM via ElementRef
en l'injectant dans le constructeur de votre composant:
constructor(myElement: ElementRef) { ... }
Docs: https://angular.io/docs/ts/latest/api/core/index/ElementRef-class.html
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) {
// ...
}
}
exemple mis à jour pour fonctionner avec la dernière version
pour plus de détails sur l'élément natif, ici
angulaire 4+ :
Utilisez renderer.selectRootElement
avec un sélecteur CSS pour accéder à l'élément.
j'ai un formulaire qui affiche initialement une entrée de courrier électronique. Une fois le courriel entré, le formulaire sera élargi pour leur permettre de continuer à ajouter de l'information relative à leur projet. Toutefois, s'il s'agit de et non de , le formulaire comprendra une section sur l'adresse au-dessus de la section des renseignements sur le projet.
à l'heure actuelle, la partie saisie des données n'a pas été divisée en composantes, de sorte que les sections sont gérées selon les directives *ngIf. Je dois mettre l'accent sur le champ Notes de projet s'il s'agit d'un client existant, ou sur le champ Prénom s'il s'agit d'un nouveau client.
j'ai essayé les solutions, sans succès. Cependant, la mise à jour 3 dans cette réponse m'a donné la moitié de la solution finale. L'autre moitié provient de la réponse de MatteoNY dans ce thread. Le résultat est le suivant:
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');
}
}
puisque la seule chose que je fais est de mettre l'accent sur un élément, Je n'ai pas besoin de me préoccuper de détection de changement, donc je peux effectivement lancer l'appel à renderer.selectRootElement
en dehors de L'angle. Parce que j'ai besoin de donner du temps aux nouvelles sections pour le rendu, la section element est enveloppée dans un timeout pour permettre aux threads de rendu de rattraper le temps avant que la sélection de l'élément ne soit tentée. Une fois que tout cela est mis en place, je peux il suffit d'appeler l'élément en utilisant les sélecteurs CSS de base.
je sais que cet exemple traitait principalement de l'événement focus, mais il est difficile pour moi que cela ne puisse pas être utilisé dans d'autres contextes.
pour les personnes qui tentent de saisir l'instance du composant à l'intérieur d'un *ngIf
ou *ngSwitchCase
, vous pouvez suivre cette astuce.
créer une directive 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);
}
}
exportez votre composant avec un nom tel que myComponent
@Component({
selector: 'wm-my-component',
templateUrl: 'my-component.component.html',
styleUrls: ['my-component.component.css'],
exportAs: 'myComponent'
})
export class MyComponent { ... }
utilisez ce modèle pour obtenir le ElementRef
et MyComponent
instance
<div [ngSwitch]="type">
<wm-my-component
#myComponent="myComponent"
*ngSwitchCase="Type.MyType"
(init)="init($event, myComponent)">
</wm-my-component>
</div>
utilisez ce code à la machine à écrire
init(myComponentRef: ElementRef, myComponent: MyComponent) {
}
importer le ViewChild décorateur de @angulaire de base, comme suit:
<form #f="ngForm"> ... </form>
import { ViewChild } from '@angular/core';
class TemplateFormComponent {
@ViewChild('f') myForm: any;
.
.
.
}
maintenant vous pouvez utiliser l'objet 'myForm' pour accéder à n'importe quel élément de la classe.
source: 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*/
};
}
}
je voudrais ajouter que si vous utilisez ElementRef
, comme recommandé par toutes les réponses, alors vous allez immédiatement rencontrer le problème que ElementRef
a une déclaration de type terrible qui ressemble à
export declare class ElementRef {
nativeElement: any;
}
c'est stupide dans un environnement de navigateur où nativeElement est un HTMLElement
.
Pour contourner cela, vous pouvez utiliser la technique suivante
import {Inject, ElementRef as ErrorProneElementRef} from '@angular/core';
interface ElementRef {
nativeElement: HTMLElement;
}
@Component({...}) export class MyComponent {
constructor(@Inject(ErrorProneElementRef) readonly elementRef: ElementRef) { }
}
pour obtenir le prochain frère immédiat ,utilisez ce
event.source._elementRef.nativeElement.nextElementSibling
sélectionnant l'élément cible dans la liste. Il est facile de sélectionner élément particulier de la liste des mêmes éléments.
code composant:
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= '';
}
}
}
code html:
<ul *ngFor="let event of listEvents; let i = index">
<li (click)="selectElement(event.name, i)" [class]="event.class">
{{ event.name }}
</li>
code css:
.selected {
color: red;
background:blue;
}