如何从祖父组件更改孙子组件内的 html 输入值
How to change html input value inside a grandchild component from grandparent component
我有一个 WidgetsModule,我在其中创建了一个输入组件
input.component.ts
import { AfterViewInit, Component, Input, OnInit, Output, ViewChild } from '@angular/core';
@Component({
selector: 'app-input',
templateUrl: './input.component.html',
styleUrls: ['./input.component.css']
})
export class InputComponent implements OnInit, AfterViewInit {
@Input() Nombre:string = '';
@Input() Placeholder:string = '';
@Input() Value:string = '';
constructor() { }
ngOnInit(): void { }
ngAfterViewInit(): void { this.bindFocusAnimation(); }
onInput(evt:Event):void{ this.Value = (<HTMLInputElement>evt.target).value; }
bindFocusAnimation():void{
var materialInput:HTMLInputElement = <HTMLInputElement>document.getElementById(this.Nombre);
if(materialInput.value && materialInput.value.trim().length > 0)
materialInput.parentElement.lastElementChild.setAttribute("class", "label-material active");
// move label on focus
materialInput.addEventListener("focus", function () {
materialInput.parentElement.lastElementChild.setAttribute("class", "label-material active");
});
// remove/keep label on blur
materialInput.addEventListener("blur", function () {
var css = "label-material";
if(materialInput.value !== undefined && materialInput.value.trim().length > 0)
css += " active";
materialInput.parentElement.lastElementChild.setAttribute("class", css);
});
}
}
input.component.html
<div class="input-material-group mb-3">
<input class="input-material" id="{{Nombre}}" type="text" name="{{Nombre}}" value="{{Value}}" (input)="onInput($event)" autocomplete="off">
<label class="label-material" for="{{Nombre}}">{{Placeholder}}</label>
</div>
在组件外部,我可以更改值的值,但此值不会在 html 输入内部呈现。
举个例子,我有一个 clearFields();
函数,我在其中制作 input.Value = '0'
,制作组件的 console.log()
,显示值已正确更改,但是当查看 html 值还在
这是我呈现输入组件的页面代码
tipo-componente.component.ts
import { Component, OnInit, ViewChild } from '@angular/core';
import { Constantes } from 'src/app/constantes';
import { ApiRequest } from 'src/app/interface/api-request.interface';
import { TipoComponente } from 'src/app/interface/tipo-componente.interface';
import { TipoComponenteService } from 'src/app/services/tipo-componente.service';
import { WidgetsModule } from 'src/app/widgets/widgets.module';
@Component({
selector: 'app-tipo-componente',
templateUrl: './tipo-componente.component.html',
styleUrls: ['./tipo-componente.component.css']
})
export class TipoComponenteComponent implements OnInit {
tiposComponentes:TipoComponente[];
id:number;
tipo:string;
icono:string;
constructor(private TipoComponenteSvc:TipoComponenteService) { }
ngOnInit(): void {
}
ngAfterViewInit() {
this.cargarTiposComponentes();
this.limpiarCampos();
}
cargarTiposComponentes():void{
const req:ApiRequest = {
Usuario:Constantes.usuario.Usuario,
Contrasenia:Constantes.usuario.Contrasenia,
Key:Constantes.usuario.Key,
RequestObject:{ Id:0, Descrip:'' }
};
// this.TipoComponenteSvc.cargar(req).pipe(
// tap(
// res => {
// if(res.Status){
// this.tiposComponentes = <TipoComponente[]>res.Data;
// }
// else
// console.log(res.Mensaje);
// }
// )
// ).subscribe();
}
limpiarCampos():void{
this.id = 0;
this.tipo = '0';
this.icono = '';
}
btnNuevo_Click():void{
this.limpiarCampos();
WidgetsModule.showPanel('pnlTipoComponente');
}
btnGuardar_Click():void{
WidgetsModule.hidePanel('pnlTipoComponente');
this.limpiarCampos();
}
btnEditar_Click(element:TipoComponente):void{
WidgetsModule.showPanel('pnlTipoComponente');
}
btnEliminar_Click(element:TipoComponente):void{
WidgetsModule.hidePanel('pnlTipoComponente');
this.cargarTiposComponentes();
}
}
tipo-componente.component.html
<section>
<div class="container-fluid">
<div class="row gy-4">
<div class="col-12">
<div class="card mb-0">
<div class="card-header">
<h3 class="h4 mb-0 title">Compact Table</h3>
<div class="w-auto tool-box">
<a class="tool-button new-button" id="btnNuevo" (click)="btnNuevo_Click()">
<svg class="svg-icon svg-icon-sm svg-icon-heavy">
<use xlink:href="#add-1"></use>
</svg> Nuevo
</a>
</div>
</div>
<div class="card-body pt-0">
<div class="row new-element-panel" id="pnlTipoComponente" style="display:none">
<input type="hidden" id="hfId" [value]="id"/>
<div class="col-3">
<app-input Nombre="inTipo" [Value]="tipo" Placeholder="Tipo de Componente"></app-input>
</div>
<div class="col-3">
<app-input Nombre="inIcono" [Value]="icono" Placeholder="Ícono"></app-input>
</div>
<div class="">
<a class="tool-button save-button" id="btnGuardar" (click)="btnGuardar_Click()">
<svg class="svg-icon svg-icon-sm svg-icon-heavy">
<use xlink:href="#add-1"></use>
</svg> Guardar
</a>
</div>
</div>
<div class="table-responsive">
<table class="table mb-0 table-striped table-sm">
<thead>
<tr>
<th>#</th>
<th>Descripción</th>
<th>Ícono</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let tipo of this.tiposComponentes; let index = index">
<th>{{ tipo.Id }}</th>
<td>{{ tipo.Descrip }}</td>
<td>{{ tipo.Icono }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
我尝试过使用@ViewChild 注释、EventEmitter,但我真的不记得还有什么(自上周以来再次坚持使用 xD)。期待您的回答并提前致谢
根据我的测试,问题是 Angular 没有将“tipo-componente”组件中的变量绑定到“输入”组件中的变量。
我认为您最好的选择是使用 eventEmitter。 EventEmitter 允许您的组件发送事件。例如:
在您的 input.component.ts 文件中:
@Output() setInput = new EventEmitter<string>();
onInput(evt:Event) : void {
this.Value = (<HTMLInputElement>evt.target).value;
console.log(this.Value)
this.setInput.emit(this.Value);
}
这将使您的输入组件在您每次更改输入时发送一个“setInput”事件。
在你的tipo-componente.component.html:
<app-input Nombre="inTipo" [Value]="tipo" Placeholder="Tipo de Componente" (setInput)="receiveInput($event)"></app-input>
这将使您的应用监听来自 app-input 的“setInput”事件并调用 receiveInput() 方法。
在你的tipo-componente.component.ts:
receivedInput(event : string){
this.tipo = event;
}
这将使您的应用更改“tipo-componente”组件中变量“tipo”的值。
如有需要,请随意询问!
编辑
请注意 EventEmitter class 来自@angular/core
我有一个 WidgetsModule,我在其中创建了一个输入组件
input.component.ts
import { AfterViewInit, Component, Input, OnInit, Output, ViewChild } from '@angular/core';
@Component({
selector: 'app-input',
templateUrl: './input.component.html',
styleUrls: ['./input.component.css']
})
export class InputComponent implements OnInit, AfterViewInit {
@Input() Nombre:string = '';
@Input() Placeholder:string = '';
@Input() Value:string = '';
constructor() { }
ngOnInit(): void { }
ngAfterViewInit(): void { this.bindFocusAnimation(); }
onInput(evt:Event):void{ this.Value = (<HTMLInputElement>evt.target).value; }
bindFocusAnimation():void{
var materialInput:HTMLInputElement = <HTMLInputElement>document.getElementById(this.Nombre);
if(materialInput.value && materialInput.value.trim().length > 0)
materialInput.parentElement.lastElementChild.setAttribute("class", "label-material active");
// move label on focus
materialInput.addEventListener("focus", function () {
materialInput.parentElement.lastElementChild.setAttribute("class", "label-material active");
});
// remove/keep label on blur
materialInput.addEventListener("blur", function () {
var css = "label-material";
if(materialInput.value !== undefined && materialInput.value.trim().length > 0)
css += " active";
materialInput.parentElement.lastElementChild.setAttribute("class", css);
});
}
}
input.component.html
<div class="input-material-group mb-3">
<input class="input-material" id="{{Nombre}}" type="text" name="{{Nombre}}" value="{{Value}}" (input)="onInput($event)" autocomplete="off">
<label class="label-material" for="{{Nombre}}">{{Placeholder}}</label>
</div>
在组件外部,我可以更改值的值,但此值不会在 html 输入内部呈现。
举个例子,我有一个 clearFields();
函数,我在其中制作 input.Value = '0'
,制作组件的 console.log()
,显示值已正确更改,但是当查看 html 值还在
这是我呈现输入组件的页面代码
tipo-componente.component.ts
import { Component, OnInit, ViewChild } from '@angular/core';
import { Constantes } from 'src/app/constantes';
import { ApiRequest } from 'src/app/interface/api-request.interface';
import { TipoComponente } from 'src/app/interface/tipo-componente.interface';
import { TipoComponenteService } from 'src/app/services/tipo-componente.service';
import { WidgetsModule } from 'src/app/widgets/widgets.module';
@Component({
selector: 'app-tipo-componente',
templateUrl: './tipo-componente.component.html',
styleUrls: ['./tipo-componente.component.css']
})
export class TipoComponenteComponent implements OnInit {
tiposComponentes:TipoComponente[];
id:number;
tipo:string;
icono:string;
constructor(private TipoComponenteSvc:TipoComponenteService) { }
ngOnInit(): void {
}
ngAfterViewInit() {
this.cargarTiposComponentes();
this.limpiarCampos();
}
cargarTiposComponentes():void{
const req:ApiRequest = {
Usuario:Constantes.usuario.Usuario,
Contrasenia:Constantes.usuario.Contrasenia,
Key:Constantes.usuario.Key,
RequestObject:{ Id:0, Descrip:'' }
};
// this.TipoComponenteSvc.cargar(req).pipe(
// tap(
// res => {
// if(res.Status){
// this.tiposComponentes = <TipoComponente[]>res.Data;
// }
// else
// console.log(res.Mensaje);
// }
// )
// ).subscribe();
}
limpiarCampos():void{
this.id = 0;
this.tipo = '0';
this.icono = '';
}
btnNuevo_Click():void{
this.limpiarCampos();
WidgetsModule.showPanel('pnlTipoComponente');
}
btnGuardar_Click():void{
WidgetsModule.hidePanel('pnlTipoComponente');
this.limpiarCampos();
}
btnEditar_Click(element:TipoComponente):void{
WidgetsModule.showPanel('pnlTipoComponente');
}
btnEliminar_Click(element:TipoComponente):void{
WidgetsModule.hidePanel('pnlTipoComponente');
this.cargarTiposComponentes();
}
}
tipo-componente.component.html
<section>
<div class="container-fluid">
<div class="row gy-4">
<div class="col-12">
<div class="card mb-0">
<div class="card-header">
<h3 class="h4 mb-0 title">Compact Table</h3>
<div class="w-auto tool-box">
<a class="tool-button new-button" id="btnNuevo" (click)="btnNuevo_Click()">
<svg class="svg-icon svg-icon-sm svg-icon-heavy">
<use xlink:href="#add-1"></use>
</svg> Nuevo
</a>
</div>
</div>
<div class="card-body pt-0">
<div class="row new-element-panel" id="pnlTipoComponente" style="display:none">
<input type="hidden" id="hfId" [value]="id"/>
<div class="col-3">
<app-input Nombre="inTipo" [Value]="tipo" Placeholder="Tipo de Componente"></app-input>
</div>
<div class="col-3">
<app-input Nombre="inIcono" [Value]="icono" Placeholder="Ícono"></app-input>
</div>
<div class="">
<a class="tool-button save-button" id="btnGuardar" (click)="btnGuardar_Click()">
<svg class="svg-icon svg-icon-sm svg-icon-heavy">
<use xlink:href="#add-1"></use>
</svg> Guardar
</a>
</div>
</div>
<div class="table-responsive">
<table class="table mb-0 table-striped table-sm">
<thead>
<tr>
<th>#</th>
<th>Descripción</th>
<th>Ícono</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let tipo of this.tiposComponentes; let index = index">
<th>{{ tipo.Id }}</th>
<td>{{ tipo.Descrip }}</td>
<td>{{ tipo.Icono }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
我尝试过使用@ViewChild 注释、EventEmitter,但我真的不记得还有什么(自上周以来再次坚持使用 xD)。期待您的回答并提前致谢
根据我的测试,问题是 Angular 没有将“tipo-componente”组件中的变量绑定到“输入”组件中的变量。
我认为您最好的选择是使用 eventEmitter。 EventEmitter 允许您的组件发送事件。例如:
在您的 input.component.ts 文件中:
@Output() setInput = new EventEmitter<string>();
onInput(evt:Event) : void {
this.Value = (<HTMLInputElement>evt.target).value;
console.log(this.Value)
this.setInput.emit(this.Value);
}
这将使您的输入组件在您每次更改输入时发送一个“setInput”事件。
在你的tipo-componente.component.html:
<app-input Nombre="inTipo" [Value]="tipo" Placeholder="Tipo de Componente" (setInput)="receiveInput($event)"></app-input>
这将使您的应用监听来自 app-input 的“setInput”事件并调用 receiveInput() 方法。
在你的tipo-componente.component.ts:
receivedInput(event : string){
this.tipo = event;
}
这将使您的应用更改“tipo-componente”组件中变量“tipo”的值。
如有需要,请随意询问!
编辑
请注意 EventEmitter class 来自@angular/core