Coder Social home page Coder Social logo

ryanpatrickcook / ember-parachute Goto Github PK

View Code? Open in Web Editor NEW

This project forked from offirgolan/ember-parachute

0.0 2.0 0.0 1.26 MB

Improved Query Params for Ember

Home Page: https://offirgolan.github.io/ember-parachute

License: MIT License

JavaScript 80.93% HTML 11.44% CSS 7.63%

ember-parachute's Introduction

Ember Parachute

Build Status npm version

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:

// controllers/my-route.js
import Ember from 'ember';
import QueryParams from 'ember-parachute';

export const myQueryParams = new QueryParams({
  parachuteOpen: {
    as: 'parachute',
    defaultValue: true,
  },
  page: {
    defaultValue: 1,
    refresh: true,
    replace: true
  },
  search: {
    defaultValue: '',
    refresh: true
  },
  tags: {
    defaultValue: ['Ember', 'Parachute'],
    serialize(value) {
      return value.toString();
    },
    deserialize(value = '') {
      return value.split(',');
    }
  }
});

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

  setup({ queryParams }) {
    this.fetchData(queryParams);
  },

  queryParamsDidChange({ shouldRefresh, queryParams }) {
    // if any query param with `refresh: true` is changed, `shouldRefresh` is `true`
    if (shouldRefresh) {
      this.fetchData(queryParams);
    }
  },

  reset({ queryParams }, isExiting) {
    if (isExiting) {
      this.resetQueryParams();
    }
  },

  fetchData(queryParams) {
    // fetch data
  },

  actions: {
    resetAll() {
      // reset all query params to their default values specified in the query param map
      this.resetQueryParams();
    }
  }
});

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.

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:

import QueryParams from 'ember-parachute';

const myQueryParams = new QueryParams({
  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):

interface QueryParamOption {
  as?: string;
  defaultValue: any; // required
  refresh?: boolean;
  replace?: boolean;
  scope?: 'controller';
  serialize?(value: any): any;
  deserialize?(value: any): any;
}

For example:

direction: {
  as: 'dir',
  defaultValue: 'asc',
  refresh: true,
  scope: 'controller',
  serialize(value) {
    return value;
  },
  deserialize(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'],
  serialize(value) {
    return value.toString();
  },
  deserialize(value = '') {
    return value.split(',');
  }
}

The above will show ?tags=Ember,Parachute (before encoding) in the URL. When you get the value though, it is still an array:

controller.get('tags'); // ['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,
  serialize(value) {
    return value ? 'yes' : 'no';
  },
  deserialize(value) {
    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.

import QueryParams from 'ember-parachute';

const SortParams = {
  sortName: {
    defaultValue: 'name',
    refresh: true
  },
  sortDirection: {
    defaultValue: 'asc',
    refresh: true
  }
};

const SearchParams = {
  query: {
    defaultValue: '',
    refresh: true
  }
}

const myQueryParams = new QueryParams(SortParams, SearchParams /*, ... */);

const myExtendedQueryParams = myQueryParams.extend({
  sidebarOpen: {
    defaultValue: true
  }
} /*, ... */);

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 = new QueryParams({ /* ... */ });

export default Ember.Controller.extend(myQueryParams.Mixin, {
  // ...
});

The mixin adds the following to your controller:

Computed Property - allQueryParams

You can use this CP to get all query param values:

controller.get('allQueryParams'); // { 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.

Computed Property - queryParamsState

This CP returns an object with information about the state of your query params.

controller.get('queryParamsState.page'); // { 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: Ember.computed.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.

Hooks

All hooks will receives a ParachuteEvent as an argument which can be defined as:

// what changed
interface QueryParamsChanged {
  [queryParamKey: string]: string;
}

// all changes
interface QueryParamsChanges {
  [queryParamKey: string]: string;
}

// all query param values
interface QueryParams {
  [queryParamKey: string]: any;
}

interface ParachuteEvent {
  changes: QueryParamsChanges;
  changed: QueryParamsChanged;
  queryParams: QueryParams;
  routeName: string;
  shouldRefresh: boolean;
}

Hook - queryParamsDidChange

function queryParamsDidChange(queryParamsChangedEvent: ParachuteEvent): void;
export default Controller.extend(myQueryParams.Mixin, {
  queryParamsDidChange({ routeName, shouldRefresh, queryParams, changed, changes }) {
    if (shouldRefresh) {
      // refetch data
    }

    if (changed.myQueryParamKey) {
      // do something special
    }
  }
});

Hook - setup

function setup(queryParamsChangedEvent: ParachuteEvent): void;
export default Controller.extend(myQueryParams.Mixin, {
  setup({ routeName, shouldRefresh, queryParams, changed, changes }) {
    // Fetch some initial data & setup the controller
  }
});

Hook - reset

function reset(queryParamsChangedEvent: ParachuteEvent, isExiting: boolean): void;
export default Controller.extend(myQueryParams.Mixin, {
  reset({ routeName, shouldRefresh, queryParams, changed, changes }, isExiting) {
    if (isExiting) {
      this.resetQueryParams();
    }
  }
});

Events

The controller also emits an event for each hook which receives the same arguments:

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

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

  onReset: Ember.on('reset', function(queryParamsChangedEvent: ParachuteEvent, isExiting: boolean) {
    // ...
  })
});

For example, you can use this in conjunction with ember-metrics to track when query params were changed:

this.get('metrics').trackEvent(Object.assign({
  event: 'Query Params Changed',
  routeName: routeName,
}, queryParams));

Function - resetQueryParams

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:

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

  actions: {
    resetAll() {
      this.resetQueryParams();
    }
  }
});
{{#button onClick=(action "resetAll") disabled=(not queryParamsChanged)}}
  Reset All
{{/button}}

Function - setDefaultQueryParamValue

function setDefaultQueryParamValue(key: string, defaultValue: any): void;

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.setDefaultQueryParamValue('search', 'foo');
controller.setDefaultQueryParamValue('direction', 'asc');

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.

ember-parachute's People

Contributors

abhilashlr avatar anehx avatar ember-tomster avatar heroiceric avatar jasonmit avatar mazondo avatar offirgolan avatar poteto avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.