Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: queryParam & withParachute decorators #69

Merged
merged 2 commits into from
Jan 9, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 11 additions & 10 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
module.exports = {
root: true,
parser: 'babel-eslint',
parserOptions: {
ecmaVersion: 2017,
sourceType: 'module'
},
plugins: [
'ember'
],
extends: [
'eslint:recommended',
'plugin:ember/recommended'
],
plugins: ['ember'],
extends: ['eslint:recommended', 'plugin:ember/recommended'],
env: {
browser: true
},
rules: {
'ember/avoid-leaking-state-in-ember-objects': 'off'
},
overrides: [
// node files
Expand Down Expand Up @@ -44,9 +41,13 @@ module.exports = {
node: true
},
plugins: ['node'],
rules: Object.assign({}, require('eslint-plugin-node').configs.recommended.rules, {
// add your custom rules and overrides for node files here
})
rules: Object.assign(
{},
require('eslint-plugin-node').configs.recommended.rules,
{
// add your custom rules and overrides for node files here
}
)
}
]
};
84 changes: 66 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ If it is a bug [please open an issue on GitHub](http://github.com/offirgolan/emb
The source of truth for your application's query params are query param maps. First, define one in your controller:

```js
// controllers/my-route.js
import Ember from 'ember';
import Controller from '@ember/controller';
import QueryParams from 'ember-parachute';
import { or } from '@ember/object/computed';

export const myQueryParams = new QueryParams({
parachuteOpen: {
Expand All @@ -62,10 +62,8 @@ export const myQueryParams = new QueryParams({
}
});

export default Ember.Controller.extend(myQueryParams.Mixin, {
queryParamsChanged: Ember.computed.or(
'queryParamsState.{page,search,tags}.changed'
),
export default Controller.extend(myQueryParams.Mixin, {
queryParamsChanged: or('queryParamsState.{page,search,tags}.changed'),

setup({ queryParams }) {
this.fetchData(queryParams);
Expand Down Expand Up @@ -101,6 +99,60 @@ In the above example, the mixin adds the `setup`, `reset`, and `queryParamsDidCh

Please continue reading for more advanced usage.

## Decorators

This package provides some decorators in order to use ember-parachute with the now supported class syntax.

### `queryParam`

```js
import Controller from '@ember/controller';
import { queryParam } from 'ember-parachute/decorators';

export default class MyController extends Controller {
@queryParam({
as: 'parachute',
serialize(value) {
return value ? 'open' : 'closed';
},
deserialize(value) {
return value === 'open' ? true : false;
}
})
parachuteOpen = true;

@queryParam({ refresh: true, replace: true }) page = 1;

@queryParam({ refresh: true }) search = '';

@queryParam({
refresh: true,
serialize(value = '') {
return value.toString();
},
deserialize(value = '') {
return value.split(',');
}
})
tags = ['Ember', 'Parachute'];
}
```

### `withParachute`

If you're not using any query params but still want the `setup` and `reset` hooks, you can use the `withParachute` class decorator.

```js
import Controller from '@ember/controller';
import { withParachute } from 'ember-parachute/decorators';

@withParachute
export default class MyController extends Controller {
setup() {}
reset() {}
}
```

## Query Param Map

The query param map is the source of truth for your query params. Here, you'll be able to define configuration for each query param:
Expand Down Expand Up @@ -267,7 +319,7 @@ const myQueryParams = new QueryParams({
/* ... */
});

export default Ember.Controller.extend(myQueryParams.Mixin, {
export default Controller.extend(myQueryParams.Mixin, {
// ...
});
```
Expand Down Expand Up @@ -295,9 +347,7 @@ controller.get('queryParamsState.page'); // { value: 2, defaultValue: 1, changed
This CP is useful when creating another CP to determine if any query params have changed from their default values:

```js
queryParamsChanged: Ember.computed.or(
'queryParamsState.{page,search,tags}.changed'
);
queryParamsChanged: or('queryParamsState.{page,search,tags}.changed');
```

You can then use this CP to conditionally display a button that can clear all query params to their default values.
Expand Down Expand Up @@ -402,18 +452,18 @@ export default Controller.extend(myQueryParams.Mixin, {
The controller also emits an event for each hook which receives the same arguments:

```ts
export default Ember.Controller.extend({
onChange: Ember.on('queryParamsDidChange', function(
export default Controller.extend({
onChange: on('queryParamsDidChange', function(
queryParamsChangedEvent: ParachuteEvent
) {
// ...
}),

onSetup: Ember.on('setup', function(queryParamsChangedEvent: ParachuteEvent) {
onSetup: on('setup', function(queryParamsChangedEvent: ParachuteEvent) {
// ...
}),

onReset: Ember.on('reset', function(
onReset: on('reset', function(
queryParamsChangedEvent: ParachuteEvent,
isExiting: boolean
) {
Expand Down Expand Up @@ -445,10 +495,8 @@ function resetQueryParams(params?: string[]): void;
Reset all or given params to their default value. The second argument is an array of query params to reset. If empty, all query params will be reset. You can use this in an action to reset query params when they have changed:

```js
export default Ember.Controller.extend(myQueryParams.Mixin, {
queryParamsChanged: Ember.computed.or(
'queryParamsState.{page,search,tags}.changed'
),
export default Controller.extend(myQueryParams.Mixin, {
queryParamsChanged: or('queryParamsState.{page,search,tags}.changed'),

actions: {
resetAll() {
Expand Down
27 changes: 1 addition & 26 deletions addon/-private/query-param.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
import { get } from '@ember/object';
import { assert } from '@ember/debug';
import { isPresent, isEmpty } from '@ember/utils';
import { isPresent } from '@ember/utils';
import Ember from 'ember';

const { canInvoke } = Ember;

const { keys } = Object;

const REQUIRED_PROPS = ['defaultValue'];

/**
* Normalized query param object.
*
Expand All @@ -21,10 +17,6 @@ export default class QueryParam {
`[ember-parachute] You must specify a key to the QueryParam Class`,
isPresent(key)
);
assert(
`[ember-parachute] You must specify all required fields for the query param: '${key}'`,
this._validateOptions(options)
);

/** @type {string} */
this.key = key;
Expand Down Expand Up @@ -88,21 +80,4 @@ export default class QueryParam {
toString() {
return `QueryParam<${this.key}>`;
}

/**
* Validate required options.
*
* @private
* @param {object} options
* @returns {boolean}
*
* @memberof QueryParam
*/
_validateOptions(options) {
let optionKeys = keys(options);
return (
!isEmpty(optionKeys) &&
REQUIRED_PROPS.every(p => optionKeys.indexOf(p) > -1)
);
}
}
16 changes: 16 additions & 0 deletions addon/decorators/-private/query-params-for.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import QueryParams from '../../query-params';

const QP_MAP = new WeakMap();

export function getQueryParamsFor(klass) {
QP_MAP.set(klass, QP_MAP.get(klass) || new QueryParams());

return QP_MAP.get(klass);
}

export function addQueryParamFor(klass, key, definition) {
QP_MAP.set(
klass,
getQueryParamsFor(klass).extend({ [key]: definition || {} })
);
}
2 changes: 2 additions & 0 deletions addon/decorators/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default as queryParam } from './query-param';
export { default as withParachute } from './with-parachute';
40 changes: 40 additions & 0 deletions addon/decorators/query-param.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import {
addQueryParamFor,
getQueryParamsFor
} from './-private/query-params-for';

function createDescriptor(desc, qpDefinition) {
qpDefinition = qpDefinition || {};

const descriptor = {
...desc,
finisher(klass) {
addQueryParamFor(klass, desc.key, qpDefinition);
klass.reopen(getQueryParamsFor(klass).Mixin);

return klass;
}
};

if (desc.kind === 'field') {
if (typeof desc.initializer === 'function') {
qpDefinition.defaultValue = desc.initializer();
}

descriptor.initializer = function initializer() {
return qpDefinition.defaultValue;
};
}

return descriptor;
}

export default function queryParam(qpDefinition) {
// Handle `@queryParam` usage
if (`${qpDefinition}` === '[object Descriptor]') {
return createDescriptor(qpDefinition);
}

// Handle `@queryParam()` usage
return desc => createDescriptor(desc, qpDefinition);
}
12 changes: 12 additions & 0 deletions addon/decorators/with-parachute.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import QueryParams from '../query-params';

export default function withParachute(desc) {
return {
...desc,
finisher(klass) {
klass.reopen(new QueryParams().Mixin);

return klass;
}
};
}
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@
"ember-cli-babel": "^7.1.2"
},
"devDependencies": {
"@ember-decorators/babel-transforms": "^4.0.0",
"@ember/jquery": "^0.5.2",
"@ember/optional-features": "^0.6.3",
"@types/ember": "^3.0.26",
"babel-eslint": "^10.0.1",
"broccoli-asset-rev": "^2.7.0",
"ember-cli": "~3.6.1",
"ember-cli-autoprefixer": "^0.8.1",
Expand All @@ -54,6 +56,8 @@
"ember-cli-uglify": "^2.1.0",
"ember-composable-helpers": "^2.1.0",
"ember-concurrency": "^0.8.26",
"ember-concurrency-decorators": "^0.5.0",
"ember-decorators": "^4.0.0",
"ember-disable-prototype-extensions": "^1.1.3",
"ember-export-application-global": "^2.0.0",
"ember-load-initializers": "^2.0.0",
Expand Down
Loading