Ember Parachute
Philosophy
ember-parachute
is an addon that improves upon the experience of working with
query params in Ember. Instead of defining query params in both your route and controller, with this addon you can define them in one place as a query param map/object.
This map is the source of truth for your query params, and will generate a mixin that you can then add into your controller. The mixin adds very helpful properties and methods that makes working with query params a breeze!
One important point about this addon is that it is opinionated about where the data is fetched. In a traditional query params setup, your route is responsible for fetching data in its model
hook. With this addon, the responsibility moves into the controller. The benefit of this approach is that data fetching no longer blocks your UI from loading, and paves the way for advanced UX such as "skeleton loading".
Installation
ember install ember-parachute
Helpful Links
Looking for help?
If it is a bug please open an issue on GitHub.
Usage
The source of truth for your application's query params are query param maps. First, define one in your controller:
;;; const myQueryParams = parachuteOpen: as: 'parachute' defaultValue: true page: defaultValue: 1 refresh: true replace: true search: defaultValue: '' refresh: true tags: defaultValue: 'Ember' 'Parachute' { return value; } { return value; } ; ;
In the above example, the mixin adds the setup
, reset
, and queryParamsDidChange
hooks. You can use these hooks to perform tasks like fetching data based on the query params or resetting them when leaving the route. Additionally, you can create a computed property observing queryParamsState
that will allow you to display a button in your UI that can clear all query params via resetQueryParams
.
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
;; @ parachuteOpen = true; @ page = 1; @ search = ''; @ 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.
;; @withParachute {} {}
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:
; const myQueryParams = direction: as: 'dir' defaultValue: 'asc' refresh: true page: defaultValue: 1 refresh: true replace: true search: defaultValue: '' refresh: true ;
Each query param is defined as follows (using TypeScript notation for documenting types):
For example:
direction: as: 'dir' defaultValue: 'asc' refresh: true scope: 'controller' { return value; } { return value; }
as
The as
option lets you optionally override the query param URL key for a query param. By default this will be the same as the key in the query param map.
defaultValue
Required. The defaultValue
option specifies the default value for the query param. When a query param is set to its default value, it will not appear in the URL.
refresh
When refresh
is true
, the queryParamsDidChange
hook provided by the mixin will notify you when a refreshable query param has changed. You can use that value to determine whether or not you need to refetch data.
replace
By default, Ember will use pushState to update the URL in the address bar in response to a controller query param property change, but when replace
is true
it will use replaceState instead (which prevents an additional item from being added to your browser's history).
scope
scope
can only be one value if specified: controller
. This is equivalent to the scope
option in regular Ember query params. You can read more about it in the bottom paragraph here.
serialize
An optional function that lets you serialize or format a value before it is updated in the URL. For example, if your query param represents an array of values, you could do the following to avoid having the [
and ]
being included into the URL:
tags: defaultValue: 'Ember' 'Parachute' { return value; } { return value; }
The above will show ?tags=Ember,Parachute
(before encoding) in the URL. When you get the value though, it is still an array:
controller; // ['Ember', 'Parachute']
deserialize
If you provide a serialize
function, you will need to include a deserialize
as well. This function will be used to transform the value in the URL back into the value your controller receives.
For example:
showReadme: as: 'readme' defaultValue: true { return value ? 'yes' : 'no'; } { return value === 'yes' ? true : false; }
Your controller value for showReadme
will still be true
or false
, even though it is displayed in your URL as ?showReadme=yes
.
Extending
The Query Param Map not only accepts multiple arguments, but it can also be extended.
; const SortParams = sortName: defaultValue: 'name' refresh: true sortDirection: defaultValue: 'asc' refresh: true ; const SearchParams = query: defaultValue: '' refresh: true ; const myQueryParams = SortParams SearchParams /*, ... */; const myExtendedQueryParams = myQueryParams;
With the above code, the myExtendedQueryParams
map will generate Query Params for sortName
, sortDirection
, query
, and sidebarOpen
.
Controller Mixin
After creating a query params map, you can generate a controller mixin with the Mixin
property on the query params map:
const myQueryParams = /* ... */; ;
The mixin adds the following to your controller:
allQueryParams
Computed Property - You can use this CP to get all query param values:
controller; // { page: 2, direction: 'desc' };
This CP is useful in scenarios where you need to pass all query params as an option. For example, you could pass these into a link-to
.
queryParamsState
Computed Property - This CP returns an object with information about the state of your query params.
controller; // { value: 2, defaultValue: 1, changed: true }
This CP is useful when creating another CP to determine if any query params have changed from their default values:
queryParamsChanged: ;
You can then use this CP to conditionally display a button that can clear all query params to their default values.
Hooks
All hooks will receives a ParachuteEvent
as an argument which can be defined as:
// what changed // all changes // all query param values
queryParamsDidChange
Hook - ;
;
setup
Hook - ;
;
Note: If you've overridden your route's
setupController
, you must usethis._super(...arguments);
insetupController
for thesetup
hook to fire.
reset
Hook - ;
;
Note: If you've overridden your route's
resetController
, you must usethis._super(...arguments);
inresetController
for thereset
hook to fire.
Events
The controller also emits an event for each hook which receives the same arguments:
;
For example, you can use this in conjunction with ember-metrics
to track when query params were changed:
this;
resetQueryParams
Function - ;
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:
;
{{#button onClick=(action "resetAll") disabled=(not queryParamsChanged)}} Reset All{{/button}}
Note: This method works well in conjunction with
setDefaultQueryParamValue
.
setDefaultQueryParamValue
Function - ;
Set the default value for a given param. An example where this is useful is where you need to fetch default values from elsewhere (e.g. your API).
controller;controller;
If you set a new default value after the existing default has been promoted to the value, like when calling setDefaultQueryParamValue
in the setup
hook, you need to call resetQueryParams
to apply those new defaults as the values. You can do this for all QPs or for a single one via resetQueryParams(['nameOfQp'])
.
NOTE: Changing the defaultValue at any point will not clear the query parameter from being shown in the URI. We do not have control over that as it is private API.