Skip to content

Commit

Permalink
feat(transition): Added transition.paramsChanged() to get added/delet…
Browse files Browse the repository at this point in the history
…ed/changed parameter values for a transition
  • Loading branch information
christopherthielen committed May 29, 2018
1 parent fd6e0d8 commit 10b7fde
Show file tree
Hide file tree
Showing 2 changed files with 150 additions and 0 deletions.
82 changes: 82 additions & 0 deletions src/transition/transition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import { UIInjector } from '../interface';
import { RawParams } from '../params/interface';
import { ResolvableLiteral } from '../resolve/interface';
import { Rejection } from './rejectFactory';
import { applyPairs, flattenR, uniqR } from '../common';

/** @hidden */
const stateSelf: (_state: StateObject) => StateDeclaration = prop('self');
Expand Down Expand Up @@ -286,6 +287,87 @@ export class Transition implements IHookRegistry {
return Object.freeze(this._treeChanges[pathname].map(prop('paramValues')).reduce(mergeR, {}));
}

/**
* Gets the new values of any parameters that changed during this transition.
*
* Returns any parameter values that have changed during a transition, as key/value pairs.
*
* - Any parameter values that have changed will be present on the returned object reflecting the new value.
* - Any parameters that *not* have changed will not be present on the returned object.
* - Any new parameters that weren't present in the "from" state, but are now present in the "to" state will be present on the returned object.
* - Any previous parameters that are no longer present (because the "to" state doesn't have them) will be included with a value of `undefined`.
*
* The returned object is immutable.
*
* #### Examples:
*
* Given:
* ```js
* var stateA = { name: 'stateA', url: '/stateA/:param1/param2' }
* var stateB = { name: 'stateB', url: '/stateB/:param3' }
* var stateC = { name: 'stateB.nest', url: '/nest/:param4' }
* ```
*
* #### Example 1
*
* From `/stateA/abc/def` to `/stateA/abc/xyz`
*
* ```js
* var changed = transition.paramsChanged()
* // changed is { param2: 'xyz' }
* ```
*
* The value of `param2` changed to `xyz`.
* The value of `param1` stayed the same so its value is not present.
*
* #### Example 2
*
* From `/stateA/abc/def` to `/stateB/123`
*
* ```js
* var changed = transition.paramsChanged()
* // changed is { param1: undefined, param2: undefined, param3: '123' }
* ```
*
* The value `param3` is present because it is a new param.
* Both `param1` and `param2` are no longer present so their value is undefined.
*
* #### Example 3
*
* From `/stateB/123` to `/stateB/123/nest/456`
*
* ```js
* var changed = transition.paramsChanged()
* // changed is { param4: '456' }
* ```
*
* The value `param4` is present because it is a new param.
* The value of `param3` did not change, so its value is not present.
*
* @returns an immutable object with changed parameter keys/values.
*/
paramsChanged(): { [paramName: string]: any };
paramsChanged<T>(): T;
paramsChanged() {
const fromParams = this.params('from');
const toParams = this.params('to');

// All the parameters declared on both the "to" and "from" paths
const allParamDescriptors: Param[] = []
.concat(this._treeChanges.to)
.concat(this._treeChanges.from)
.map(pathNode => pathNode.paramSchema)
.reduce(flattenR, [])
.reduce(uniqR, []);

const changedParamDescriptors = Param.changed(allParamDescriptors, fromParams, toParams);

return changedParamDescriptors.reduce((changedValues, descriptor) => {
changedValues[descriptor.id] = toParams[descriptor.id];
return changedValues;
}, {});
}

/**
* Creates a [[UIInjector]] Dependency Injector
*
Expand Down
68 changes: 68 additions & 0 deletions test/transitionSpec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1199,6 +1199,74 @@ describe('transition', function() {
});
});

describe('paramsChanged()', () => {
beforeEach(() => {
router.stateRegistry.register({ name: 'stateA', url: '/stateA/:param1/:param2' });
router.stateRegistry.register({ name: 'stateB', url: '/stateB/:param3' });
router.stateRegistry.register({ name: 'stateB.nest', url: '/nest/:param4' });
});

it('should contain only changed param values', async done => {
await $state.go('stateA', { param1: 'abc', param2: 'def' });
const goPromise = $state.go('stateA', { param1: 'abc', param2: 'xyz' });
await goPromise;

const changed = goPromise.transition.paramsChanged();
expect(Object.keys(changed)).toEqual(['param2']);
expect(changed).toEqual({ param2: 'xyz' });

done();
});

it('should contain new params values when switching states', async done => {
await $state.go('stateB', { param3: '123' });
const goPromise = $state.go('stateB.nest', { param3: '123', param4: '456' });
await goPromise;

const changed = goPromise.transition.paramsChanged();
expect(Object.keys(changed)).toEqual(['param4']);
expect(changed).toEqual({ param4: '456' });

done();
});

it('should contain removed params with `undefined` when switching states', async done => {
await $state.go('stateB.nest', { param3: '123', param4: '456' });
const goPromise = $state.go('stateB');
await goPromise;

const changed = goPromise.transition.paramsChanged();
expect(Object.keys(changed)).toEqual(['param4']);
expect(changed).toEqual({ param4: undefined });

done();
});

it('should contain removed params with `undefined` when switching states', async done => {
await $state.go('stateB.nest', { param3: '123', param4: '456' });
const goPromise = $state.go('stateB');
await goPromise;

const changed = goPromise.transition.paramsChanged();
expect(Object.keys(changed)).toEqual(['param4']);
expect(changed).toEqual({ param4: undefined });

done();
});

it('should contain added and removed params', async done => {
await $state.go('stateB.nest', { param3: '123', param4: '456' });
const goPromise = $state.go('stateA', { param1: 'abc', param2: 'def' });
await goPromise;

const changed = goPromise.transition.paramsChanged();
expect(Object.keys(changed).sort()).toEqual(['param1', 'param2', 'param3', 'param4']);
expect(changed).toEqual({ param1: 'abc', param2: 'def', param3: undefined, param4: undefined });

done();
});
});

describe('from previous transitions', () => {
it('should get their Transition resolves cleaned up', async done => {
router.stateRegistry.register({ name: 'resolve', resolve: { foo: () => 'Some data' } });
Expand Down

0 comments on commit 10b7fde

Please sign in to comment.