PHPFixing
  • Privacy Policy
  • TOS
  • Ask Question
  • Contact Us
  • Home
  • PHP
  • Programming
  • SQL Injection
  • Web3.0

Wednesday, August 24, 2022

[FIXED] How to change html input value inside a grandchild component from grandparent component

 August 24, 2022     angular, components, input, module, typescript     No comments   

Issue

I have a WidgetsModule where I've created an input component

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>

outside the component, I can change the Value's value, but this value is not rendered inside html input. Givin an example, I have a clearFields(); function where I make input.Value = '0', making a console.log() of the component, shows me that the value was correctly changed, but when looking at the html the value is still there

this is the page's code where I render the input component

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>

I'v tried using @ViewChild annotations, EventEmitters, and I can't really remember what else (again stuck with this since the last week xD). I look forward for your answers and thanks in advance


Solution

From what I have tested, the problem is that Angular does not bind the variable in the "tipo-componente" component to the variable in the "input" component.

I think the best option you have is using eventEmitter. EventEmitter allows your component to send events. For example:

In your input.component.ts file:

@Output() setInput = new EventEmitter<string>();

onInput(evt:Event) : void {
   this.Value = (<HTMLInputElement>evt.target).value; 
   console.log(this.Value)
   this.setInput.emit(this.Value);
}

This will make your input component send a "setInput" event each time you change the input.

In your tipo-componente.component.html:

<app-input Nombre="inTipo" [Value]="tipo" Placeholder="Tipo de Componente" (setInput)="receiveInput($event)"></app-input>

This will make your app listen to the "setInput" event from app-input and call the receiveInput() method.

In your tipo-componente.component.ts:

receivedInput(event : string){
   this.tipo = event;
}

This will make your app change the value of the variable "tipo" in the "tipo-componente" component.

Fell free to ask for clarification if needed!

EDIT

Please note that the EventEmitter class is the one from @angular/core



Answered By - Manuel Tomás
Answer Checked By - Pedro (PHPFixing Volunteer)
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg
Newer Post Older Post Home

0 Comments:

Post a Comment

Note: Only a member of this blog may post a comment.

Total Pageviews

Featured Post

Why Learn PHP Programming

Why Learn PHP Programming A widely-used open source scripting language PHP is one of the most popular programming languages in the world. It...

Subscribe To

Posts
Atom
Posts
Comments
Atom
Comments

Copyright © PHPFixing