How do you target ember-cli-mirage from a regular AJAX request using ember-concurrency?

I think the spot that you have the debugger statement is not what you want. That’s just verifying that mirage is booting. I think you want the debugger in the route handler itself, what would be line 17 in the screenshot (let { term } = queryParams;).

I also noticed that in the quick search code you’re using the query param “q” but in the mirage handler you’re using “term” so you probably want to change this line:

  let url = `/api?q=${term}`;

to

  let url = `/api?term=${term}`;

Now we’re cooking with gas! Watching the Promises in Ember Inspector shows concurrency doing exactly what it’s configured to do. It’s still broken but it’s also damn exciting for me to look at this and recognize this is a series of concurrency tasks firing right on schedule… until the rejection on finally. I know it’s right on schedule because I was entering “b-e-a-n” into the input. Fantastic since I didn’t even get a handle on the difference between objects and methods or the this keyword (among many other things) until last night!

36

This is where my excitement gets the big 'ol cancelAll because after pushing the rejection value to Console I’m told:

"Error: Mirage: Your Ember app tried to GET '/api?term=bean', but there was no route defined to handle this request. Define a route that matches this path in your mirage/config.js file. Did you forget to add your namespace?"

But we did not forget our namespace.

// mirage/config.js

/* eslint-disable */

export default function() {
  this.namespace = "api";
this.get("/terms", function({ terms }, { queryParams }) {
  debugger;
  let { term } = queryParams;
  if (!term) {
    return terms.all();
  } else {
    term = term.toLowerCase();
    return terms.findBy({ term });
  }
});
  this.get("/terms/:id");
}

Moreover, the results are the same whether you use:

let url = `/api?q=${term}`;

or

let url = `/api?term=${term}`;

Long shot, but could this be a result of not customizing any of the code to match the rest of the app?

Ah whoops that was a lazy miss on my part, it’s because the EC code is just making a call to /api and not /api/terms.

let url = `/api?term=${term}`;

That will just hit the servers /api endpoint with query param term which isn’t what you want.

In the mirage config your handler endpoint is {namespace}/terms:

  this.namespace = "api";
  this.get("/terms", function({ terms }, { queryParams }) {

So change the line to be:

let url = `/api/terms?term=${term}`;

If it’s not one thing, it’s another.

59

Error: json.items is undefined

And the Console.

jQuery.Deferred exception: json.items is undefined _default<.searchRepo<@http://localhost:4200/assets/glossary-app.js:5567:7
_resumeGenerator@http://localhost:4200/assets/vendor.js:111031:46
_handleResolvedContinueValue@http://localhost:4200/assets/vendor.js:111187:12
_proceed@http://localhost:4200/assets/vendor.js:111151:14
invoke@http://localhost:4200/assets/vendor.js:27727:24
flush@http://localhost:4200/assets/vendor.js:27637:25
flush@http://localhost:4200/assets/vendor.js:27804:31
_end@http://localhost:4200/assets/vendor.js:28271:42
end@http://localhost:4200/assets/vendor.js:28011:18
_run@http://localhost:4200/assets/vendor.js:28316:26
_join@http://localhost:4200/assets/vendor.js:28292:29
join@http://localhost:4200/assets/vendor.js:28065:25
join@http://localhost:4200/assets/vendor.js:16746:28
_proceedSoon@http://localhost:4200/assets/vendor.js:111109:19
proceed@http://localhost:4200/assets/vendor.js:111124:12
handleYieldedUnknownThenable/<@http://localhost:4200/assets/vendor.js:110533:20
mightThrow@http://localhost:4200/assets/vendor.js:3898:29
resolve/</process<@http://localhost:4200/assets/vendor.js:3966:12
 undefined

Searched all of @machty’s repos and the only variation on:

let json = yield this.get("getJSON").perform(url);

is

let json = yield $.getJSON(url);

This produces the same error.

Probably from the second of these two lines:

  let json = yield this.get("getJSON").perform(url);
  return json.items.slice(0, 10);

Try putting a breakpoint on that line to see what you’re getting in “json”. My guess is that it looks like a JSONAPI payload, so you’d want json.data.slice(0,10) but that’s just a guess.

Interesting. Looks like it’s successful with the breakpoint.

10

Breaks when you remove it though.

json.data.slice seems like a good guess… at least it throws a different error?

jQuery.Deferred exception: json.data.slice is not a function _default<.searchRepo<@http://localhost:4200/assets/glossary-app.js:5567:24
_resumeGenerator@http://localhost:4200/assets/vendor.js:111031:46
_handleResolvedContinueValue@http://localhost:4200/assets/vendor.js:111187:12
_proceed@http://localhost:4200/assets/vendor.js:111151:14
invoke@http://localhost:4200/assets/vendor.js:27727:24
flush@http://localhost:4200/assets/vendor.js:27637:25
flush@http://localhost:4200/assets/vendor.js:27804:31
_end@http://localhost:4200/assets/vendor.js:28271:42
end@http://localhost:4200/assets/vendor.js:28011:18
_run@http://localhost:4200/assets/vendor.js:28316:26
_join@http://localhost:4200/assets/vendor.js:28292:29
join@http://localhost:4200/assets/vendor.js:28065:25
join@http://localhost:4200/assets/vendor.js:16746:28
_proceedSoon@http://localhost:4200/assets/vendor.js:111109:19
proceed@http://localhost:4200/assets/vendor.js:111124:12
handleYieldedUnknownThenable/<@http://localhost:4200/assets/vendor.js:110533:20
mightThrow@http://localhost:4200/assets/vendor.js:3898:29
resolve/</process<@http://localhost:4200/assets/vendor.js:3966:12
 undefined

Maybe that:

finally {
    xhr.abort();
  }

should go?

So the problem is that you’re trying to slice a non-array. Now you have to decide how you want this to behave. The example from ember-concurrency is doing a slice to trim the results to 10, but your backend is only returning one record (or all), not an array. If you want it to return anything that partially matches the query string you’ll need to modify your mirage route handler.

Then you have to ask yourself if you want to be using ember data for this or just raw AJAX. If the latter then you’ll have to parse the attributes out yourself OR push the json into the store when you’re ready to use it in the route.

So at this point it’s sort of a design/product question than a technical one.

I’ve been hammering away at this for 11 months and made the decision to go with Ember Data early on for several reasons so definitely want to stick it out. It doesn’t look like much with 27 definitions in the store now, but the full json has over 6,000 terms.

I started at “What the heck is command line??” but was determined to figure out how to interact with the json by calling it from an S3 bucket. Oil and gas people don’t trust the cloud, but they trust AWS and if I want to sell this I knew I had to slay that dragon. Was able to get all configured, learn TailwindCSS, clone SLB’s glossary look and feel, wire up the basic components of the app, and basically learn Ember the hardest way possible. Which is why I kept running headlong into brick walls trying to work with json from AWS.

Was nearly ready to give up hope until a couple weeks ago when I decided to start over, chop the json down to the bare minimum, and try to power through my json/Ember Data problems/learning curve using Mirage. Figured if I could build-up the code to get a couple dozen terms, I could turn around and refactor it to get a couple/few/6,000+.

We live to fight another day.

Keep hope alive!

Thank you for attending my TED Talk.

Ok if you want to stick with Ember Data then your ember concurrency task should probably just use store.query instead of jquery.json. I don’t think there would be any issues with that anyway, I think the quicksearch exampe from EC was just demonstrating a non-Ember Data example. So your search task looks like:

searchRepo: task(function * (term) {
  if (isBlank(term)) { return []; }
  yield timeout(DEBOUNCE_MS);
  return yield this.store.query('term', { term });
}).restartable(),

You could also slice the result down if you wanted, the above will return all matches. And of course this is assuming that you want to return more than once match per query, which I think makes sense for a quicksearch. Next your mirage handler needs to return an array instead of a single result…

this.get("/terms", function({ terms }, { queryParams }) {
  debugger;
  let { term } = queryParams;
  let terms = terms.all();
  if (term) {
     const match = term.toLowerCase();
     terms = terms.filter(t => t.term.toLowerCase().includes(match));
  }
  return terms;
});

That should return any term models that have a ‘term’ that includes the query string submitted by the client.

When running a search.

stack: “Error: Ember Data Request GET api/terms returned a 500↵Payload (application/json)↵[object Object]↵ at ErrorClass.EmberError`

When navigating to localhost:4200/terms/bean

Error while processing route: terms Your Ember app tried to GET ‘api/terms/bean’, but there was no route defined to handle this request. Define a route that matches this path in your mirage/config.js file. Did you forget to add your namespace?

Hmmm can’t tell offhand what that would be without more code. Could you push your latest to github? Seems like you might have some code in your adapter that’s rewriting query URLs? That’s just a guess though…

Pushed.

Small issue in the mirage config and once that was fixed the application template wasn’t be displaying the right things in the dropdown. I got the dropdown list part working, here’s a git diff:

--- a/app/pods/application/template.hbs
+++ b/app/pods/application/template.hbs
@@ -14,8 +14,8 @@
     {{/if}}

     <ul>
-      {{#each searchRepo.lastSuccessful.value as |repo|}}
-        <li>{{repo.full_name}}</li>
+      {{#each searchRepo.lastSuccessful.value as |term|}}
+        <li>{{term.term}}</li>
       {{/each}}
     </ul>
   </div>
diff --git a/mirage/config.js b/mirage/config.js
index f2464d4..9c2d59c 100755
--- a/mirage/config.js
+++ b/mirage/config.js
@@ -1,12 +1,12 @@
 export default function() {
   this.namespace = "api";
-this.get("/terms", function({ terms }, { queryParams }) {
-
-  let { term } = queryParams;
-  if (term) {
-    const match = term.toLowerCase();
-    terms = terms.filter(t => t.term.toLowerCase().includes( match ));
-  }
-  return terms;
-});
+  this.get("/terms", function({ terms }, { queryParams }) {
+    let { term } = queryParams;
+    let results = terms.all();
+    if (term) {
+       const match = term.toLowerCase();
+       results = results.filter(t => t.term.toLowerCase().includes(match));
+    }
+    return results;
+  });
 }

Basically my original snipped a couple comments ago was overloading “terms” which caused an error so I changed it to “results”. And then a tweak to the template to match the models being returned.

This fixes the search, thank you!!

Unfortunately no longer able to navigate to individual words. Still getting “…but there was no route defined…” error posted in full above. Do I need to update the router.js?

Ah, so the route model hook is gone, and Ember has this convenient but confusing behavior where it guesses your model. So it’s making a request to findRecord('term', params.term_id). I’d recommend putting the model hook back (and by back I mean just return the same findRecord from the previous sentence) because explicit is always better.

It probably would have worked anyway but it looks like you may have removed the mirage shorthand for get('terms/:id') so mirage doesn’t know how to handle that findRecord request. You’ll need to put that back.

Nailed it! Added this.get(“terms/:id”); back. Search works and can navigate to individual terms. Man, I owe you BIG TIME!! Thank you!!

1 Like

What’s the right way to {{#link-to}} here? The most basic implementation seems like it would be:

      {{#each searchRepo.lastSuccessful.value as |term|}}
        {{#link-to "term.term" primaryTerm}}{{term.term}}{{/link-to}}<br>
      {{/each}}

But that and everything else I try throws:

WARNING: This link-to is in an inactive loading state because at least one of its parameters presently has a null/undefined value, or the provided route name is invalid.

I’d think it would be something like:

  {{#each searchRepo.lastSuccessful.value as |term|}}
    {{#link-to "term” term.id}}{{term.term}}{{/link-to}}<br>
  {{/each}}
1 Like

Got it!

{{#link-to "terms" term.id}}{{term.term}}{{/link-to}}<br>
1 Like