This is the latest in API thought. Is it any good?
ThingController = Ember.Controller.extend({
foo: "myDefaultValue",
bar: ['such', 'api'],
complicatedObj: { complex: 'thing' },
maverick: "imsoglobal",
// queryParams if all defaults:
// queryParams: ['foo', 'bar', 'maverick']
queryParams: {
// Register `foo` as a queryParam.
// Once the route associated with this controller
// has entered, `foo` will be bound to
// "thing[foo]=___" in the URL. If no one
// sets foo to some other value, the URL
// will have "thing[foo]=myDefaultValue".
foo: true,
// Register `bar` as a queryParam.
// Its default value is an array of strings,
// so it will be serializable in the URL as
// "thing[foo][]=such&thing[foo][]=api"
bar: true,
// complicatedObj: true...
// can't just enable defaults on complicatedObj;
// need (de)serializers.
// If (de)serializers weren't provided, helpful
// errors would throw.
complicatedObj: {
deserialize: function(urlStringValue) {
return { complicatedObj: "someString" + urlStringValue };
},
serialize: function(obj) {
// If complicatedObj were set to obj,
// this would cause the URL query param
// for complicatedObj to be "thing[complicatedObj]=omglol"
return "omglol";
}
},
// maverick overrides the key used in generating the URL
// query param, e.g. "maverick=imsoglobal" vs
// the default "thing[maverick]=imsoglobal".
// Note: this is not a solution for having multiple
// controllers binding to the same query params, this
// is just a way to letting you override the formatting
// default. Global query params should be on application
// route and other controllers can bind to the
// property on application controller.
maverick: {
key: 'maverick'
}
}
});
Alternatives to the above API exhibited extreme weakness when it came to serializing/deserializing, or providing default values for query params upon entering routes without awkwardly repeating this default at the route layer.
Ember.Route.extend({
// Schedules a "refresh" transition where by
// the route hierarchy doesn't change but
// all the model hooks are re-run, starting
// from this route. If a parent route calls
// refresh() immediately after this one does,
// the parent route will be the starting
// point for the refresh().
refresh: function() {},
actions: {
queryParamsDidChange: function(changedQueryParams) {
// A `queryParamsDidChange` action fires when
// the URL updates or a transition has occurred
// that updates query params. At the beginning
// of a link-to, transitionTo, or handleURL,
// if a change in query params is detected,
// a queryParamsDidChange will be fired from
// the pivot route upward, giving routes above
// the pivot route a chance to request a refresh(),
// which will cause them to be the pivot route for
// this transition. They could also transitionTo
// elsewhere at this point (though don't have
// a solid use case in mind for that).
// This is the default implementation of
// this action handler provided for each route.
// Conceivably you could override the handler
// and perform a more thorough/complicated
// check as to whether a refresh transition
// should occur.
var refreshForQueryParams = this.get('refreshForQueryParams');
refreshForQueryParams.forEach(function(qp) {
if (changedQueryParams.hasOwnProperty(qp)) {
// Schedule a refresh.
this.refresh();
}
// Bubble, so that parent refresh()'s override
// this one and cause the refresh to start
// at a higher point.
return true;
});
}
},
// This is the quickest/easiest way to set this route
// up to self-refresh when its query params change.
// If these weren't specified, changes to the query
// param wouldn't cause a transition/refresh at all
// and the effects of a changing QP would only
// impact the controller.
refreshForQueryParams: ['foo', /^bar/]
});
{{#link-to 'dest' foo=123}}
The above generates "/dest?dest[foo]=123" assuming that
the dest
property has been configured to be a QP
property on DestController
.
{{#link-to 'dest' parent:bar=123}}
If dest route has a parent route called 'parent',
and ParentController#bar
is a QP property, then
this will generate "/dest?parent[bar]=123".
link-to
is smart enough to look at the destination
route hierarchy and know what query params have been
set up on the controllers associated with that route
hierarchy (we're not supporting anything super fancy
like {{render}}
though).
The charset of a handlebars helper hash key is pretty restrictive; we wouldn't even be able to handle setting the prop on a controller whose module name had a dash in it.
Maybe this means we just get rid of the colon approach and enforce the rule that controllers in the same hierarchy can't reuse QP property names.
So the parent example would just become:
{{#link-to 'dest' bar=123}}
and generate the same url: "/dest?parent[bar]=123"
It's all in my brain and largely solidified, but will need to write it out at a later point because I am TIRED.
Here are a bunch of thoughts though:
- Default behavior: updating a query param causes a replaceState by default. This should probably be configurable somehow. How?
- The
queryParamsDidChange
action is how this no-transition-on-qp-changes behavior is overridden; if a route handles this action and callsthis.refresh()
, that causes what would have just been a controller property update to turn into a full on classic router transition whereby model hooks and the like are called. - The actually controller setting of QP properties
always happens at the end of the transition process,
rather than in
setupController
(though you'll have the ability to more eagerly set these properties insetupController
). The reason for this is that there are many different ways to update QPs (url changes, transitionTos, link-tos, property changes), and not all of these ways involve transitions, and it just seems like race condition hell to, for certain cases, set these properties in asetupController
hook and in other cases set the properties elsewhere. So we're gonna set all of the properties all at once, at the end of a transition.
Promises. For v1 at least.
Sorry to interrupt your mindflow, but these type of URLs "/dest?dest[foo]=123', '"/dest?parent[bar]=123" are so
against common practices and current API's support.
I'm sure, you understand that everyone uses, first of all, simpler URL structure, including github.
https://github.com/emberjs/ember.js/issues?labels=assert&page=2&state=open
And those type of URL should be supported by default.
I'm sure its even not my opinion only, if you would you be willing to ask, but may be I'm missing something...
Best regards, Roman.