#Extend location API on Ember 5

1 messages · Page 1 of 1 (latest)

rancid hamlet
#

I've used the gist at https://gist.github.com/evoactivity/39fd9061a1a560b744112b6812294031 converted directly to javascript to implement eager url update. I had to slightly update it, in this case I made it a new implementation type and changed the implementation type in config/environment.js.

This appeared to be working, but I realised deep links now completely fail to load with an error similar to Cannot GET /colonisation/results/DF140094-28C5-11F1-A5FF-8A10F0982BED. If start at the root url and then navigate, it works fine. I've tried adding debugging statements into the constructor or other places, but nothing comes up in the console.

I did look for other (nicer) ways to update the URL eagerly (ideally I only want this to happen in a couple of places, but was willing for it to happen everywhere). Any ideas what's going on or how I may better implement eager URL updating?

sweet walrus
#

Your server needs to route all requests to the index.html

rancid hamlet
#

it does in production, this is in the dev server (ember serve), it's only when I have this location API put in that it doesn't work. If I change locationType in config/environment.js back tohistory it works fine

sweet walrus
#

The Cannot GET … is not something the frontend would throw.

What ember version?
Are you using ember-cli or vite dev server?

rancid hamlet
#

ember-cli and ember 5

sweet walrus
#

Can you show me the changes you made to environment.js?

rancid hamlet
#

literally only that change locationType: 'history', // eager-history-location works locationType: 'eager-history-location', doesn't

#

and I have a that gist converted to js and placed at app/locations/eager-history-location.js

#

and when I navigate from the root of the app, it seems to work correctly, and eager updates the url whilst the route model is loading

#

just deep links look like 404's

sweet walrus
#

Where do you see the cannot get error? In the cli?

rancid hamlet
#

in the app in the browser

sweet walrus
#

Your app loads but not the route? So is a catch all/404 route displaying that error?

rancid hamlet
#

I'm not sure, with that locationType set to history, if I to a non existant route I get a blank page (no 404 page set), and an unrecognisedURL error in the console

#

with the custom locationType any URL other than the route URL generates a text error in the browser and nothing in the browser

#

If I go to the root URL with the custom locationType. The app loads, and I can navigate fine to whichever page I want (and the URL updates, eagerly). refresh the page and it instantly errors. I don't get anything in the console, including if I put a console.log in the constructor of that eager-history-location.js

sweet walrus
#

Can you compare the network tab in dev tools loading the root URL vs a deep link

rancid hamlet
#

yeah gimme a second, bit annoying changing anything in config/environment.js as I have to kill the server and start it again for the changes to take effect (as opposed to regular code)

sweet walrus
#

I’m going to try to setup a reproduction here (unless you can share one)

rancid hamlet
#

Part of the problem here is the sheer size of the app (it's been around a long time), it's possible it's something else conflicting as well, though over the years I have massaged a lot of the less ember like behaviour to be mostly ember like

sweet walrus
#

I’ve been using that custom location across many ember versions and apps since I posted it. Never experienced anything like what you’re seeing 🥲

rancid hamlet
#

it's possible the conversion from typescript to javascript broke something

sweet walrus
#

Can you share your js version?

rancid hamlet
#
ember-cli: 5.9.0
node: 22.12.0
os: linux x64```
#

So, in this case with the custom location API, the network console errors in the very first request, so there are 2 (favicon and the base request)

#

it almost feels like this is a dev server issue no longer responding to all urls

#

whereas with the standard history locationType it has the reloading dev container ember-cli-live-reload.js in the browser, then a bunch of other requests before it 404's after a dozen or so requests

#

There's some debugging statements in there but other than that it's just a simple typescript to javascript conversion

sweet walrus
#

elite-dangerous-gui
Now I'm intrigued

#

what ya building?

rancid hamlet
sweet walrus
#

nice

#

so, I have a reproduction

#

going to do some poking to figure it out

rancid hamlet
#

Specifically, right now, I'm converting a set of very old structures whereby the plotter results pages do nothing in the model other than set some stuff, then in setupcontroller they continually poll my backend to get the job results

#

and then manually set some fields in the controller, to fetching all that data in the model (and polling for it) and using the loading substate

#

This works fine, but the old way the results url (which has the job identifier) was eagerly loaded, so if it takes a minute to load you can just refresh or whatever. Using the model it doesn't which isn't ideal, hence wanting the eager URL update

#

I have another problem with the loading substate but one issue at a time methinks (That problem is almost certainly a my understanding issue)

sweet walrus
#

ok, so I don't think the actual code of the custom location is to blame, I can give it any location name (ie one that doesn't exist) and I get the same error.

rancid hamlet
#

I wasn't sure what to do with the first file in your link, so I didn't create it (I suspected it was only useful for typescript)

#

I don't know if that's related

#

Much appreciate the help btw

sweet walrus
sweet walrus
rancid hamlet
#

ok, wow, I don't think I'd have ever found that

#

That's working now, thanks

#

I don't want to cloud this thread with an unrelated question, so it's hopefully useful for others, should I just create another thread or use the #help channel (I'm not really sure of the difference)?

sweet walrus
#

The difference is vibes lol

#

start a new thread

#

btw, if you don't want the trailing slash for your urls you can simply remove this line

url = url.replace(/\/?$/, '/');
rancid hamlet
#

Yeah I thought that was part of it

#

That's why I added the debugging in there as I thought that may have been the cause

#

I can just remove the whole formatURL function right?

sweet walrus
#

I think so yeah, been a while since I fought with the router 😄

rancid hamlet
#

yep, that's all working now, thanks (and the formatURL function is not required if you don't need the trailing slash. I suspect there are a few other pieces I could prune but I'll respond once I've sorted my other issue out and have a chance to experiment)

rancid hamlet
#

@sweet walrus I think I have come across at least one (possibly 2, but I'm seeing multiple errors and it's probably worthwhile resolving them one at a time in case they're related) more issues with the Location API stuff.
I have the following 2 route structures: this.route( 'tourist/search', { path: '/tourist/search/:search_guid' }, function () { this.route('results', { path: '/results/:guid' }); }, ); this.route('tourist', function () { this.route('results', { path: '/results/:guid' }); });When I'm, on /tourist/search/[guid]/results/[guid]?[params] I'm getting a javascript errorUncaught TypeError: can't access property "shouldSupersede", newHandlerInfo is undefined applyToHandlers http://server:4200/assets/vendor.js:56163 applyToState http://server:4200/assets/vendor.js:56112 generate http://server:4200/assets/vendor.js:56975 generate http://server:4200/assets/vendor.js:35714 urlFor http://server:4200/assets/vendor.js:34910 determineUrlForDestinationRoute http://server:4200/assets/elite-dangerous-gui.js:15841 EagerHistoryLocation http://server:4200/assets/elite-dangerous-gui.js:15778

#

That determineUrl line I stuck some debugging in to output the transition just before it and the one which appears to error is ``` routeWillChangeHandler = (transition) => {
if (!this.hasRouteInfoForAnalysis(transition)) {
return;
}
if (LOG_TRANSITIONS)
debug(${LOG_TAG}: routeWillChange -> ${transition.to.name});
this.transitionQueue.push(transition);

console.log(transition);
const newUrl = this.determineUrlForDestinationRoute(transition);
if (newUrl) {
  if (LOG_TRANSITIONS)
    debug(`${LOG_TAG}: shouldUpdateCallback -> ${newUrl}`);
  this.shouldUpdateURLCallbacks.forEach((cb) => {
    cb(newUrl);
  });
}

};``` The transition output just before the supersede error had an attribute withtargetName: "application_loading"

sweet walrus
#

pretty sure you can't name a route with a slash

rancid hamlet
#

What do you mean?

sweet walrus
#

'tourist/search' -> 'tourist-search'

#

'tourist/search' would translate to a route structure of

this.route('tourist', function () {
  this.route('search');
});
rancid hamlet
#

Oh, right, I think I tried a bunch of options (it's just a name of course) but I wanted the actual directory structure to appear as route/tourist/search but not come under the /tourist base route

#

They're related but distinct functions, so from a URL perspective it makes sense to nest for user perspective but not from a route perspective

sweet walrus
#

so in my repro

this loads fine

this.route(
  'tourist-search',
  { path: '/tourist/search/:search_guid' },
  function () {
    this.route('results', { path: '/results/:guid' });
  },
);

this does not (but not with the error you see)

this.route(
  'tourist/search',
  { path: '/tourist/search/:search_guid' },
  function () {
    this.route('results', { path: '/results/:guid' });
  },
);
rancid hamlet
#

I guess it doesn't matter if I have it as tourist-search/ in controllers/routes/templates

sweet walrus
#

So whatever is triggering the error isn't related to the route structure I don't think

#

oh, when I force the application loading template to render then I see

#

I see a slightly more useful error message

 Uncaught Error: You must provide param `search_guid` to `generate`.
    getParam route-recognizer.js:140
    1 route-recognizer.js:186
    generate$1 route-recognizer.js:547
    generate router_js.js:1839
    Ember 2
    determineUrlForDestinationRoute eager-history-location.js:106
    EagerHistoryLocation eager-history-location.js:43
    ... more stuff
rancid hamlet
#

I've shuffled those routes around anyway, as it does make more sense (It always felt like a hack and they don't need to be under the same code structure, just the same path)

sweet walrus
#

not sure how I've missed this for so long

#

silly bug

#

in this function

rancid hamlet
#

I wonder if that's related to my seperate issue, but thanks

sweet walrus
#

findAllRouteParamsForUrlLookup

#

change the if (route) to while (route)

rancid hamlet
#

Ok, that works, thanks. I'll just look quckly into another error I'm seeing whereby the model for tourist-search isn't set when I'm loading /tourist/search/F890BAF8-297E-11F1-A5FF-8A10F0982BED/results/D51B154E-2980-11F1-A5FF-8A10F0982BED?loop=0&range=60&source=Sol. (The only thing that the route model for /tourist/search does is stick the search_guid param into the model). I feel like this is a me bug

#

Yep, it was indeed a me issue. OK let me confirm whether the previous issue I saw is still happening

#

The other issue I saw is still hapenning, and this one is weird but feels like an off by one or an ordering bug somewhere. I have a route /systems/search/F890BAF8-297E-11F1-A5FF-8A10F0982BED/1 which is transitioned to fine from /systems. However, if I reload /systems/search/F890BAF8-297E-11F1-A5FF-8A10F0982BED/1 then the URL changes to /systems/search/1/16C191AE-29DA-11F1-AC13-ADB176679F3C (the 2 dynamic segments are reversed). I'm looking to see if this is something I have backwards somewhere but I'm pretty sure this worked fine before the history change.
The route definition for that route is:

    this.route('search', { path: '/search/:guid/:page' });
  });```
#

Yep, I can confirm that removing the eager url loading doesn't experience the issue

#

Aha, I think it's the finalParams.reverse(); in findAllRouteParamsForUrlLookup. I'm not sure that's needed

#

OK, it is required, but the bug a little more nuanced. You iterate through each route backwards and enumerate the parameters for it forwards. Lets say we have a parent route with parameter foo, and a child route with parameters bar and baz. You'd process child route first and end up with [bar, baz], then you'd process parent and end up with [bar, baz, foo]. You then reverse that and end up with [foo, baz, bar]. What you need to do is reverse each routes parameters before appending them to the list, then reverse the whole thing before returning (Actually not 100% sure about this, and I have a suspicion that this is an Object.keys ordering thing). The issue is definitely in the findAllRouteParamsForUrlLookup function thoughEdit: Still not quite the issue, it's close though and it's certainly in the findAllRouteParamsForUrlLookup function

#

So yeah, the final param ordering is determined by the Object.keys call in Object.keys(obj).forEach((key) => {

#

I've temporarily converted it to for (const obj of params) { for (const key in obj) { if (typeof obj[key] !== 'undefined') { finalParams.push(obj[key]); } } } which does the same thing but is easier to read

sweet walrus
#

can you try this?

findAllRouteParamsForUrlLookup(transition) {
  let route = transition.to;
  const finalParams = [];

  while (route) {
    const params = Object.values(route.params);
    finalParams.unshift(...params);
    route = route.parent;
  }

  return finalParams;
}

it's much simpler.

#

I should set this up as a proper repo with tests, not just a gist

rancid hamlet
#

Can do, that's simpler than my version ``` findAllRouteParamsForUrlLookup(transition) {
let route = transition.to;
const params = [];
const finalParams = [];
while (route) {
const keys = Object.keys(route.params);
if (keys.length > 0) {
params.push(route.params);
}
console.log('route.params', Object.keys(route.params));
route = route.parent;
}
for (const obj of params) {
const keys = Object.keys(obj).reverse();
for (const key of keys) {
if (typeof obj[key] !== 'undefined') {
finalParams.push(obj[key]);
}
}
}

finalParams.reverse();
return finalParams;

}```

#

Yep, that all works

#

Once again, I really appreciate all the help. I'm not sure how many other people are using that gist (do you get views stats anywhere?), I guess you could turn it into an addon and that would give you a better idea of how many people are using it based on downloads.

sweet walrus
#

I think this loop might be safer as we don't rely on the object being in the correct order, where as paramNames should be guaranteed to be the correct order.

while (route) {
  const params = [];
  for (const name of route.paramNames) {
    params.push(route.params[name]);
  }
  finalParams.unshift(...params);
  route = route.parent;
}

But yeah, I think I'll change this into an addon so I can actually test it, I know a few people are using it but have no hard stats.