Skip to content
This repository has been archived by the owner on Dec 26, 2017. It is now read-only.

Commit

Permalink
chore(docs): Add API and Testing documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
MikeRyanDev committed Sep 27, 2016
1 parent cddb9ae commit 0dbff75
Show file tree
Hide file tree
Showing 4 changed files with 279 additions and 90 deletions.
96 changes: 6 additions & 90 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,97 +7,13 @@ To install @ngrx/effects from npm:
```bash
npm install @ngrx/effects --save
```
### Example Application

https://github.com/ngrx/example-app

## Effects
In @ngrx/effects, effects are _sources of actions_. You use the `@Effect()` decorator to hint which observables on a service are action sources, and @ngrx/effects automatically merges your action streams letting you subscribe them to store.

To help you compose new action sources, @ngrx/effects exports an `Actions` observable service that emits every action dispatched in your application.


### Example
1. Create an AuthEffects service that describes a source of login actions:
```ts
import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import { Actions, Effect } from '@ngrx/effects';

@Injectable()
export class AuthEffects implements OnDestroy {
constructor(
private http: Http,
private actions$: Actions
) { }

@Effect() login$ = this.actions$
// Listen for the 'LOGIN' action
.ofType('LOGIN')
// Map the payload into JSON to use as the request body
.map(action => JSON.stringify(action.payload))
.switchMap(payload => this.http.post('/auth', payload)
// If successful, dispatch success action with result
.map(res => ({ type: 'LOGIN_SUCCESS', payload: res.json() }))
// If request fails, dispatch failed action
.catch(() => Observable.of({ type: 'LOGIN_FAILED' }));
);
}
```

2. Provide your service via `EffectsModule.run` to automatically start your effect:
```ts
import { AuthEffects } from './effects/auth';
import { EffectsModule } from '@ngrx/effects';
### Documentation

@NgModule({
imports: [
EffectsModule.run(AuthEffects)
]
})
export class AppModule { }
```
*Note*: For effects that depend on the application to be bootstrapped (i.e. effects that depend on the Router) use `EffectsModule.runAfterBootstrap`. Be aware that `runAfterBootstrap` will only work in the root module.
* [Intro to @ngrx/effects](./docs/intro.md)
* [API](./docs/api.md)
* [Testing](./docs/testing.md)

### Example Application


### Testing Effects

1. Declare the `EffectsTestingModule` in your testing module:
```ts
import { EffectsTestingModule, EffectsTestRunner } from '@ngrx/effects';

describe('My Effect', () => {
beforeEach(() => TestBed.configureTestingModule({
imports: [
EffectsTestingModule
],
declarations: [
AuthEffects
]
}));
});
```

2. Inject the `EffectsTestRunner` and use it queue actions:
```ts
let runner: EffectsTestRunner;

beforeEach(inject([
EffectsTestRunner,
(_runner) => {
runner = _runner;
}
]));
```

3. Queue up actions then subscribe to the effect you want to test, asserting on the result:
```ts
it('should return a LOGIN_SUCCESS action after logging in', () => {
runner.queue({ type: 'LOGIN' });

authEffects.login$.subscribe(result => {
expect(result).toEqual({ type: 'LOGIN_SUCCESS' });
});
});
```
https://github.com/ngrx/example-app
164 changes: 164 additions & 0 deletions docs/api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
# @ngrx/effects API



## EffectsModule

Feature module for @ngrx/effects.

### run

Registers an effects class to be run immediately when the target module is
created. Must be called multiple times for each effect class you want to run.

Usage:
```ts
@NgModule({
imports: [
EffectsModule.run(SomeEffectsClass)
]
})
export class AppModule { }
```

### runAfterBootstrap

Registers an effects class to be run after root components are bootstrapped.
Only works in the root module. Useful if your effects class requires components
to be bootstrapped.

If you are using a version of Angular older than 2.1, `runAfterBootstrap` is
necessary for effects that inject services from `@angular/router`.

Usage:
```ts
@NgModule({
imports: [
EffectsModule.runAfterBootstrap(SomeEffectsClass)
]
})
```


## Actions

Stream of all actions dispatched in your application including actions
dispatched by effect streams.

### ofType

Filter actions by action type. Accepts multiple action types.

Usage:
```
actions$.ofType('LOGIN', 'LOGOUT');
```


## Effect

Decorator that marks a class property or method as an effect. Causes the
decorated observable to be subscribed to `Store` when the effect class is
ran.

Usage:
```ts
class MyEffects {
constructor(private actions$: Actions, private auth: AuthService) { }

@Effect() login$: Observable<Actions> = this.actions$
.ofType('LOGIN')
.switchMap(action =>
this.auth.login(action.payload)
.map(res => ({ type: 'LOGIN_SUCCESS', payload: res }))
.catch(err => Observable.of({ type: 'LOGIN_FAILURE', payload: err }))
);

@Effect() logout(): Observable<Actions> {
return this.actions$
.ofType('LOGOUT')
.switchMap(() =>
this.auth.logout()
.map(res => ({ type: 'LOGOUT_SUCCESS', payload: res }))
.catch(err => Observable.of({ type: 'LOGOUT_FAILURE', payload: err }))
);
}
}
```

Observables decorated with the `@Effect()` decorator are expected to be a stream
of actions to be dispatched. Pass `{ dispatch: false }` to the decorator to
prevent actions from being dispatched.

Usage:
```ts
class MyEffects {
constructor(private actions$: Actions) { }

@Effect({ dispatch: false }) logActions$ = this.actions$
.do(action => {
console.log(action);
});
}
```

## Utilities

### toPayload
Maps an action to its payload.

Usage:
```ts
actions$.ofType('LOGIN').map(toPayload);
```

### mergeEffects
Manually merges all decorated effects into a combined observable.

Usage:
```ts
export class MyService {
constructor(effects: SomeEffectsClass) {
mergeEffects(effects).subscribe(result => {

});
}
}
```

## EffectsSubscription

An rxjs subscription of all effects running for the current injector. Can be
used to stop all running effects contained in the subscription.

Usage:
```ts
class MyComponent {
constructor(private subscription: EffectsSubscription) { }

ngOnDestroy() {
this.subscription.unsubscribe();
}
}
```

### addEffects
Add additional instances of effect classes to the subscription.

Usage:
```ts
class MyComponent {
constructor(effects: MoreEffects, subscription: EffectsSubscription) {
subscription.addEffects([ moreEffects ]);
}
}
```

### parent
A pointer to the parent subscription. Used to access an entire tree of
subscriptions.

Usage:
```ts
subscription.parent.unsubscribe();
```
68 changes: 68 additions & 0 deletions docs/intro.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Introduction

## Effects
In @ngrx/effects, effects are _sources of actions_. You use the `@Effect()`
decorator to hint which observables on a service are action sources, and
@ngrx/effects automatically merges your action streams letting you
subscribe them to store.

To help you compose new action sources, @ngrx/effects exports an `Actions`
observable service that emits every action dispatched in your application.


### Example
1. Create an AuthEffects service that describes a source of login actions:
```ts
import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import { Actions, Effect } from '@ngrx/effects';

@Injectable()
export class AuthEffects implements OnDestroy {
constructor(
private http: Http,
private actions$: Actions
) { }

@Effect() login$ = this.actions$
// Listen for the 'LOGIN' action
.ofType('LOGIN')
// Map the payload into JSON to use as the request body
.map(action => JSON.stringify(action.payload))
.switchMap(payload => this.http.post('/auth', payload)
// If successful, dispatch success action with result
.map(res => ({ type: 'LOGIN_SUCCESS', payload: res.json() }))
// If request fails, dispatch failed action
.catch(() => Observable.of({ type: 'LOGIN_FAILED' }));
);
}
```

2. Provide your service via `EffectsModule.run` to automatically start your effect:
```ts
import { EffectsModule } from '@ngrx/effects';
import { AuthEffects } from './effects/auth';

@NgModule({
imports: [
EffectsModule.run(AuthEffects)
]
})
export class AppModule { }
```
*Note*: For effects that depend on the application to be bootstrapped (i.e.
effects that depend on the Router) use `EffectsModule.runAfterBootstrap`. Be
aware that `runAfterBootstrap` will only work in the root module.


### SystemJS Configuration
@ngrx/effects is published with a UMD bundle. Map @ngrx/effects imports to the
bundle:

```ts
System.config({
map: {
'@ngrx/effects': 'npm:@ngrx/effects/bundles/effects.umd.js'
}
})
```
41 changes: 41 additions & 0 deletions docs/testing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Testing Effects

1. Import the `EffectsTestingModule` in your testing module:
```ts
import { EffectsTestingModule, EffectsRunner } from '@ngrx/effects/testing';

describe('My Effect', () => {
beforeEach(() => TestBed.configureTestingModule({
imports: [
EffectsTestingModule
],
declarations: [
AuthEffects
]
}));
});
```

2. Inject the `EffectsTestRunner`:
```ts
let runner: EffectsRunner;

beforeEach(inject([
EffectsRunner,
(_runner) => {
runner = _runner;
}
]));
```

3. Queue up actions and then subscribe to the effect you want to test asserting
on the result:
```ts
it('should return a LOGIN_SUCCESS action after logging in', () => {
runner.queue({ type: 'LOGIN' });

authEffects.login$.subscribe(result => {
expect(result).toEqual({ type: 'LOGIN_SUCCESS' });
});
});
```

0 comments on commit 0dbff75

Please sign in to comment.