Resource API 包括 resource 和 rxResource 函数,其预设值未定义。当开发人员仅提供一个 loader 时,函数的传回类型为 T | undefined。在 Angular 19.2.0 中,Resource API 将新增一个覆盖 undefined 的 defaultValue 选项,且函数传回类型 T。
在这篇文章中,我将向您展示如何使用 defaultValue 选项在 rxResource 函数中传回一个虚拟 Person 物件。
向 Resource API 新增 defaultValue 选项
使用 rxResource 检索星际大战详细信息
export function getPerson() {
assertInInjectionContext(getPerson);
const http = inject(HttpClient);
return (id: number) => {
return http.get<Person>(`https://swapi.dev/api/people/${id}`).pipe(
delay(500),
map((p) => ({...p, id } as Person)),
catchError((err) => {
console.error(err);
return throwError(() => err);
}));
}
}
getPerson 函数呼叫 endpoint 来检索星际大战角色。如果 HTTP request 抛出错误,函数会捕获该错误,记录错误讯息,然后重新抛出它。 HTTP request 增加 500 毫秒的延迟来模拟长时间资源载入。
const DEFAULT: Person = {
id: -1,
name: \'NA\',
height: \'NA\',
mass: \'NA\',
hair_color: \'NA\',
skin_color: \'NA\',
eye_color: \'NA\',
gender: \'NA\',
films: [],
}
export function getStarWarsCharacter(id: Signal<number>, injector: Injector) {
return runInInjectionContext(injector, () => {
const getPersonFn = getPerson();
const starWarsResource = rxResource({
request: id,
loader: ({ request: searchId }) => getPersonFn(searchId),
defaultValue: DEFAULT
}).asReadonly();
return computed(() => ({
value: starWarsResource.value(),
status: starWarsResource.status(),
}));
});
}
getStarWarCharacter 函数建立一个透过 ID 查询星际大战角色的 resource。此资源有一个新的defaultValue 属性,当资源未定义、正在载入或出现错误时,该属性会传回虚拟的 Person 物件。此函数传回由资源状态和资源值组成的计算讯号。
重构 CharacterComponent
export class CharacterComponent {
searchId2 = signal(initialId);
injector = inject(Injector);
person = getStarWarsCharacter(this.searchId2, this.injector);
}
使用 rxResource,我成功地从 CharacterComponent 元件中的后端伺服器查询了星际大战角色。
<h3>Display one of the 83 Star War Characters</h3>
<div class="border">
<p>Resource Status: {{ person().status }}</p>
<app-character-info [info]="person().value" />
</div>
<app-character-picker (newSearchId)="searchId2.set($event)" />
CharacterComponent 元件显示资源状态,并将星际大战角色传递到 CharacterInfoComponent 元件。 CharacterPickerComponent 元件会改变 ID 并将其传送给父元件。。
重构 CharacterInfoComponent
@Component({
selector: \'app-character-info\',
standalone: true,
template: `
@let person = info();
<p>Id: {{ person.id }} </p>
<p>Name: {{ person.name }}</p>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CharacterInfoComponent {
info = input.required<Person>();
}
CharacterInfoComponent 是一个接受星际大战角色的 ID 和详细资讯的元件。 我将模板逻辑移到一个新元件,使得 ChartacterComponent 看起来更简洁。
rxResource 函数使用 defaultValue 选项传回一个虚拟的 Person 物件;因此,info signal input 永远不会未定义。 signal input 的类型是 Person,范本不需要检查 info 是否未定义。
重构 CharactePickerComponent
<div class="container">
<button (click)="delta.set({ value: -1 })">-1</button>
<button (click)="delta.set({ value: 1 })">+1</button>
</div>
export class CharacterPickerComponent {
newSearchId = output<number>();
delta = signal<{ value: number }>({ value: 0 });
characterId = linkedSignal<{ value: number }, number>({
source: this.delta,
computation: ({ value }, previous) => {
const previousValue = !previous ? initialId : previous.value;
return previousValue + value;
}
});
}
当 delta 讯号接收到新值时,characterId 讯号会增加或减少目前 ID 以得出新的 ID。
在建构函式中,effect 的回呼函数将 characterId LinkedSignal 的值发射给父元件。 Angular 团队表示 LinkedSignal 的 computation 函数应该是 pure 的;因此,它并不是一个释放价值的好地方。
constructor() {
effect(() => this.newSearchId.emit(this.characterId()));
}
当使用者点击按钮增加或减少ID时,rxResource函数从后端伺服器载入资料时会显示预设值。 使用者介面显示资源状态2,枚举值为「Loading」。资料取得完成后进行显示,且资源状态变为4,即「Resolved」的枚举值。当使用者将ID改为17时,HTTP请求会抛出例外。资源状态变成1,「Error」枚举值,显示预设值。
defaultValue 选项允许开发人员为 Resource API 提供 undefined 以外的预设值。当资源必须传回 T 而不是 T | undefined 时,这很有帮助的。当物件可能为 null 时,不需要额外的程式码来解构属性并指派预设值。
参考:
- PR: https://github.com/angular/angular/pull/59655
- Resource API: https://angular.dev/guide/signals/resource
- Stackblitz Demo: https://stackblitz.com/edit/stackblitz-starters-kvhzplsg?file=src%2Fstar-war%2Fresources%2Fstar-war.resource.ts