Conversation
There was a problem hiding this comment.
Having the name is great but do you think it would be worth giving the instance or the class as well? I would imagine you could use ember with something that allows you to specify "query fragments" (Falcor) in your routes and potentially components, if you have a way of collecting those fragments recursively.
For example you could imagine that in node you could do something like this and prefetch all the data for the initial route:
let indexHTML = fs.readFileSync(__dirname+'/index.html').toString();
const dataRegex = /%DATA%/;
const buildQuery = (handler, query = []) => {
query = query.concat(handler.instance.collectQuery());
if (handler.parent) {
buildQuery(handler.parent, query)
}
return callUpstreamService(query)
}
app.collectQueryFor('/profile/123').then(buildQuery).then(data => {
let indexHTML = indexHTML.replace(dataRegex, JSON.stringify(data));
res.writeHead(200, {
'Content-Length': indexHTML.length,
'Content-Type': 'text/html'
});
res.write(indexHTML);
res.end();
})This would allow you to extract the data requirements without a lot of overhead. If not, this can be proved out in user land as you should be able to lookup the route instances from the info.
There was a problem hiding this comment.
I don't want to encourage people to do anything remotely stateful with Route instances. I think it's dubious that they are instances at all.
Your overall scenario is an important one, and it comes down to enabling routes to resolve their promises in parallel instead of in series. That would let your data layer be Falcor or GraphQL-like and automagically batch and combine the requests generated in each of the model hooks. There would be no need to do a new top-down query building step -- the hooks we have are already where routes tell us what data they need.
That whole feature is probably a different RFC. I think it would (1) cause routes to be non-blocking by default (meaning their children's model hooks begin to run even before their own model hook resolves), (2) make modelFor return a promise, so that you can re-introduce explicit dependencies when you need them, and (3) provide an optional way to declare a route as blocking, which can still be necessary if you're going to do data-dependent redirections.
There was a problem hiding this comment.
👍 on routes not being instances. Making them stateful prevents all sorts of cleverness for solving exactly the problem Chad brings up. The separate RFC is #97.
There was a problem hiding this comment.
I think they are fine being internally/privately stateful, but not good being singletons.
|
A few random thoughts/notes:
|
|
@ef4 we have a similar service now. There're two things that we're using that could be part of the public interface. It's easy to build those on top of the current public interface, but something to consider. Our RoutingService is also Ember.Evented, so a consumer can subscribe to the |
text/0000-router-service.md
Outdated
There was a problem hiding this comment.
Dropping didTransition and willTransition is likely going to be very painful. Basically anybody who ever implemented analytics used those hooks. Some people even (read, me) stored stuff on the transition object and pulled it back out later.
There was a problem hiding this comment.
I agree. Maybe we need to provide a new event that doesn't expose private members.
It also seems better than relying on an observer.
—
Sent from Mailbox
On Tue, Oct 6, 2015 at 2:37 PM, Nathan Hammond notifications@github.com
wrote:
+A
url-forhelper can be implemented almost identically to theis-activeexample above.
+
+
+### New Properties
+
+currentRoute: an observable property. It is guaranteed to change whenever a route transition happens (even when that transition only changes parameters and doesn't change the active route). You should consider its value deeply immutable -- we will replace the whole structure whenever it changes. The value ofcurrentRouteis aRouteInforepresenting the current leaf route.RouteInfois described below.
+
+currentRouteName: a convenient alias forcurrentRoute.name.
+
+currentURL: provides the serialized string representingcurrentRoute.
+
+
+### Deprecation
+
+I propose deprecating the publicly extensiblewillTransitionanddidTransitionhooks. They are redundant with an observablecurrentRoute, and the arguments they receive leak internal implemetation from router.js.Dropping
didTransitionandwillTransitionis likely going to be very painful. Basically anybody who ever implemented analytics used those hooks. Some people even (read, me) stored stuff on the transition object and pulled it back out later.Reply to this email directly or view it on GitHub:
https://github.com/emberjs/rfcs/pull/95/files#r41325988
There was a problem hiding this comment.
willTransition is especially helpful for globally preventing a route based on some global state. Say for example pending file uploads. Something the "routable component" might not be aware of.
There was a problem hiding this comment.
@ef4 I think we likely want both evented and observable here. It seems like different use-cases would prefer to "subscribe" in different ways.
There was a problem hiding this comment.
To check would the proposed observable/event be on the router service or the private router?
Seems like an interesting thought to have components subscribe to beforeTransition or whatever the event would be and could interject their thoughts.
The first thing that comes to mind is a user-form component within a users.create route, while the route does not know the user-form's state until submission (DDAU) the form could listen for the transition and check to see if any data could be lost and alert/confirm, etc.
There was a problem hiding this comment.
@ef4 By deprecating the willTransition and didTransition hooks, do you mean that the willTransition and didTransition events would still be available (or something akin to them at least)? Would this be the time/place to request a changeTransition event (name up for debate) that would be triggered for transitions initiated between willTransition and didTransition?
|
Overall, I really like this idea! I think a routing service is very helpful an will make my previous apps use less private APIs. I think the only downside is consumers relying on this service to not handle routing concerns in the |
text/0000-router-service.md
Outdated
There was a problem hiding this comment.
I think it might be useful to also introduce more APIs here to get closer to parody with the underlying browser API. For instance it's currently not very clear how to go back a transition. Solving this in user space normally requires you to save off the previous transition to create an adhoc linked list across your app or create a service to keep track of this, which may be subject to getting out of sync. Things get more leaky when you are in a node environment and now need to guard against window references because the framework didn't provide a good way of handling those cases with a good abstraction.
There was a problem hiding this comment.
I suspect this would be a tangental RFC
|
I got linked here in slack, that this RFC might be curious how people are using this.router.router.currentHandlerInfos.forEach(route => {
var routeSegments = routesWithSegments[route.name];
// using route._names instead of route.params because
// the latter's order is not guaranteed
route._names.forEach(param => {
var paramsSource = routeSegments && routeSegments[param]
? routeSegments : route.params;
models.push(paramsSource[param]);
});
}); |
There was a problem hiding this comment.
I'm uncertain about Enumerable future, I believe we should transition to iterable, as that isn't something we have done yet, i suspect we should leave out the enumerable part here. We can always expose iterability etc. at a later date.
There was a problem hiding this comment.
I believe with this linked list you can get what router.router.currentHandlerInfos used to get you. I also use route._names as an ordered version of route.params mentioned here. Is this something that is covered in the RFC? Is the new RouteInfo.params ordered?
|
@ef4 -- By deprecating the Also, would this be the time/place to request a |
|
I added some comments a couple weeks ago. I'm unfamiliar with final comment period. Are those comments now blocking the merge, or will the merge proceed with unaddressed comments? |
|
@kellyselden I missed your open question about param ordering.
This seems like a simple and uncontroversial bit of detail to add, thanks for pointing it out. |
|
After taking feedback and hearing a lot of uncertainty around general-purpose dynamic variables, I'm going to table them in favor of |
|
Reviewing the original implementation of the I'd like to add a rider to this RFC which allows the subexpression to receive objects. It becomes incredibly annoying in the scenario where we don't investigate the final state to prune values from the query string inside of Handlebars files and makes it more difficult to accomplish standardization of nested query param behavior since everything must still be key/value pairs. Current State: Proposed Approach: Ember.controller.extend({
someQPObject: {
key1: 'value1',
key2: 'value2'
}
});This would make it sane to adopt my proposed nested query params specification as a unifying pattern across all of our layers: Sketch to update current implementation: positional.forEach(function(positionalArg) { if (typeof positionalArg !== "object") { assert(); } });
var result = Ember.assign({}, ...positional, named.value());I'm also amenable to disallowing mixed usage and only allowing a single positional param: if (positional.length === 1 && typeof positional === "object" && Object.keys(named).length === 0) { return positional[0]; }
if (positional.length === 0) { return Ember.assign({}, named.value()); } |
|
@nathanhammond that all sounds good but this RFC is already pretty big and thoroughly argued, and I was hoping not to have to restart final comment period (since my latest updates were all just incorporating feedback from the discussion so far and AFAIK there are no controversial questions outstanding). Done separately, the query params change seems like a short and sweet RFC that can sail through. |
|
|
||
| - default values will not be stripped from generated URLs. For example, `urlFor('my-route', { sortBy: 'title' })` will always include `?sortBy=title`, whether or not `title` is the default value of `sortBy`. | ||
|
|
||
| - to explicitly unset a query parameter, you can pass the symbol `Ember.DEFAULT_VALUE` as its value. For example, `transitionTo('my-route', { sortBy: Ember.DEFAULT_VALUE })` will result in a URL that does not contain any `?sortBy=`. |
There was a problem hiding this comment.
Rereading this now with a bit more context and explaining it for future travelers (read: me, six months from now).
This is to ensure that our default behavior of "hydrating unsupplied query params" continues to work. Without this we effectively end up doing Object.assign({}, unsupplied, queryParams); which would make queryParams only additive, which is wrong. We can't use values other than this symbol as it is possible that it may be valid user input.
- I'm concerned about the name as
DEFAULT_VALUEimplies more than we likely mean to convey. I'd nominateUNSET_VALUE,UNBIND_VALUE, orDELETE_VALUEinstead. - Can we stash this somewhere not on the
Emberglobal?
There was a problem hiding this comment.
Also,
- This approach for unsetting does not completely support nested (structured) array query params. It's not easy to model
unsetbehavior in this scenario:{ foo: [ "a", "b", "c"] }=>foo[]=a&foo[]=b&foo[]=c. That should instead do some sort of slice, so maybe some sort of:
{ foo: SLICE(SLICE(ORIGINAL,0,1),0,1) } I believe that this needs to support nesting to be complete.
(This maps to my forthcoming serialization unification proposal.)
There was a problem hiding this comment.
This gets worse if you nest arrays of objects and want to UNSET a property on a nested value.
{
characters: [
{ thing: "one", hat: false },
{ thing: "two", hat: false },
{ thing: "cat", hat: true }
]
}
Indices get included because otherwise the array is ambiguous.
characters[0][thing]=one&characters[0][hat]=0&
characters[1][thing]=two&characters[1][hat]=0&
characters[2][thing]=cat&characters[2][hat]=1
If I wish to unset characters[2][hat] and eliminate characters[0][thing]=one (never liked that one anyway) I'm unsure it's possible to do gracefully with this set of proposed tools.
There was a problem hiding this comment.
I think structured query params are additive API, so unless there is something in this proposal that forecloses on your design, I think we don't need to completely solve it here.
There was a problem hiding this comment.
They're additive in that they're new but considering our current state has severe issues I want to ensure that we feel good about our future direction.
For the basic scenario and only-keyed-objects I think we can get away with DELETE_VALUE. To support arrays it almost has to become a Lisp where you specify the transforms. To do that gracefully I feel like we would need to adopt:
push,popshift,unshiftslicesplice- ...
And that's beyond what I would want to support as it would come with a parser and all sorts of other annoying problems. Throwing away that proposal, I think it's bad.
We could always stamp something as "pre-merged" and expose an API to get the unsupplied values. It'll make structured query param merging palatable for advanced users, doesn't impact this simple scenario, doesn't add a tremendous amount of complexity and another Lisp-y microsyntax.
For bonus points I don't think that needs to be specified at this time and seems like a direction where this doesn't paint us into any corners.
/FIN
|
I would like to thank everyone involved for shaping this RFC into its current state through detailed and constructive feedback. This service has been a long time coming, and we hope that transitioning from the current intimate API is both swift and easy. On top of that, it will unlock new capabilities and optimization opportunities. In this Final Comment Period only one new concern was raised, and it was considered that it is an additive concern that can be addressed by a follow up RFC to the present one. So all that's left to do is to merge it. 🎉 One final thank you to @ef4 for sticking with it for the last year and change! |
|
Would it make sense to consider cc: @kazuzenefits |
|
@MiguelMadero - I think it seems fine, but it should be done as a follow-up RFC. It should be small enough and be able to focus on just this one method... |
|
Thanks @rwjblue I can throw something together. I just wanted to check first. |
|
I think would be handy if we can expose methods like |
|
Not sure if this is the right place to add comments for continuing work on the router service or not (considering this was merged but doesn't represent the entirety of what was described in https://github.com/emberjs/rfcs/blob/master/text/0095-router-service.md). Anyway my 2c: What I would like to see is the addition of something like I guess alternatively |
…4199) * [ember__routing] Add types from Router Service RFC emberjs/rfcs#95 * [ember__routing] Add `RouteInfo` tests Also includes tweaks to `RouteInfo` definition and docs
…4199) * [ember__routing] Add types from Router Service RFC emberjs/rfcs#95 * [ember__routing] Add `RouteInfo` tests Also includes tweaks to `RouteInfo` definition and docs
Rendered