Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' into master
Browse files Browse the repository at this point in the history
phillipzada authored Oct 23, 2017

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
2 parents dae762f + 4db7d61 commit 3e5ff01
Showing 17 changed files with 71 additions and 63 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@ Reactive libraries for Angular
- [@ngrx/router-store](./docs/router-store/README.md) - Bindings to connect the Angular Router to @ngrx/store
- [@ngrx/store-devtools](./docs/store-devtools/README.md) - Store instrumentation that enables a
[powerful time-travelling debugger](https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd?hl=en).
- [@ngrx/entity](./docs/entity/README.md) - Entity State adapter for managing record collections.

## Examples
- [example-app](./example-app/README.md) - Example application utilizing @ngrx libraries, showcasing common patterns and best practices.
4 changes: 2 additions & 2 deletions example-app/app/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HttpClientModule } from '@angular/common/http';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { RouterModule } from '@angular/router';
import { HttpModule } from '@angular/http';

import { StoreModule } from '@ngrx/store';
import { EffectsModule } from '@ngrx/effects';
@@ -30,7 +30,7 @@ import { environment } from '../environments/environment';
CommonModule,
BrowserModule,
BrowserAnimationsModule,
HttpModule,
HttpClientModule,
RouterModule.forRoot(routes, { useHash: true }),

/**
9 changes: 8 additions & 1 deletion example-app/app/books/actions/book.ts
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@ import { Book } from '../models/book';

export const SEARCH = '[Book] Search';
export const SEARCH_COMPLETE = '[Book] Search Complete';
export const SEARCH_ERROR = '[Book] Search Error';
export const LOAD = '[Book] Load';
export const SELECT = '[Book] Select';

@@ -25,6 +26,12 @@ export class SearchComplete implements Action {
constructor(public payload: Book[]) {}
}

export class SearchError implements Action {
readonly type = SEARCH_ERROR;

constructor(public payload: string) {}
}

export class Load implements Action {
readonly type = LOAD;

@@ -41,4 +48,4 @@ export class Select implements Action {
* Export a type alias of all actions in this action group
* so that reducers can easily compose action types
*/
export type Actions = Search | SearchComplete | Load | Select;
export type Actions = Search | SearchComplete | SearchError | Load | Select;
10 changes: 9 additions & 1 deletion example-app/app/books/components/book-search.ts
Original file line number Diff line number Diff line change
@@ -14,16 +14,23 @@ import { Component, Output, Input, EventEmitter } from '@angular/core';
</md-input-container>
<md-spinner [class.show]="searching"></md-spinner>
</md-card-content>
<md-card-footer><span *ngIf="error">{{error}}</span></md-card-footer>
</md-card>
`,
styles: [
`
md-card-title,
md-card-content {
md-card-content,
md-card-footer {
display: flex;
justify-content: center;
}
md-card-footer {
color: #FF0000;
padding: 5px 0;
}
input {
width: 300px;
}
@@ -50,5 +57,6 @@ import { Component, Output, Input, EventEmitter } from '@angular/core';
export class BookSearchComponent {
@Input() query = '';
@Input() searching = false;
@Input() error = '';
@Output() search = new EventEmitter<string>();
}
4 changes: 3 additions & 1 deletion example-app/app/books/containers/find-book-page.ts
Original file line number Diff line number Diff line change
@@ -11,19 +11,21 @@ import { Book } from '../models/book';
selector: 'bc-find-book-page',
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<bc-book-search [query]="searchQuery$ | async" [searching]="loading$ | async" (search)="search($event)"></bc-book-search>
<bc-book-search [query]="searchQuery$ | async" [searching]="loading$ | async" [error]="error$ | async" (search)="search($event)"></bc-book-search>
<bc-book-preview-list [books]="books$ | async"></bc-book-preview-list>
`,
})
export class FindBookPageComponent {
searchQuery$: Observable<string>;
books$: Observable<Book[]>;
loading$: Observable<boolean>;
error$: Observable<string>;

constructor(private store: Store<fromBooks.State>) {
this.searchQuery$ = store.select(fromBooks.getSearchQuery).take(1);
this.books$ = store.select(fromBooks.getSearchResults);
this.loading$ = store.select(fromBooks.getSearchLoading);
this.error$ = store.select(fromBooks.getSearchError);
}

search(query: string) {
8 changes: 4 additions & 4 deletions example-app/app/books/effects/book.spec.ts
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@ import { empty } from 'rxjs/observable/empty';
import { BookEffects, SEARCH_SCHEDULER, SEARCH_DEBOUNCE } from './book';
import { GoogleBooksService } from '../../core/services/google-books';
import { Observable } from 'rxjs/Observable';
import { Search, SearchComplete } from '../actions/book';
import { Search, SearchComplete, SearchError } from '../actions/book';
import { Book } from '../models/book';

export class TestActions extends Actions {
@@ -62,10 +62,10 @@ describe('BookEffects', () => {
expect(effects.search$).toBeObservable(expected);
});

it('should return a new book.SearchComplete, with an empty array, if the books service throws', () => {
it('should return a new book.SearchError if the books service throws', () => {
const action = new Search('query');
const completion = new SearchComplete([]);
const error = 'Error!';
const completion = new SearchError('Unexpected Error. Try again later.');
const error = 'Unexpected Error. Try again later.';

actions$.stream = hot('-a---', { a: action });
const response = cold('-#|', {}, error);
2 changes: 1 addition & 1 deletion example-app/app/books/effects/book.ts
Original file line number Diff line number Diff line change
@@ -51,7 +51,7 @@ export class BookEffects {
.searchBooks(query)
.takeUntil(nextSearch$)
.map((books: Book[]) => new book.SearchComplete(books))
.catch(() => of(new book.SearchComplete([])));
.catch(err => of(new book.SearchError(err)));
});

constructor(
4 changes: 4 additions & 0 deletions example-app/app/books/reducers/index.ts
Original file line number Diff line number Diff line change
@@ -105,6 +105,10 @@ export const getSearchLoading = createSelector(
getSearchState,
fromSearch.getLoading
);
export const getSearchError = createSelector(
getSearchState,
fromSearch.getError
);

/**
* Some selector functions create joins across parts of state. This selector
17 changes: 16 additions & 1 deletion example-app/app/books/reducers/search.ts
Original file line number Diff line number Diff line change
@@ -3,12 +3,14 @@ import * as book from '../actions/book';
export interface State {
ids: string[];
loading: boolean;
error: string;
query: string;
}

const initialState: State = {
ids: [],
loading: false,
error: '',
query: '',
};

@@ -21,25 +23,36 @@ export function reducer(state = initialState, action: book.Actions): State {
return {
ids: [],
loading: false,
error: '',
query,
};
}

return {
...state,
query,
loading: true,
error: '',
query,
};
}

case book.SEARCH_COMPLETE: {
return {
ids: action.payload.map(book => book.id),
loading: false,
error: '',
query: state.query,
};
}

case book.SEARCH_ERROR: {
return {
...state,
loading: false,
error: action.payload,
};
}

default: {
return state;
}
@@ -51,3 +64,5 @@ export const getIds = (state: State) => state.ids;
export const getQuery = (state: State) => state.query;

export const getLoading = (state: State) => state.loading;

export const getError = (state: State) => state.error;
20 changes: 6 additions & 14 deletions example-app/app/core/services/google-books.spec.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
import { TestBed } from '@angular/core/testing';
import { Http } from '@angular/http';
import { HttpClient } from '@angular/common/http';
import { cold } from 'jasmine-marbles';
import { GoogleBooksService } from './google-books';

describe('Service: GoogleBooks', () => {
let service: GoogleBooksService;
let http: any;
let http: HttpClient;

beforeEach(() => {
TestBed.configureTestingModule({
providers: [
{ provide: Http, useValue: { get: jest.fn() } },
{ provide: HttpClient, useValue: { get: jest.fn() } },
GoogleBooksService,
],
});

service = TestBed.get(GoogleBooksService);
http = TestBed.get(Http);
http = TestBed.get(HttpClient);
});

const data = {
@@ -35,11 +35,7 @@ describe('Service: GoogleBooks', () => {
const queryTitle = 'Book Title';

it('should call the search api and return the search results', () => {
const httpResponse = {
json: () => books,
};

const response = cold('-a|', { a: httpResponse });
const response = cold('-a|', { a: books });
const expected = cold('-b|', { b: books.items });
http.get = jest.fn(() => response);

@@ -50,11 +46,7 @@ describe('Service: GoogleBooks', () => {
});

it('should retrieve the book from the volumeId', () => {
const httpResponse = {
json: () => data,
};

const response = cold('-a|', { a: httpResponse });
const response = cold('-a|', { a: data });
const expected = cold('-b|', { b: data });
http.get = jest.fn(() => response);

10 changes: 5 additions & 5 deletions example-app/app/core/services/google-books.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
import 'rxjs/add/operator/map';
import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import { Book } from '../../books/models/book';

@Injectable()
export class GoogleBooksService {
private API_PATH = 'https://www.googleapis.com/books/v1/volumes';

constructor(private http: Http) {}
constructor(private http: HttpClient) {}

searchBooks(queryTitle: string): Observable<Book[]> {
return this.http
.get(`${this.API_PATH}?q=${queryTitle}`)
.map(res => res.json().items || []);
.get<{ items: Book[] }>(`${this.API_PATH}?q=${queryTitle}`)
.map(books => books.items || []);
}

retrieveBook(volumeId: string): Observable<Book> {
return this.http.get(`${this.API_PATH}/${volumeId}`).map(res => res.json());
return this.http.get<Book>(`${this.API_PATH}/${volumeId}`);
}
}
6 changes: 2 additions & 4 deletions modules/effects/package.json
Original file line number Diff line number Diff line change
@@ -10,12 +10,10 @@
"type": "git",
"url": "https://github.com/ngrx/platform.git"
},
"authors": [
"Mike Ryan"
],
"authors": ["Mike Ryan"],
"license": "MIT",
"peerDependencies": {
"@angular/core": "^4.0.0",
"@angular/core": "^4.0.0 || ^5.0.0-rc.2",
"@ngrx/store": "^4.0.0",
"rxjs": "^5.0.0"
}
6 changes: 2 additions & 4 deletions modules/entity/package.json
Original file line number Diff line number Diff line change
@@ -10,12 +10,10 @@
"type": "git",
"url": "https://github.com/ngrx/platform.git"
},
"authors": [
"Mike Ryan"
],
"authors": ["Mike Ryan"],
"license": "MIT",
"peerDependencies": {
"@angular/core": "^4.0.0",
"@angular/core": "^4.0.0 || ^5.0.0-rc.2",
"@ngrx/store": "^4.0.0",
"rxjs": "^5.0.0"
}
16 changes: 5 additions & 11 deletions modules/router-store/package.json
Original file line number Diff line number Diff line change
@@ -10,19 +10,13 @@
"type": "git",
"url": "git+https://github.com/ngrx/platform.git"
},
"keywords": [
"RxJS",
"Angular",
"Redux"
],
"authors": [
"Victor Savkin <[email protected]>"
],
"keywords": ["RxJS", "Angular", "Redux"],
"authors": ["Victor Savkin <[email protected]>"],
"license": "MIT",
"peerDependencies": {
"@angular/common": "^4.0.0",
"@angular/core": "^4.0.0",
"@angular/router": "^4.0.0",
"@angular/common": "^4.0.0 || ^5.0.0-rc.2",
"@angular/core": "^4.0.0 || ^5.0.0-rc.2",
"@angular/router": "^4.0.0 || ^5.0.0-rc.2",
"@ngrx/store": "^4.0.0",
"rxjs": "^5.0.0"
}
1 change: 0 additions & 1 deletion modules/router-store/spec/integration.spec.ts
Original file line number Diff line number Diff line change
@@ -458,7 +458,6 @@ describe('integration spec', () => {
{ type: 'router', event: 'GuardsCheckEnd', url: '/next' },
{ type: 'router', event: 'ResolveStart', url: '/next' },
{ type: 'router', event: 'ResolveEnd', url: '/next' },

{ type: 'router', event: 'NavigationEnd', url: '/next' },
]);

8 changes: 1 addition & 7 deletions modules/store-devtools/package.json
Original file line number Diff line number Diff line change
@@ -10,13 +10,7 @@
"type": "git",
"url": "git+https://github.com/ngrx/platform.git"
},
"keywords": [
"RxJS",
"Angular",
"Redux",
"Store",
"@ngrx/store"
],
"keywords": ["RxJS", "Angular", "Redux", "Store", "@ngrx/store"],
"contributors": [
{
"name": "Rob Wormald",
8 changes: 2 additions & 6 deletions modules/store/package.json
Original file line number Diff line number Diff line change
@@ -10,19 +10,15 @@
"type": "git",
"url": "git+https://github.com/ngrx/platform.git"
},
"keywords": [
"RxJS",
"Angular",
"Redux"
],
"keywords": ["RxJS", "Angular", "Redux"],
"author": "Rob Wormald <[email protected]>",
"license": "MIT",
"bugs": {
"url": "https://github.com/ngrx/platform/issues"
},
"homepage": "https://github.com/ngrx/platform#readme",
"peerDependencies": {
"@angular/core": "^4.0.0",
"@angular/core": "^4.0.0 || ^5.0.0-rc.2",
"rxjs": "^5.0.0"
}
}

0 comments on commit 3e5ff01

Please sign in to comment.