Search code examples
angulartypescriptrxjsangular-materialsubject-observer

Why does it tell me that the value property of target does not exist if all html elements have a value?


I am doing a project related to API and I am using Angular, Angular Material, Typescrit and RXJS, the error is when trying to highlight a search bar, in this fragment of html code I get the error seen in the imagecode error this is the complete html5

div id="pagina">
  <br>
  <br>
  <video src="../../../../assets/videos/background-video.mp4" autoplay loop muted  type="video/mp4"></video>
  <div id="titulo">
    <h1>LOS CAMPEONES</h1>
    <br>
    <h2>Conoce a los campeones de las distintas regiones de Runeterra.</h2>
    <blockquote>¡Haz click en el botón saber más para descubrir más detalles o busca a tu campeón favorito!</blockquote>
  </div>
  <div id="separador">
    <img src="../../../../assets/images/decorator-hr-lg.png" alt="decorator-hr">
  </div>
  <div id="busqueda">

    <mat-form-field class="buscar">
      <input class="campo" type="text" matInput placeholder="Buscar un campeón" aria-label="Buscar un campeón"
      (keyup)="searchChampion.next($event.target?.value)">
      <!--cada vez que se presione una teclla(on key up s) se envia/emite ael valor de esa letra presionada
      ($even.target.value)y   $event es para manejar eventos (con esto manejo onkeyup), atarget es 
    la propiedad de event (equivale ha hacer un document.getElement  , document.querySelector, 
    es el DOM de angular , mas bien es el elemnto que disparo ese evento (la tecla pulsada valla)
    avalue es el valor del target p es p r es r lol es lol) 
  modo resumido cada vez que el usuario presiona una tecla y levanta el dedo(keyup) se searchCampion
lo emite(next), es decir se emite el valor de la tecla que el usuario pulse
funciona gracias a rxjs principalmente-->
      <button mat-button matSuffix>
        <mat-icon>search</mat-icon>
      </button>
    </mat-form-field>

  </div>

  <div id="container">
    <ng-template ngIf="isLoad">
      <div class="cartas" *ngFor="let champion of champions ">
        <mat-card>
          <mat-card-header id="campeones">

            <img mat-card-image [alt]="champion.name" [src]="'../../../../assets/img/icons/'+champion.name+'.jpeg'">
          </mat-card-header>
          <mat-card-content>

            <mat-card-title id="nombre"><h3>{{champion.name}}</h3></mat-card-title>
          </mat-card-content>
        </mat-card>
      </div>
    </ng-template>
  </div>

and this is the typescript

import { Component } from '@angular/core';
import { LolService } from '../../services/lol.service';
import {Datum } from '../../interfaces/champions.interface';
import { Subject } from 'rxjs';


@Component({
  selector: 'card-component',
  templateUrl: './card-component.component.html',
  styleUrl: './card-component.component.scss'
})
export class CardComponent{
  isLoad=false;
  searchChampion=new Subject<string>();//esto servira para manejar los eventos del buscador a apartir de obserables
  champions:Datum[]=[];
  championsFilter:Datum[]=[];//esto lo usare para filtar campeones en la barra de busqueda

  constructor(private lolService:LolService ){
  this.obtenerInfo();
  }
  searchChampions():void{//estafuncion  es para buscar por nombre
    this.searchChampion.subscribe(key =>{//el subject search champion se suscribe a cada observable que emite en cardcomponent html
      
      this.championsFilter = this.champions.filter(champ => champ.name.toLowerCase().includes(key.toLowerCase()));
      //despues lo va filtrandon  y guardando en un  arreglo  de objetos
      //de cada campeon (cada objeto del array ) coje el nombre(la propiedad name de cada objeto) la vuelve minuscula(uso la funcion toLoweCase para poner el nombre de cada la propiedad name de cada objeto champion del arreglo champions   en miniscula )
      //despues uso include para combaprobar si el nombre recibido(el string key convertido en minusculas) es gual al nombre del campeon actual(a la propiedad name del objeto actual champ)
    })
  }

  obtenerInfo(){
    this.lolService.obtenerChampInfo()
    .subscribe( champion=>{

      console.log(Object.values(champion.data))//
     this.champions=Object.values(champion.data);
      console.log(this.champions)
      this.isLoad=true
      // for (let champion of this.champions){ //para que no se me pete la memoria con las descargas al hacer un cambio u otro
      //   let url='https://gameriv.com/wp-content/uploads/2023/08/'+champion.name+'-Icon.jpeg'
      //   let nombreImagen:string=champion.name+'.jpeg';
      //   this.lolService.descargarImg(url,nombreImagen)
      //   console.log('imagen descargada')
      // }
    })
  }
}

How i can fix the error?

I put it optional because first it told me that it could be null

primer error


Solution

  • For this particular scenario, it's best you wrap $any(). This is the HTML equivalent of setting as any type, the event (TypeDefinition) does not have value we know it has a value (actual object).

    <input class="campo" type="text" placeholder="Buscar un campeón" aria-label="Buscar un campeón"
      (keyup)="searchChampion.next($any($event.target).value)">
    

    Stackblitz Demo


    An alternative will be to just define a method, which will wrap the target and perform the same action.

    Here I use TypeScript intersection operator & to define the type as EventTarget and also has a value property of any type and assigned it to a type Target. We can use this to get rid of the error:

    import { Component } from '@angular/core';
    import { bootstrapApplication } from '@angular/platform-browser';
    import { Subject } from 'rxjs';
    import 'zone.js';
    
    export type Target = EventTarget & { value: string };
    
    @Component({
      selector: 'app-root',
      standalone: true,
      template: `
        <input class="campo" type="text" placeholder="Buscar un campeón" aria-label="Buscar un campeón"
          (keyup)="searchChampionEvent($event)">
      `,
    })
    export class App {
      name = 'Angular';
      searchChampion = new Subject<string>();
    
      searchChampionEvent(event: Event) {
        const stringValue = (event.target as Target)?.value ?? '';
        this.searchChampion.next(stringValue);
      }
    }
    
    bootstrapApplication(App);
    

    Stackblitz Demo