Code Monkey home page Code Monkey logo

ember-parachute's Introduction

Ember Parachute

Build Status npm version Ember Observer Score

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:

import Controller from '@ember/controller';
import QueryParams from 'ember-parachute';
import { or } from '@ember/object/computed';

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 Controller.extend(myQueryParams.Mixin, {
  queryParamsChanged: 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.

Decorators

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

queryParam

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.

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:

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;
  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 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: 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
  }
});

Note: If you've overridden your route's setupController, you must use this._super(...arguments); in setupController for the setup hook to fire.

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();
    }
  }
});

Note: If you've overridden your route's resetController, you must use this._super(...arguments); in resetController for the reset hook to fire.

Events

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

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

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

  onReset: 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 Controller.extend(myQueryParams.Mixin, {
  queryParamsChanged: or('queryParamsState.{page,search,tags}.changed'),

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

Note: This method works well in conjunction with setDefaultQueryParamValue.

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');

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.

ember-parachute's People

Contributors

abhilashlr avatar anehx avatar buschtoens avatar chrisseto avatar efx avatar ember-tomster avatar heroiceric avatar jasonmit avatar knownasilya avatar maxscalefactor avatar mazondo avatar offirgolan avatar poteto avatar scottkidder avatar zeppelin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

ember-parachute's Issues

setDefaultQueryParamValue - not working as expected?

First off, I think I just stumbled upon a gem. ๐Ÿ‘ LOVE the problems this addon is proposing to solve!

(Let me know if my expectations are off on this or not.)
I'm assuming that anywhere/anytime I set setDefaultQueryParamValue and those queryParams are in the URI they'll be removed (because they are now the default)? Currently that isn't the case though, looks to be the same way in the demo as well. You add "Jump" to the tags and hit "Set as Defaults" but it remains in the URI.
Is this the expected behavior?

Then, my own application, (perhaps you can tell me if this is the improper place to use setDefaultQueryParamValue):
Within my route's setupController I make a request to the API and in that response I'm able to derive what my queryParams defaults will be so I fire controller.setDefaultQueryParamValue('facetContext', record.mapBy('key')); however like I previously noted the URI does not change and is still populated with the query param values (which should now be the defaults).

Thanks!


EDIT: I've also tried moving my setDefaultQueryParamValue call into the beforeModel hook in case things need to be set before a model hook resolves.
Just as a test if I was to statically set the defaultValue: ['value1', 'value2'], it will work as expected (omitting these values from the URL) but using setDefaultQueryParamValue instead does not work.

State

Should we be keeping state or QP values?

For example, we want to display a Clear All button when a query param is not its default value. How do we do that?

Option 1

Pass a new property to queryParamsDidChange that is an object that contains properties that are not their default values. This is different then #3 which exposed a present object. Since we allow to update the defaultValue of a param at any time, a param can be present but still be equal to its default value. (Should this replace the present property?)

Option 2

Keep some sort of state object on the service or on the controller.

// some/file/foo.js

sortHasChanged: computed.readOnly('qp.state.fooIndex.sort.hasChanged'),
qpsHaveChanged: computed.readOnly('qp.state.fooIndex.hasChanged')

IMO I'm a bigger fan of Option 1 since we can explicitly set properties based on the object returned.

Using key as reference to a service

I'm not sure if it was supported properly, but it worked well before upgrade to 0.3.8

We used a service to store query params in application controller to be accessible within our application.
So it was was working code

export const appQueryParams = new QueryParams({
  'someService.property': {
      as: 'property',
      defaultValue: 3600,
      replace: true,
      deserialize(value) {
        return parseInt(value);
      }
    }, 
});

export default Controller.extend(appQueryParams.Mixin, {
  someService: Ember.inject.service(),
})

But now it works only in case of alias is being set in controller like this

export const appQueryParams = new QueryParams({
  someServicePropertyAlias: {
      as: 'property',
      defaultValue: 3600,
      replace: true,
      deserialize(value) {
        return parseInt(value);
      }
    }, 
});

export default Controller.extend(appQueryParams.Mixin, {
  someService: Ember.inject.service(),
  someServicePropertyAlias: alias('someService.propertyAlias'),
})

Is this functionality supported? Or maybe there are issues in our code?
Thanks for help

Assertion Failed: Attempting to lookup an injected property on an object without a container

Observing this error when using the @queryParam decorator:

Error: Assertion Failed: Attempting to lookup an injected property on an object without a container, ensure that the object was instantiated via a container.

Strangely, it throws the error when trying to set up the service injections on my controller.

Usage:

export default class StuffController extends Controller {
  // error occurs when trying to set up this injection
  @service('stuff') stuffAPI;

  @tracked stuffs = [];

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

  @queryParam({ refresh: true }) pageSize = 100;
  ...
}

Versions

"ember-cli": "~3.13.0",
"ember-parachute": "^1.0.2",
"ember-source": "~3.13.1"

Setup hook not getting query params present in URL

setup({queryParams}) hook only gets the default values not those set in the current URL. Is there another hook that I'm not aware of?

If I do my fetchData from the setup method, its ignoring values in the URL on page load.

Upcoming RFC281 "native ES5 getters" incompatibility

https://github.com/emberjs/rfcs/blob/master/text/0281-es5-getters.md

https://travis-ci.org/offirgolan/ember-parachute/jobs/328966177#L754-L770

Error: Assertion Failed: You attempted to access the firstObject._getter property (of QueryParam,QueryParam). Due to certain internal implementation details of Ember, the firstObject property previously contained an internal "descriptor" object (a private API), therefore firstObject._getter would have been function () { return objectAt(this, 0); }. This internal implementation detail was never intended to be a public (or even intimate) API.

This internal implementation detail has now changed and the (still private) "descriptor" object has been relocated to the object's "meta" (also a private API). Soon, accessing firstObject on this object will return the computed value (see RFC #281 for more details).

If you are seeing this error, you are likely using an addon that relies on this now-defunct private implementation detail. If you can, find out which addon is doing this from the stack trace below and report this bug to the addon authors. If you feel stuck, the Ember Community Slack (https://ember-community-slackin.herokuapp.com/) may be able to offer some help.

If you are an addon author and need help transitioning your code, please get in touch in the #dev-ember channel in the Ember Community Slack.

   at new EmberError (http://localhost:7357/assets/vendor.js:24683:25)
   at Object.assert (http://localhost:7357/assets/vendor.js:24926:15)
   at Object.get (http://localhost:7357/assets/vendor.js:34533:42)
   at objectValues (http://localhost:7357/assets/test-support.js:2822:15)
   at objectValues (http://localhost:7357/assets/test-support.js:2823:40)
   at Assert.propEqual (http://localhost:7357/assets/test-support.js:4953:15)
   at Object.<anonymous> (http://localhost:7357/assets/tests.js:579:12)
   at runTest (http://localhost:7357/assets/test-support.js:4120:30)
   at Test.run (http://localhost:7357/assets/test-support.js:4106:6)
   at http://localhost:7357/assets/test-support.js:4312:12

Exported service doesn't exist

https://github.com/offirgolan/ember-parachute/blob/master/app/services/qp.js#L1

The file cannot be found when injecting the service, i.e.

ember-metal.js:3988 Error: Could not find module `ember-parachute/services/qp` imported from `bwt/services/qp`
    at missingModule (loader.js:247)
    at findModule (loader.js:258)
    at Module.findDeps (loader.js:168)
    at findModule (loader.js:262)
    at requireModule (loader.js:24)
    at Class._extractDefaultExport (index.js:392)
    at Class.resolveOther (index.js:113)
    at Class.superWrapper [as resolveOther] (ember-utils.js:423)
    at Class.resolve (resolver.js:164)
    at resolve (container.js:873)

I'd submit a fix if I knew where the service was..

Defining controllerName on a route throws an error

When a route has a controllerName defined, and the route doesn't have an explicit controller for itself, lookup-controller util throws an error at this line:

return factory.class.proto();

Taking just get(route, 'controller') will remain undefined for the route and it tries to get the default factory.class in the above line, but instead the route has a definition as controllerName as something else.

Instead if it also takes get(route, 'controllerName') in addition to get(route, 'controller'), it seems to solve the problem. But unsure if this is the right approach.

Ember version: 2.4

Changing query in setup hook

I'm doing query param validation on page load in the setup hook:

setup({ queryParams }) {
  if (this.isPastDate(queryParams.date)) {
    this.set('date', null);
  }
  this.fetchData(queryParams);
}

While the 'date' controller value updates, the URL still has the old value for date.

Is setup() hook not ideal for changing query params? Wanted to avoid using beforeModel() on the route, since this is more concise.

replaceRoute in setup sends incorrect event to queryParamsDidChange

Ran into a weird bug when you call replaceRoute and only change query params for the current route inside the setup method. What happens is the setup method runs and the query params are changed. The subsequent call ember parachute makes to queryParamsDidChange seems to contain the original(not updated) values in queryParams but includes any updated query params in changed. This cause some weird issues with my logic which was relying on changed.

Assume we have query params foo and bar and they default to empty strings. First hit the route and setup is called where the queryParams are { foo: '', bar: '' }. Then use replaceRoute in setup and set them to { foo: 'foo', bar: 'bar' }. When queryParamsDidChange is called its queryParams will be { foo: '', bar: '' }. but its changed will be { foo: 'foo', bar: 'bar' }. Let me know if you need a full reproduction and I can toss one together.

Support deserializing duplicate query params

Query parameters are allowed to be repeated in URLs, e.g. status=PaidTrial&status=Paid. As far as I can tell, ember-parachute doesn't seem to offer a means of accessing these duplicate query parameters. I know it would be a pseudo-breaking change, but it would be great if duplicate query parameters would cause an array to be passed into deserialize rather than just passing in one of the params and ignoring the other.

Properties decorated with queryParam are undefined

import { queryParam, withParachute } from 'ember-parachute/decorators';

export default class ApplicationController extends Controller {
  @queryParam
  selectedZoning = [];
}

selectedZoning is undefined when called from my template. No errors. Using Ember 3.10.

Any ideas?

Uncaught TypeError: Cannot assign to read only property 'foostate' of object '[object Object]'

I've been trying to use Ember Parachute with [email protected] and [email protected] with ES6 classes and decorators. The decorator form does not seem to work and complains about the property being read only.

Controller:

import { action } from '@ember/object';
import { queryParam } from 'ember-parachute/decorators';

export default class FooController extends Controller {
  @queryParam({
    as: "foo",
    serialize(value) {
      return value;
    },
    deserialize(value) {
      return value;
    }
  })
  foostate = "my state";

  @action
  updateState() {
    this.foostate = "my new state";
  }
}

Template:

{{page-title "Foo"}}

<button {{on "click" (action "updateState")}}>
  Update state
</button>
{{outlet}}

When I click the button I am returned the error

   at FooController.updateState (foo.js:38)
   at Backburner._run (backburner.js:1013)
   at Backburner._join (backburner.js:989)
   at Backburner.join (backburner.js:760)
   at join (index.js:168)
   at index.js:770
   at instrument (index.js:144)
   at index.js:769
   at Proxy.<anonymous> (index.js:727)

It doesn't seem to reach the serialize / deserialize methods at all, and fails at the point of trying to update the value.

I have also tried adding @tracked into the mix, but this still returns the same error. Hopefully I'm doing something wrong, but I can't see where.

Thanks in advance.

Using this addon inside of an engine causes Route to be reopened twice

When this addon is used inside of an engine, it's initializer is run twice, once for the parent app and once for the engine. This results in all of the hooks being fired twice.

For example, the queryParamsDidChange hook will run twice every time a query param changes.

I can create a demo app if you'd like.

Unexpected values of queryParams on a controller using decorators

I'm seeing unexpected values of queryParams on a controller using decorators.

Steps to reproduce:

  1. Add the following init method to tests/dummy/app/controllers/index.js.
init(){
  this._super(...arguments);
  console.log(this.get('queryParams'));
}
  1. Serve the app
    #. Notice in the console that queryParams is an array of hashes rather than an array or a hash.
[
  {"parachuteOpen":{"as":"parachute"}},
  {"parachuteOpen":{"as":"parachute"},"page":{"as":"page"}},
  {"parachuteOpen":{"as":"parachute"},"page":{"as":"page"},"search":{"as":"search"}},
  {"parachuteOpen":{"as":"parachute"},"page":{"as":"page"},"search":{"as":"search"},"tags":{"as":"tags"}}
]

I would have expected this value to be in one of the following formats:
[ "parachuteOpen", "page", "search", "tags" ]
{"parachuteOpen":{"as":"parachute"},"page":{"as":"page"},"search":{"as":"search"},"tags":{"as":"tags"}}

I believe this is cause when each decorator adds an additional init call.
One possible solution might be to guard against calling init multiple times.

Dynamically set query params

I would like to be able to define some query parameters dynamically. My first approach is to do this from the Route in setupController:

  setupController(controller, model) {
    // Parachute things here, dynamically build QP configuration object

    const parachuteController = controller.reopen(ParachuteParams.Mixin);
    super.setupController(parachuteController, model);
  }

This does not work because the query params don't seem to be set or updated when the properties are changed on the controller.

I've researched this quite a bit, but I can't find any different approach other than this discussion.

Any thoughts appreciated, thanks

Accessing the controller / services

Would it hurt, if we were able to access the corresponding Controller from the serialize and deserialize methods?

I am asking primarily because I need to synchronously access the store service, in order to lookup a record for an ID. Basically something like this:

new QueryParams({
  someModel: {
    defaultValue: null,

    serialize(model) {
      return model ? get(model, 'id') : '';
    },

    deserialize(id) {
      return id ? get(this, 'store').peekRecord('model-name', id) : null;
    }
});

Do you think this is a form of unhealthy entanglement, even though the lookup is guaranteed to work synchronously?

Make controller available to serialize/deserialize QPs

I don't know if it is possible, but it would be incredibly useful for the serialize and deserialize functions to have access to the object the query params are mixed into. For example, consider a query param that represents an array index. The deserialize function should be able to sanity check that index to make sure it's in range.

const myQueryParams = new QueryParams({
  foo: {
    deserialize(value) {
      const length = this.get('bar.length'); // TypeError: this.get is not a function
      // ..
    }
  }
});

export default Ember.Controller.extend(myQueryParams.Mixin, {
  bar: []
});

ember-fastboot support

Hello,

I have found this add-on very interesting - great job :) However, I wanted to ask If you are planning any support of ember-fastboot. Server-side prerendering is a must have for many apps, so giving it up completely is impossible for me.

Non-blocking UI is a good thing, but fetching all data in controller actually makes ember-fastboot impossible to work (as it renders blank/skeleton page to web crawlers).

What do you think @offirgolan? :)

Deprecation warning `deprecated-run-loop-and-computed-dot-access` blocking Ember 4 upgrade

Using the add-on in an app is producing the below deprecation warning. I believe this deprecation blocks the ability to upgrade to Ember 4.

DEPRECATION: Using `run.schedule` has been deprecated. Instead, import the value directly from @ember/runloop:

  import { schedule } from '@ember/runloop';

These usages may be caused by an outdated ember-cli-babel dependency. Usages of the Ember Global may be caused by an outdated ember-cli-babel dependency. The following steps may help:
...

Not sure if there are other instances of deprecated usage, but this particular warning is traced to:

run.schedule('afterRender', this, () => {

Also noticed this usage:

return new Promise(resolve => run.later(resolve, 1000));

Preventing a query param change

What do you think about adding a way to prevent a query param from changing? In a use case where you might bind a tab-like interface to a query param, with the active tab in the URL. With a normal route-driven UI, you could hook into willTransition to prevent a transition, but there's no way to have similar behavior with Parachute. Supporting refreshModel would solve this specific use case, but I know that that's not going to be supported.

I'm imagining something like queryParamsWillChange that could return false to reject the change

Provide a way to get serialized QPs from the controller

Hi,

First of all, thanks for creating and maintaining this awesome addon! ๐Ÿ‘

It would be awesome if you could provide a way to access the serialized query params from the controller. This way I wouldn't have to serialize them manually when I fetch the data..

I'd be glad to help with this, just let me know if you think it is a good idea.

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.