Skip to content

Commit d6b4b9d

Browse files
authored
Merge pull request #1 from pietro909/one-reducer
One reducer
2 parents 66932b8 + c7017ec commit d6b4b9d

13 files changed

+267
-12
lines changed

.gitignore

100644100755
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ app/**/*.js
44
*.map
55
*.log
66
typings/
7+
.idea

.travis.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
language: node_js
2+
node_js:
3+
- "4.2"
4+
before_script:
5+
- npm install
6+
script: npm run tsc

README.md

100644100755
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
[![Build Status](https://travis-ci.org/pietro909/one-source-of-truth-for-angular.svg?branch=youtube)](https://travis-ci.org/pietro909/one-source-of-truth-for-angular)
12
# One Source of Truth for Angular 2
23

34
Brief description of the article and what it covers.

app/app.component.ts

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,45 @@
1-
import { Component } from '@angular/core';
1+
import {Component, OnInit} from '@angular/core';
2+
import {Store, provideStore} from "@ngrx/store";
3+
import {Observable} from "rxjs/Rx";
4+
import {CurrentSearch} from "./models/current-search.model";
5+
import {ProximitySelector} from "./components/proximity-selector.component";
6+
import {SearchBox} from "./components/search-box.component";
7+
import {SearchReducer} from "./reducers/search.reducer";
28

3-
@Component({
9+
const storeManager = provideStore({ currentSearch: SearchReducer });
410

11+
@Component({
512
selector: 'my-app',
13+
providers: [ storeManager ],
14+
directives: [ SearchBox, ProximitySelector ],
15+
template: `
16+
<h1>{{title}}</h1>
17+
<div class="row">
18+
<search-box [store]="store"></search-box>
19+
<proximity-selector [store]="store"></proximity-selector>
20+
</div>
21+
<p>{{ state | json }}</p>
22+
`
23+
})
624

7-
template: `<h1>{{title}}</h1>`,
25+
export class AppComponent implements OnInit {
826

9-
})
27+
title = 'heroes and books'; //One Source of Truth for Angular 2';
28+
29+
private state: CurrentSearch;
30+
31+
private currentSearch: Observable<CurrentSearch>;
1032

11-
export class AppComponent {
33+
constructor(
34+
private store: Store<CurrentSearch>
35+
) {
36+
this.currentSearch = this.store.select<CurrentSearch>('currentSearch');
37+
}
1238

13-
title = 'One Source of Truth for Angular 2';
39+
ngOnInit() {
40+
this.currentSearch.subscribe((state: CurrentSearch) => {
41+
this.state = state;
42+
});
43+
}
1444

1545
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import {Component, Input} from "@angular/core";
2+
import {Store} from "@ngrx/store";
3+
import {CurrentSearch} from "../models/current-search.model";
4+
5+
@Component({
6+
selector: 'proximity-selector',
7+
inputs: ['store'],
8+
template: `
9+
<div class="input-group">
10+
<label for="useLocation">Use current location</label>
11+
<input type="checkbox"
12+
[disabled]="disabled"
13+
(change)="onLocation($event)">
14+
</div>
15+
<div class="input-group">
16+
<label for="locationRadius">Radius</label>
17+
<input type="range" min="1" max="100" value="50"
18+
[disabled]="!active"
19+
(change)="onRadius($event)">
20+
</div>
21+
`
22+
})
23+
24+
export class ProximitySelector {
25+
26+
static StoreEvents = {
27+
position: 'ProximitySelector:POSITION',
28+
radius: 'ProximitySelector:RADIUS',
29+
off: 'ProximitySelector:OFF'
30+
};
31+
32+
@Input()
33+
store: Store<CurrentSearch>;
34+
35+
active = false;
36+
37+
onLocation($event: any) {
38+
this.active = $event.target.checked;
39+
if (this.active) {
40+
navigator.geolocation.getCurrentPosition((position: any) => {
41+
this.store.dispatch({
42+
type: ProximitySelector.StoreEvents.position,
43+
payload: {
44+
position: {
45+
latitude: position.coords.latitude,
46+
longitude: position.coords.longitude
47+
}
48+
}
49+
});
50+
});
51+
} else {
52+
this.store.dispatch({
53+
type: ProximitySelector.StoreEvents.off,
54+
payload: {}
55+
});
56+
}
57+
}
58+
59+
onRadius($event: any) {
60+
const radius = parseInt($event.target.value, 10);
61+
this.store.dispatch({
62+
type: ProximitySelector.StoreEvents.radius,
63+
payload: {
64+
radius: radius
65+
}
66+
});
67+
}
68+
69+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import {Observable} from "rxjs/Rx";
2+
import {ElementRef, OnInit, Component, Input} from "@angular/core";
3+
import {Store} from "@ngrx/store";
4+
5+
@Component({
6+
inputs: ['store'],
7+
selector: 'search-box',
8+
template: `
9+
<input type="text" class="form-control" placeholder="Search" autofocus>
10+
`
11+
})
12+
13+
export class SearchBox implements OnInit {
14+
15+
static StoreEvents = {
16+
text: 'SearchBox:TEXT_CHANGED'
17+
};
18+
19+
@Input()
20+
store: Store<any>;
21+
22+
constructor(private el: ElementRef) {}
23+
24+
ngOnInit(): void {
25+
Observable.fromEvent(this.el.nativeElement, 'keyup')
26+
.map((e: any) => e.target.value)
27+
.debounceTime(500)
28+
.subscribe((text: string) =>
29+
this.store.dispatch({
30+
type: SearchBox.StoreEvents.text,
31+
payload: {
32+
text: text
33+
}
34+
})
35+
);
36+
}
37+
38+
}

app/models/current-search.model.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
2+
export interface CurrentSearch {
3+
text: string,
4+
position: {
5+
latitude: number,
6+
longitude: number
7+
},
8+
radius: number
9+
}

app/models/search-query.model.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export interface SearchQuery {
2+
name: string;
3+
location?: {
4+
latitude: number,
5+
longitude: number,
6+
radius: number
7+
}
8+
}

app/models/search-result.model.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export interface SearchResult {
2+
id: string;
3+
title: string;
4+
thumbnailUrl: string;
5+
}

app/reducers/search.reducer.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { ActionReducer, Action } from '@ngrx/store';
2+
import {CurrentSearch} from "../models/current-search.model";
3+
import {SearchBox} from "../components/search-box.component";
4+
import {ProximitySelector} from "../components/proximity-selector.component";
5+
6+
export const SearchReducer: ActionReducer<CurrentSearch> = (state: CurrentSearch, action: Action) => {
7+
switch (action.type) {
8+
case SearchBox.StoreEvents.text:
9+
return Object.assign({}, state, {
10+
text: action.payload.text
11+
});
12+
case ProximitySelector.StoreEvents.position:
13+
return Object.assign({}, state, {
14+
position: {
15+
latitude: action.payload.position.latitude,
16+
longitude: action.payload.position.longitude
17+
}
18+
});
19+
case ProximitySelector.StoreEvents.radius:
20+
return Object.assign({}, state, {
21+
radius: action.payload.radius
22+
});
23+
case ProximitySelector.StoreEvents.off:
24+
return Object.assign({}, state, {
25+
position: null,
26+
radius: null
27+
});
28+
default:
29+
return state;
30+
}
31+
};

0 commit comments

Comments
 (0)