Angular 组件您可以将原始版本中的 React 组件替换为它们的 Angular 等效组件 — 并迅速得到一个 Angular 图书搜索应用程序。Angular 版本中的 reducer、操作、存储和状态历史都与 React 版本相同。
与 React 一样,借助 Angular,您可以实现彼此嵌套的组件。清单 6 给出了 App 组件,它包含应用程序中的其他所有组件。
清单 6. App 组件 (app/components/app.component.ts)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
| import { Component } from '@angular/core';
import Books from './books.component';
import Controls from './controls.component';
import StateViewer from './stateviewer.component';
import store from '../../store';
import { fetchBooks, setTopic } from '../../actions';
@Component({
selector: 'app',
template: `
<div style='text-align: center; font-size: 1.5em'>
{{app.title}}
</div>
<hr/>
<controls></controls>
<books></books>
<state-viewer></state-viewer>`
})
export default class {
ngOnInit() {
store.dispatch(setTopic('Border collies'));
store.dispatch(fetchBooks());
}
app = {
title: 'Book Search (Angular version)',
};
}
|
App 组件模板包含 3 个组件:controls、books 和 state-viewer。该组件的 TypeScript 在初始化 App 组件时向 Redux 存储分派操作。
controls 组件如清单 7 所示。
清单 7. controls 组件 (app/components/controls.component.ts)1
2
3
4
5
6
7
8
9
10
11
12
13
| import { Component } from '@angular/core';
@Component({
selector: 'controls',
template: `
<topic-selector></topic-selector>
<display-mode-container></display-mode-container>
<history></history>
`
})
export default class Controls {
}
|
controls 组件不执行任何操作,仅包含其他组件,所以它的关联类的实现是空的(但仍然必不可少)。
topic-selector 组件如清单 8 所示。
清单 8. TopicSelector 组件 (app/components/topicselector.component.ts)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
| import { Component } from '@angular/core';
import { fetchBooks, setTopic } from '../../actions';
import store from '../../store';
@Component({
selector: 'topic-selector',
template: `
<label for='topic'>Topic</label>
<input #topicInput
[(ngModel)]='topic'
(input)='setTopic(topicInput.value)'
(keyup.enter)='fetchTopic(topicInput.value)'
autofocus/>
`
})
export default class TopicSelector {
private topic: string;
private unsubscribe: any;
constructor() {
store.subscribe(() => {
this.topic = store.getState().topic;
});
}
setTopic(newTopic) {
store.dispatch(setTopic(newTopic));
}
fetchTopic(newTopic) {
store.dispatch(fetchBooks());
}
ngOnDestroy() {
this.unsubscribe();
}
}
|
主题选择器包含一个标签和一个输入用于指定新主题。输入的 keyup.enter 事件对应于该组件的 fetchTopic() 方法,input 事件对应于该组件的 setTopic() 方法。因此,每次击键都会更新该主题,而且在用户按 Enter 键时会发起一次新的抓取。
[(ngModel)] 结构是 Angular 的特殊的双向绑定语法。在这个示例中,Angular 将文本字段中的值传递给组件的 topic 属性,它还将对属性的更改传递回文本字段。
Redux 的 Angular 2 绑定回想一下,我最开始是通过向 Redux 存储订阅 React 组件来实现应用程序的 React 版本的。随后,我使用了 react-redux 绑定来将组件拆分为容器和包含的组件。也可以通过 ,对 Redux 和 Angular 采用同样的方法,ng2-redux 的使用不属于本文的讨论范围。
从 Redux 角度讲, 的最有趣之处在于,事实上该组件将会订阅 Redux 存储。当存储更改时,该组件会更新它的 topic 属性。您会看到,图书搜索应用程序中的所有 Angular 组件均以一种类似方式订阅 Redux 存储。
显示模式组件如清单 9 所示。
清单 9. DisplayMode 组件 (app/components/displayMode.component.ts)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
| import { Component } from '@angular/core';
import store from '../../store';
import { setDisplayMode } from '../../actions';
@Component({
selector: 'display-mode',
template: `
<span>
<label for='thumbnailRadio'>Thumbnail</label>
<input id="thumbnailRadio" style="cursor: pointer"
type="radio"
name="display_mode"
value="Thumbnail"
[checked]='displayMode === "THUMBNAIL"'
(change)="setMode('THUMBNAIL')"/>
<label for='listRadio'>List</label>
<input id="listRadio" style="cursor: pointer"
type="radio"
name="display_mode"
value="List"
[checked]='displayMode === "LIST"'
(change)="setMode('LIST')"/>
</span>
`
})
export default class DisplayMode {
private displayMode: string;
private unsubscribe: any;
constructor() {
this.unsubscribe = store.subscribe(() => {
this.displayMode = store.getState().displayMode;
});
}
setMode(value) {
store.dispatch(setDisplayMode(value));
}
ngOnDestroy() {
this.unsubscribe();
}
}
|
显示模式组件类似于状态查看器。两个组件都将输入事件映射到组件方法。两个组件都订阅 Redux 存储,以便在应用程序状态发生更改时更新一个属性。区别在于显示模式组件有两个输入,它们都是单选按钮。这些单选按钮链接到应用程序的状态中存储的显示模式值。 |