尝试应用 angular.io 指南中的“使用 setter 拦截输入属性更改”。孩子的模板显示,但没有对父母的输入进行任何更改..??它之前工作过:-) :-(
这是子组件:ts 和 html:
.ts 文件
import { Component, OnInit, Input } from '@angular/core';
import { BowlingGame } from '../model/bowling-game.model';
@Component({
selector: 'app-generate-result',
templateUrl: './generate-result.component.html',
styleUrls: ['./generate-result.component.css']
})
export class GenerateResultComponent {
@Input()
get game(): BowlingGame { return this._game; }
set game(game: BowlingGame) {
this._game.rolls[0] = game.rolls[0] + 15;
this._game = game;
}
private _game = { rolls: <number[]>[], player: "", isActive: true };
}
html:
<h2>generate-result.component.html {{game | json}}</h2>
还有父组件,html文件: 我包括所有内容,因为我不知道这位父母有很多孩子是否是一种不好的做法..? app-generate-result 子项位于代码的第一行..
<app-generate-result *ngIf="bowlingGamePlayer1" [game]="bowlingGamePlayer1"></app-generate-result>
<h1>Insert your Roll score (dashboard for 2 players)</h1>
<div class="player1" *ngIf="bowlingGamePlayer1.isActive" >
<h2>Input Player 1:</h2>
<app-create-roll-item (onRollCreated)="insertRollPlayer1($event)"></app-create-roll-item>
</div>
<div *ngIf="bowlingGamePlayer2.isActive" class="player2">
<h2>Input Player 2: </h2>
<app-create-roll-item (onRollCreated)="insertRollPlayer2($event)"></app-create-roll-item>
</div>
<h2>bowlingGame1 | json (from parent:app.comp): {{bowlingGamePlayer1 | json}}</h2>
<!-- results are displayed in parent: app.component.html -->
<table>
<tr>
<td></td>
<td colspan="3">frame1</td>
<td colspan="3">frame2</td>
<td colspan ="3">frame3</td>
<td colspan ="3">frame4</td>
<td colspan ="3">frame5</td>
</tr>
<tr class="player1">
<td>Player:1</td>
<td *ngFor="let score of bowlingGamePlayer1.rolls">{{score}}</td>
</tr>
<tr class="player2">
<td>Player:2</td>
<td *ngFor="let score of bowlingGamePlayer2.rolls">{{score}}</td>
</tr>
</table>
<h1>rollsPlayer1</h1>
<h2>{{rollsPlayer1 | json}}</h2>
<!--
<h1>rollsPerFramePlayer1</h1>
<h2>{{rollsPerFramePlayer1 | json}}</h2>
-->
<h3>indexRollFrame</h3>
<h3>{{indexRollFrame}}</h3><h3>rollsPerFrame: {{rollsPerFrame | json }}</h3>
<h1>totalPins</h1>
<h2>{{totalPins}}</h2>
<h1>rolls player 2 length: {{rollsPlayer2.length}} /// max nr rolls {{maxNumberRollsPlayer2}}</h1>
<h1>rolls player 1 length: {{rollsPlayer1.length}} /// max nr rolls {{maxNumberRollsPlayer1}}</h1>
<h1>index frame player 1/ player2</h1>
<h2>{{indexFramePlayer1 }}/ {{indexFramePlayer2}}</h2> -->
父级的 ts(出于完整性原因):
import { Component, OnInit, Output } from '@angular/core';
import { BowlingGame } from './model/bowling-game.model';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
bowlingGamePlayer1
bowlingGamePlayer2
rollsPlayer1
rollsPlayer2
rollsPerFrame
// total pinns trown !!
totalPins = 0;
// roll index in frame (range: 0 -1- 2)
indexRollFrame = 0;
// index frame: 0-4 (5 frames) , frame 5 is special: strike/spare in 5th frame
indexFramePlayer1 = 0;
indexFramePlayer2 = 0;
rollZero = 0;
maxNumberRollsPlayer1 = 15;
maxNumberRollsPlayer2 = 15;
// leave constructor empty of the real work
constructor() {
this.bowlingGamePlayer1 = new BowlingGame([], "player1", true);
this.bowlingGamePlayer2 = new BowlingGame([], "player2", false);
this.rollsPlayer1 = this.bowlingGamePlayer1.rolls;
this.rollsPlayer2 = this.bowlingGamePlayer2.rolls;
this.totalPins = 0;
this.rollsPerFrame = <number[]>[];
}
ngOnInit(): void {
}
passToNextPlayer() {
this.bowlingGamePlayer1.isActive = !this.bowlingGamePlayer1.isActive;
this.bowlingGamePlayer2.isActive = !this.bowlingGamePlayer2.isActive;
this.indexRollFrame = 0;
this.totalPins = 0;
this.rollsPerFrame = [];
}
// try to make a function per frame: check! // spare = 15 pins after 2 OR 3 rolls!
insertRollPlayer1(r:number):void {
// after execution: length == maxNumberRolls :-)
if (this.rollsPlayer1.length < (this.maxNumberRollsPlayer1)) {
this.rollsPlayer1.push(r)
// put the scores per frame in a rollsPerFrame variable
this.rollsPerFrame[this.indexRollFrame] = r; // array [0,0,0], starts with indexRollFrame 0
this.totalPins += this.rollsPerFrame[this.indexRollFrame]; // number, starts with 0
// // starting from frame 5 (index is 4 beforehand) apply other logic !!! frame 5 is longer than the other frames!
if ((this.indexFramePlayer1 == 4) && (this.totalPins < 15) && (this.indexRollFrame == 2)) {
this.passToNextPlayer();
} else if ((this.indexFramePlayer1 == 4) && (this.totalPins < 15) && (this.indexRollFrame < 2)) {
this.indexRollFrame += 1;
}
// set max number of rolls, based on 5th frame. Frame 5 is not limited to three rolls!
if(this.indexFramePlayer1 == 4 && this.totalPins == 15) {
if(this.indexRollFrame==0) {
this.maxNumberRollsPlayer1 = 16;
this.indexRollFrame += 1;
// case spare in 2 rolls
} else if (this.indexRollFrame == 1) {
this.maxNumberRollsPlayer1 = 16;
this.indexRollFrame += 1;
// spare in 3 rolls // edge case! this could be the last roll!
} else if (this.indexRollFrame == 2) {
this.maxNumberRollsPlayer1 = 17;
this.indexRollFrame += 1;
}
}
// set the logic for the end of the game, in case of spare or strike in last frame
if(this.indexFramePlayer1 == 4 && this.totalPins > 15) {
if(this.rollsPlayer1.length == this.maxNumberRollsPlayer1) {
this.passToNextPlayer();
}
}
if (this.indexFramePlayer1 < 4) {
if (this.totalPins == 15) {
// if strike
if (this.indexRollFrame == 0) {
// this.rollsPlayer1.push(this.rollZero);
// this.rollsPlayer1.push(this.rollZero);
this.indexFramePlayer1 += 1;
this.passToNextPlayer();
// case spare after 2 rolls
} else if (this.indexRollFrame == 1) {
// this.rollsPlayer1.push(this.rollZero);
this.indexFramePlayer1 += 1;
this.passToNextPlayer();
// case 15 pins after 3 rolls
} else {
this.indexFramePlayer1 += 1;
this.passToNextPlayer()
}
// case not 15 pins in total after 3 rolls
} else if (this.indexRollFrame == 2) {
this.indexFramePlayer1 += 1;
this.passToNextPlayer()
// case if frame is not over yet
} else {
this.indexRollFrame += 1;
}
}
}
}
// two functions for player 1/ player2 because you don't get the info from the form which player is at turn
insertRollPlayer2(r:number):void {
if (this.rollsPlayer2.length < (this.maxNumberRollsPlayer2)) {
this.rollsPlayer2.push(r)
this.rollsPerFrame[this.indexRollFrame] = r; // array [0,0,0], starts with indexRollFrame 0
this.totalPins += this.rollsPerFrame[this.indexRollFrame]; // number, starts with 0
// start with cases in frame 5
if (this.indexFramePlayer2 == 4 && this.totalPins < 15 && this.indexRollFrame == 2) {
this.bowlingGamePlayer2.isActive = false;
}
if (this.indexFramePlayer2 == 4 && this.totalPins < 15 && this.indexRollFrame < 2) {
this.indexRollFrame += 1;
}
// set max number of rolls, based on 5th frame. Frame 5 is not limited to three rolls!
if (this.indexFramePlayer2 == 4 && this.totalPins == 15) {
if (this.indexRollFrame == 0) {
this.maxNumberRollsPlayer2 = 16;
this.indexRollFrame += 1;
// case spare in 2 rolls
} else if (this.indexRollFrame == 1) {
this.maxNumberRollsPlayer2 = 16;
this.indexRollFrame += 1;
// spare in 3 rolls // edge case! this could be the last roll!
} else if (this.indexRollFrame == 2) {
this.maxNumberRollsPlayer2 = 17;
this.indexRollFrame += 1;
}
}
// set the logic for the end of the game, in case of spare or strike in last frame
if (this.indexFramePlayer2 == 4 && this.totalPins > 15) {
if (this.rollsPlayer2.length == this.maxNumberRollsPlayer2) {
this.bowlingGamePlayer2.isActive = false;
}
}
if (this.indexFramePlayer2 < 4) {
if (this.totalPins == 15) {
// if strike
if (this.indexRollFrame == 0) {
// this.rollsPlayer2.push(this.rollZero);
// this.rollsPlayer2.push(this.rollZero);
this.indexFramePlayer2 += 1;
this.passToNextPlayer();
// case spare
} else if (this.indexRollFrame == 1) {
// this.rollsPlayer2.push(this.rollZero);
this.indexFramePlayer2 += 1;
this.passToNextPlayer();
// case 15 pins after 3 rolls
} else {
this.indexFramePlayer2 += 1;
this.passToNextPlayer()
}
// case not 15 pins in total
} else if (this.indexRollFrame == 2) {
this.indexFramePlayer2 += 1;
this.passToNextPlayer()
} else {
this.indexRollFrame += 1;
}
}
}
}
}
基本的获取/设置设置如下:
用于存储值的后备属性、setter 方法(通常作为
@Input()
的一部分)和 getter 方法。
private _someValue: string;
@Input() public set someValue(value: string) {
this._someValue = value;
// this.nowDoSomethingElse();
}
public get someValue(): string { return this._value; }
每次该组件的输入值发生变化时,它都会在该 set 方法中触发,这样您就可以做任何您需要做的事情。
getter 确实是可选的,但是拥有 getter 意味着模板可以随时访问要使用的值(模板只能访问公共成员,不能访问私有成员)。
需要警惕的一件事是变化检测。如果它是字符串或其他值/基元类型,则对其进行的更改将被识别为新值并进入您所使用的 set 方法。
如果您有一个对象/引用类型,那么对该对象中的 properties 的更改不会导致您进入快乐的 setter 领域。为此,您需要设置自己的更改检测或实际为其提供新的对象引用。
快速简单的对象引用更改:
this.myThing = {...this.myThing};
一旦你的对象引用发生了这样的变化,你就进入了 setter 方法的世界。
现在有空了,更有针对性的回答。
您可以通过一种浪费的方式来做到这一点,不断地重新创建游戏对象,或者您可以只将最少量的数据传递到结果组件中。
它真的需要了解整个游戏,还是只了解一些关键值?我怀疑是后者。在这种情况下,您只需要最少的参与,并且您可以在父母中更严格地控制它。
分数组成部分:
@Input() public data: ScoringDataDto;
父组件:
private _scoreDetails: ScoringDataDto;
public get scoreDetails(): ScoringDataDto { return this._scoreDetails; }
private updateGame(...): void {
// update the game or something
// ...
this.updateScoring(...);
}
private updateScoring(...): void {
this._scoreDetails = {rolls: this._game.rolls, active: true, whatever: 'else'};
}
每当更新时创建一个新的评分对象,更改将自动注册到评分组件中。
不要害怕复制出您需要的数据位。它并不像看起来那么混乱,并且很快就成为第二天性。它还变得更易于阅读和管理,并通过将所有内容的大小减少到“需要了解的基础”来帮助您更好地构建解决方案。
Angular 毕竟是一个基于组件的系统,而不是一个整体。 父模板:
<app-score-display [data]="scoreDetails"></app-score-display>