如果多次使用,具有输入属性的可重用组件将无法在父组件上正确呈现

问题描述 投票:0回答:3

我创建了一个可重用组件(星级评定),我计划在多个地方使用它来对不同的事物进行评分,当我仅使用可重用组件一次时它工作正常,但当我在组件上多次使用它时它不会被渲染。

正如您可能已经猜到的,可重用组件(星级评定组件)具有星星,当

@input
属性具有某些值时,这些星星将突出显示,例如,如果
@input
属性具有 3,则将突出显示 3 颗星,如果为 4,则突出显示 4 颗星。

当我在多次使用可重用组件(星级组件)的地方使用它时,如果仅最后考虑

@input
属性,并且不同组件具有不同的值。

我做了一个迷你演示,并粘贴了代码来理解

评级.component.html

<div class="rating-wrapper">

  <!-- star 5 -->
  <input type="radio" id="5-star-rating" name="star-rating" value="5" [checked]="starrating === '5'" (change)="onRatingChange($event.target)" [(ngModel)]="starrating">
  <label for="5-star-rating" class="star-rating">
    <i class="fa fa-star d-inline-block"></i>
  </label>

  <!-- star 4 -->
  <input type="radio" id="4-star-rating" name="star-rating" value="4" [checked]="starrating === '4'" (change)="onRatingChange($event.target)" [(ngModel)]="starrating" >
  <label for="4-star-rating" class="star-rating star">
    <i class="fa fa-star d-inline-block"></i>
  </label>

  <!-- star 3 -->
  <input type="radio" id="3-star-rating" name="star-rating" value="3" [checked]="starrating === '3'" (change)="onRatingChange($event.target)" [(ngModel)]="starrating">
  <label for="3-star-rating" class="star-rating star">
    <i class="fa fa-star d-inline-block"></i>
  </label>

  <!-- star 2 -->
  <input type="radio" id="2-star-rating" name="star-rating" value="2" [checked]="starrating === '2'" (change)="onRatingChange($event.target)" [(ngModel)]="starrating">
  <label for="2-star-rating" class="star-rating star">
    <i class="fa fa-star d-inline-block"></i>
  </label>

  <!-- star 1 -->
  <input type="radio" id="1-star-rating" name="star-rating" value="1" [checked]="starrating === '1'" (change)="onRatingChange($event.target)" [(ngModel)]="starrating">
  <label for="1-star-rating" class="star-rating star">
    <i class="fa fa-star d-inline-block"></i>
  </label>


</div>

评级.组件.css

.container-wrapper {
    background-color: #EDF0F9;
  }
  
  .container {
    height: 100vh;
  }
  
  .rating-wrapper {
    align-self: center;
    /* box-shadow: 7px 7px 25px rgba(198, 206, 237, 0.7), -7px -7px 35px rgba(255, 255, 255, 0.7), inset 0px 0px 4px rgba(255, 255, 255, 0.9), inset 7px 7px 15px rgba(198, 206, 237, 0.8); */
    border-radius: 4rem;
    display: inline-flex;
    direction: rtl !important;
    padding: 1.5rem 2.5rem;
    margin-left: auto;
  }
  .rating-wrapper label {
    color: #E1E6F6;
    cursor: pointer;
    display: inline-flex;
    font-size: 2.1rem;
    padding: 1rem 0.6rem;
    transition: color 0.5s;
  }
  .rating-wrapper svg {
    -webkit-text-fill-color: transparent;
    -webkit-filter: drop-shadow 4px 1px 6px #c6ceed;
    filter: drop-shadow(5px 1px 3px #c6ceed);
  }
  .rating-wrapper input {
    height: 100%;
    width: 100%;
  }
  .rating-wrapper input {
    display: none;
  }
  .rating-wrapper label:hover,
  .rating-wrapper label:hover ~ label,
  .rating-wrapper input:checked ~ label {
    color: #34AC9E;
  }
  .rating-wrapper label:hover,
  .rating-wrapper label:hover ~ label,
  .rating-wrapper input:checked ~ label {
    color: #34AC9E;
  }

评级.组件.ts

import { Component, Input } from '@angular/core';

@Component({
  selector: 'app-rating',
  templateUrl: './rating.component.html',
  styleUrls: ['./rating.component.css']
})
export class RatingComponent {

  constructor(){}
 @Input()  starrating:string='3';  //<== this will get value and the stars will be highlighted

 onRatingChange(val:any){
  //blah blah blah
 }
 
}

当我在其他组件中使用它时,它不会被渲染

Manytimes.Component.html

<div *ngFor="let item of arrayResponse">

    <img
          src="assets\150.png"
          class="img-fluid rounded-circle mb-3"
          alt="coach image"
        /> <br>

        <div>{{item.name}}</div>
        <app-rating [starrating]="item?.rating"></app-rating>  // here I am passing the value of how many star should get highlighted
</div>

Manytimes.Component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'app-manytimes',
  templateUrl: './manytimes.component.html',
  styleUrls: ['./manytimes.component.css']
})
export class ManytimesComponent {

// just for demo the below response is hard coded, but actually it will come from api response
       arrayResponse:any=[
        {
          "name":"ram",
          "rating":"3"
        },
        {
          "name":"Dinesh",
          "rating":"1"
        },
        {
          "name":"ganesh",
          "rating":"2"
        },
      ]
    }

当在我的应用程序中渲染时,我期望第一个应该突出显示 3 颗星,然后是 1 颗星,最后一个应该突出显示 2 星

但只有最后一个用 2 星突出显示,所有其他都是空白

为什么会发生这种情况?我读了很多其他答案,但无法使其发挥作用。有人可以帮忙吗? 谢谢

angular parent-child angular-components
3个回答
0
投票

主要问题是

@Input() starrating:string='3';
改变了视图,因此指令
[(ngModel)]
获得了最后发出的值,即“2”。

这里,解决方案使用了一个附加标识符,稍后您将在子组件中使用它 - 评级。您可以通过多种方式执行此操作,例如,收集数组中的索引位置并将其传递为

<app-rating [componentId]="i"></app-rating>

<div *ngFor="let item of arrayResponse; let i = index">
    <img src="assets\150.png" class="img-fluid rounded-circle mb-3" alt="coach image"/> <br>
        <div>{{item.name}}</div>
        <app-rating [starrating]="item?.rating" [componentId]="i"></app-rating>
</div>

更新的评级组件:

  ngOnInit() {
    this.selectedRating[this.componentId] = this.starrating;
  }
  @Input() starrating: any = '3';

  @Input() componentId: number = 0;

  selectedRating: { [componentId: string]: string } = {};

  onRatingChange(componentId: number, rating: string) {
    this.selectedRating[componentId] = rating;
  }

在这里您可以注意到我们创建了一个新对象

selectedRating
,它保存临时数据。您实际上可以在父级创建此对象并将其作为附加输入传递给子组件,这样您就不会丢失新输入保存的数据,但是对于此示例,我们可以跳过此操作。

由于我们启动了一个新变量 -

@Input() componentId: number = 0;
selectedRating: { [componentId: string]: string } = {};
,我们需要在
rating.component.html
中使用它们:

<div class="rating-wrapper">
  <ng-container *ngFor="let star of ['5', '4', '3', '2', '1']">
    <input
      type="radio"
      [id]="star + '-star-rating-' + componentId"
      [name]="componentId + '-star-rating'"
      [value]="star"
      [ngModel]="selectedRating[componentId]"
      (change)="onRatingChange(componentId, star)"
    />
    <label [for]="star + '-star-rating-' + componentId" class="star-rating">
      <i class="fa fa-star d-inline-block"></i>
    </label>
  </ng-container>
</div>

我在这里:

  • 简化了一段代码来解决DRY原则
  • 添加了一个新属性
    name
    ,它为每个组件保存一个唯一的值。这是一种重要的方式,因为它确保恒星的一个组成部分的变化不会影响另一个组成部分。
  • 在输入的 id 中分配组件索引
  • 调整后的 ngModel 指令
  • 编写(更改)方法逻辑
  • 跳过了检查的指令

0
投票

当您使用属性并且想要进行两种方式绑定时,您需要一种

@Input
和一种
@Output
,请参阅docs - 请参阅“输出名称是属性+更改”

  @Input()  starrating!: number | string;
  @Output() starratingChange = new EventEmitter<number|string>();

当您使用输入类型的收音机并且想要创建“收音机组”时,输入应该有一个“名称”。但这个属性对于“整个群体”来说应该是唯一的。您的组件始终具有相同的名称:

star-rating

您可以选择两个选项,使用外部变量或强制用户创建属性“名称”,请参阅this SO

let StarratingUniqueId = 0;

@Component({
...
}){
export class RatingComponent {
id:number;
  constructor(){
    this.id=StarratingUniqueId ++;
  }
}

或者

constructor(@Optional() @Attribute('name') public name:string){
    if (!name)
      throw new Error("The name attribute it's neccessary")
}

除了使用checked之外,你还可以使用类似的东西

[class.checked]="starrating === '1'"

并使用

input.checked{...}

0
投票

当您在表单内的多个 input 元素中使用相同的 name 属性值时,可能会导致意外行为,因为浏览器将这些输入视为同一组的一部分。这可能会导致表单提交和数据处理出现问题。当您对具有相同名称的多个输入使用 ngModel 绑定时,所有这些输入可能会绑定到组件中的相同属性。因此,一个输入的更改将立即反映在共享相同名称的所有其他输入中。

您可以在此处为 name 属性指定唯一值。

评级.component.html

<div class="rating-wrapper">
  <!-- star 5 -->
  <input
    type="radio"
    id="5-star-rating"
    name="5-star-rating"
    value="5"
    [checked]="starrating === '5'"
    [(ngModel)]="starrating"
  />
  <label for="5-star-rating" class="star-rating">
    <i class="fa fa-star d-inline-block"></i>
  </label>

  <!-- star 4 -->
  <input
    type="radio"
    id="4-star-rating"
    name="4-star-rating"
    value="4"
    [checked]="starrating === '4'"
    [(ngModel)]="starrating"
  />
  <label for="4-star-rating" class="star-rating star">
    <i class="fa fa-star d-inline-block"></i>
  </label>

  <!-- star 3 -->
  <input
    type="radio"
    id="3-star-rating"
    name="3-star-rating"
    value="3"
    [checked]="starrating === '3'"
    [(ngModel)]="starrating"
  />
  <label for="3-star-rating" class="star-rating star">
    <i class="fa fa-star d-inline-block"></i>
  </label>

  <!-- star 2 -->
  <input
    type="radio"
    id="2-star-rating"
    name="2-star-rating"
    value="2"
    [checked]="starrating === '2'"
    [(ngModel)]="starrating"
  />
  <label for="2-star-rating" class="star-rating star">
    <i class="fa fa-star d-inline-block"></i>
  </label>

  <!-- star 1 -->
  <input
    type="radio"
    id="1-star-rating"
    name="1-star-rating"
    value="1"
    [checked]="starrating === '1'"
    [(ngModel)]="starrating"
  />
  <label for="1-star-rating" class="star-rating star">
    <i class="fa fa-star d-inline-block"></i>
  </label>
</div>

我们还可以使用 Angular 的 ngFor 指令根据值数组动态生成星级输入来简化代码。

<div class="rating-wrapper">
  <ng-container *ngFor="let rating of [5, 4, 3, 2, 1]">
    <input
      type="radio"
      [id]="rating + '-star-rating'"
      [name]="'star-rating-' + rating"
      [value]="rating"
      [checked]="starrating === rating.toString()"
    />
    <label [for]="rating + '-star-rating'" class="star-rating">
      <i class="fa fa-star d-inline-block"></i>
    </label>
  </ng-container>
</div>
© www.soinside.com 2019 - 2024. All rights reserved.