#Ember front-end for Loopback 3 back-end
1 messages · Page 1 of 1 (latest)
Error log:
app/models/task.js:
import { attr } from '@ember-data/model';
export default class TaskModel extends Model {
@attr('string') title;
@attr('string') description;
@attr('string') status;
@attr('date') dueDate;
}
app/routes/tasks.js:
export default class TasksRoute extends Route {
model() {
console.log('Checking store:', this.store);
if (this.store) {
return this.store.findAll('task');
} else {
console.error('Store is undefined!');
return [];
}
}
}
app/adapters/application.js
export default class ApplicationAdapter extends JSONAPIAdapter {
namespace = 'api';
}
app/app.js:
import Resolver from 'ember-resolver';
import loadInitializers from 'ember-load-initializers';
import config from 'ember-task-manager/config/environment';
export default class App extends Application {
modulePrefix = config.modulePrefix;
podModulePrefix = config.podModulePrefix;
Resolver = Resolver;
}
loadInitializers(App, config.modulePrefix);
any additional information required will be provided, thanks in advance!
@proven granite can you show your tasks.hbs file?
I'm going to guess you're trying to access {{model.something}}, if so it should be {{@model.something}}
I got:
task-form.hbs:
<form {{on 'submit' this.saveTask}}>
<div class="form-group">
<label for="title">Title:</label>
<input type="text" id="title" required {{on 'input' (action (mut this.title))}} />
</div>
<div class="form-group">
<label for="description">Description:</label>
<textarea id="description" {{on 'input' (action (mut this.description))}}></textarea>
</div>
<div class="form-group">
<label for="status">Status:</label>
<select id="status" {{on 'input' (action (mut this.status))}}>
<option value="todo">To-Do</option>
<option value="in-progress">In Progress</option>
<option value="completed">Completed</option>
</select>
</div>
<div class="form-group">
<label for="dueDate">Due Date:</label>
<input type="date" id="dueDate" {{on 'input' (action (mut this.dueDate))}} />
</div>
<div class="button-group">
<button type="submit">Save</button>
<button type="button" {{on 'click' this.cancel}}>Cancel</button>
</div>
</form>
</div>
and task-list.hbs:
{{#each model as |task|}}
<li>{{task.title}}</li>
{{/each}}
</ul>```
change
{{#each model as |task|}}
to
{{#each @model as |task|}}
i still got the same error
but first
this is what my page looks like
after I add the @model it just become blank
ah, I see the store is not injected into your route.
You'll want something like this to inject the store
import Route from "@ember/routing/route";
import { service } from "@ember/service";
// this might be import { inject as service } from "@ember/service" if you're on an older version of ember
export default class BlogPostsIndexRoute extends Route {
@service store;
model() {
return this.store.findAll("posts");
}
}
wait lemme try, i tried to inject it, but it just messed up everything
but lemme try this one
hmmm
well its something new hahaha
the schema should be from the api no?
hmm, that's odd.
Can you share the devDependencies of your package.json?
here it is:
"@babel/core": "^7.23.6",
"@babel/eslint-parser": "^7.23.3",
"@babel/plugin-proposal-decorators": "^7.23.6",
"@ember/optional-features": "^2.0.0",
"@ember/string": "^3.1.1",
"@ember/test-helpers": "^3.2.1",
"@glimmer/component": "^1.1.2",
"@glimmer/tracking": "^1.1.2",
"broccoli-asset-rev": "^3.0.0",
"concurrently": "^8.2.2",
"ember-auto-import": "^2.7.0",
"ember-cli": "~5.5.0",
"ember-cli-app-version": "^6.0.1",
"ember-cli-babel": "8.2.0",
"ember-cli-clean-css": "^3.0.0",
"ember-cli-dependency-checker": "^3.3.2",
"ember-cli-htmlbars": "^6.3.0",
"ember-cli-inject-live-reload": "^2.1.0",
"ember-cli-sri": "^2.1.1",
"ember-cli-terser": "^4.0.2",
"ember-data": "~5.3.0",
"ember-fetch": "^8.1.2",
"ember-load-initializers": "^2.1.2",
"ember-modifier": "^4.1.0",
"ember-page-title": "^8.1.0",
"ember-qunit": "^8.0.2",
"ember-resolver": "^11.0.1",
"ember-source": "~5.5.0",
"ember-template-lint": "^5.13.0",
"ember-welcome-page": "^7.0.2",
"eslint": "^8.55.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-ember": "^11.11.1",
"eslint-plugin-n": "^16.4.0",
"eslint-plugin-prettier": "^5.0.1",
"eslint-plugin-qunit": "^8.0.1",
"loader.js": "^4.7.0",
"prettier": "^3.1.1",
"qunit": "^2.20.0",
"qunit-dom": "^2.0.0",
"stylelint": "^15.11.0",
"stylelint-config-standard": "^34.0.0",
"stylelint-prettier": "^4.1.0",
"tracked-built-ins": "^3.3.0",
"webpack": "^5.89.0"
},```
Do you have an application serializer file?
something like
// app/serializers/application.js
import JSONAPISerializer from '@ember-data/serializer/json-api';
export default class ApplicationSerializer extends JSONAPISerializer {
}
Yep, though I'm not 100% sure that is the issue, but if you're using JSON:API the app needs to know to use it.
lemme tinkering with it for a bit
I just learn this thing this morning, still need to get a grasp of it
Yeah ember data has a bit of a learning curve but once it's working and you start to understand it, it definitely makes dealing with your API a lot nicer than just using fetch.
(fetch is also a totally valid option though btw)
Quick question, in the network tab do you see the app making a the call to your tasks api?
and if you do can you share the JSON
Can you show me an example of the JSON returned from your API. I'm not familiar with Loopback, but I'm not sure it uses JSON:API (it's a specification https://jsonapi.org/).
If it's just normal plain JSON you'll probably want to use the RESTAdapter and RESTSerializer instead of the JSONAPI variants.
parsed:
{
"title": "Updated Task",
"description": null,
"status": "to-do",
"dueDate": null,
"id": 1,
"createdAt": "2024-01-06T16:50:29.755Z",
"updatedAt": "2024-01-06T16:51:26.436Z"
}
]```
raw:
```[{"title":"Updated Task","description":null,"status":"to-do","dueDate":null,"id":1,"createdAt":"2024-01-06T16:50:29.755Z","updatedAt":"2024-01-06T16:51:26.436Z"}]```
its something like this
Ok, you'd have to implement your own serializer to be able to parse that correctly. But I still think you're running into another issue right now (since the app doesn't appear to be making the API call at all right now)
The ember-data store requires all JSON it receives to be normalized so it conforms with the JSON:API specification. But that might be jumping into the deep end if ember is all new to you today.
If you're just trying to learn ember today, I'd suggest just using fetch in your routes model() method and working with the data as a plain JS object so you can get a feel for ember itself.
If you want to dive into it though you'd setup a normalizeResponse method in your application serializer that receives the data from your API and transforms it so it matches the https://jsonapi.org specification.
The example json would be something like
{
"data": [
{
"type": "tasks"
"id": 1,
"attributes": {
"title": "Updated Task",
"description": null,
"status": "to-do",
"dueDate": null,
"createdAt": "2024-01-06T16:50:29.755Z",
"updatedAt": "2024-01-06T16:51:26.436Z"
}
}
]
}
@violet magnet do you know what might be causing the error no model was found for 'tasks' and no schema handles the type with how mado has setup their model? It seems ok to me.
yeah no worries, ember-data is undergoing quite a bit of transformation at the moment, so those docs are marked as legacy, but should give you the info you need to build a serializer.
I've pinged you in our ai chat room with an example serializer using the JSONSerializer.
thank you man!
@glad pine hey man
i found that i have tasks.hbs somehow
tasks.hbs:
<h2>Task List</h2>
{{task-list model=model}}
{{outlet}}
does this have anything to do with model?
it does, so task-list is a component right?
Modern convention would be to mark that up as <TaskList @model={{@model}} />
tasks.hbs will be your route template file
So to explain whats happening here
{{task-list model=model}}
the model you are passing to the component is a variable that doesn't exist basically. With modern ember (you're on 5.5.0 so it's modern) the model() method in the route file is passed to your template as @model so you would pass it to your component like
{{task-list model=@model}}
but in modern ember we have angle brackets for components
<TaskList @model={{@model}} />
The @model "attribute" is prepended with @ to distinguish it from normal attributes like class
This is then accessible in your components template as @model, but you could give the attribute any name eg
<TaskList @taskList={{@model}} />
then in your component you would access @taskList instead of @model
idk if you ever do ruby on rails
but this is similar to that
thanks for the explanation
still the error log persists
hahaha
which error?
sidenote Ember was originally built by big fans of rails, so it took a lot of inspiration from it initially 🙂
could you send a screenshot of your expanded app folder structure?
just so I can make sure everything is in the right location
What's in your services/store.js? (that file shouldn't be needed unless you're doing something very custom)
Also can you show me the contents of
adapters/application.js
adapters/task.js
serializers/application.js
serializer/task.js
Shouldn't need the task specific files if we can handle it generically in the application adapter and serializer.
services/store.js:
export { default } from 'ember-data/store';
adapters/application.js:
export default class ApplicationAdapter extends JSONAPIAdapter {}
adapters/task.js:
export default ApplicationAdapter.extend({
pathForType: (modelName) => modelName,
});```
serializers/application.js:
export default class ApplicationSerializer extends JSONAPISerializer {}```
serializer/task.js:
export default class TaskSerializer extends ApplicationSerializer {}```
Thanks
So I think you can remove the store service file, it will be made available to your app without needing to re-export it yourself.
Now since Loopback returns just basic JSON we shouldn't use the JSONAPIAdapater or JSONAPISerializer as they expect a very specific format for the JSON.
Change your adapters/application.js to
import RESTAdapter from '@ember-data/adapter/rest';
export default class ApplicationAdapter extends RESTAdapter {
namespace = 'api';
// pathForType = (modelName) => modelName;
// this is commented out as I don't think you'll need it but we can uncomment if needed
}
Change your serializers/application.js to
import JSONSerializer from '@ember-data/serializer/json';
export default class ApplicationSerializer extends JSONSerializer {}
remove the adapters/task.js and serializers/task.js files
Let's see if that changes/fixes things.
Also this assumes your api will be accessible on http://localhost:4200/api/tasks if that is not correct you'll need to add a host field to the ApplicationAdapter eg
import RESTAdapter from '@ember-data/adapter/rest';
export default class ApplicationAdapter extends RESTAdapter {
host = 'http://url-to-your-api-server.com';
namespace = 'api';
}
then the store will request http://url-to-your-api-server.com/api/tasks (or with pathForType uncommented http://url-to-your-api-server.com/api/task) when you do a this.store.findAll('task')
this part, the namespace
should i fill api to it?
because api accessible on localhost:4200/tasks
then you can remove the namespace
and the host api server is localhost:3000/tasks
but you will need to set host to http://localhost:3000
aight
ill try
this is what ive got
afaik, the yellow part we can ignore no?
ahh, that'll be why you had that file 😄 sorry
give me a minute to figure out why my app doesn't do that
ahh, I'm using a different folder structure so my store.js is located somewhere else. Sorry, you will want to undelete the services/store.js file. My bad!
Once the yellow warning goes away show me you current task-list.hbs file
tasks-list.hbs:
{{#each tasks as |task|}}
<li>{{task.title}} - {{task.description}}</li>
{{/each}}
</ul>```
here mate
{{#each @tasks as |task|}}
any arguments you pass to a component will need the @ prepending to the name
Are you following a third party guide? Some of the code you've shared is the old way of doing things.
tbh, I cant find any proper guide, thats why I just try to compile the scattered tutorials here and there
and a lot of questions and answers from stackoverflow that I found is from 2016-2018
yep, a lot of the tutorials you'll find on google and SO answers are quite outdated. Definitely makes things more difficult for people new to ember.
If you are able to afford it I highly recommend this ebook https://balinterdi.com/rock-and-roll-with-emberjs/
I dont think I could at this time
hahhaa
honestly, and a lil bit of backstory, I used to do RoR and JS, but I couldnt land a job, anywhere, so I applied to some country, and 1 from singapore give me this test, which I never touch at all, but I thought I could do it, and after scrambling in loopback 3 for 3 days, here we are at day 1 ember🥹
The official guides and api docs do a pretty decent job as well but they expect everything to just be working and using JSON:API specification API's, so if you need something custom it can be difficult to figure out what you need to do.
Well let's get you to a point where you can talk to the API and land you that job 🙂
So, that error looks like your tasks.js route file is missing the service injection?
from what I gather, yes
I think right now
its just the matter of connection I guess?
like the structure's there, but it just not hitting it
import Route from '@ember/routing/route';
import { service } from '@ember/service';
export default class TasksRoute extends Route {
@service store;
model() {
console.log('Checking store:', this.store);
if (this.store) {
return this.store.findAll('task');
} else {
console.error('Store is undefined!');
return [];
}
}
}
Your tasks.js should look someting like that
with the store being injected to the class
We're making progress 🙂
If you check network tab now you'll see we made a connection to the api 🙂
Couple of more steps and you should be good to go 😄
yoooo
it connects tho
if you dont mind
would you explain like what went wrong?
because I wrote something like this
not exactly
but it got different error
Yep, so the store is now correctly making a connection to the API and it's trying to ingest the JSON it sees, but Loopback doesn't follow the JSON:API specification (https://jsonapi.org/) so we need to adjust the adapter and serializer so they understand what to do with the basic JSON it's getting.
Did you make these changes before?
notice we are importing the RESTAdapter and JSONSerializer instead of JSONAPIAdapter and JSONAPISerializer
adapter:
export default class ApplicationAdapter extends RESTAdapter {
host = 'http://localhost:3000';
}```
ser:
export default class ApplicationSerializer extends JSONSerializer {}```
i think yes
ok, and did you delete the adapters/task.js and serializers/task.js files?
customizing ember-data to work outside of it's default expectations is the hardest part of ember, so once we get this working everything else should be a lot simpler to get working and understand
yes
aight captain
im still looking too who got similar issues
So, older versions of ember-data would do this transformation for you, but the newer versions needs you to tell it how to normalize your payloads, which I was unaware of.
So two options, downgrade ember-data to the older version (4.12.5), or we write the normalize operation ourselves.
hang on, let me do some more investigating
looking at the source code it should still be doing the normalization for you.
let me keep digging, something is going wrong
yeah I'm not sure what I clicked but I saw in the source code an empty normalize method but now I can't find that, looking at the code on npm I can see it should still be doing the transformation
oh I was looking at the base serializer instead of the json serializer
in your application serializer can you add this method
normalize(modelClass, resourceHash) {
const normalized = super.normalize(modelClass, resourceHash);
console.log('should be normalized');
return normalized;
}
Then see if it is logs out that console message
like this?
export default class ApplicationSerializer extends JSONSerializer {}
normalize(modelClass, resourceHash) {
const normalized = super.normalize(modelClass, resourceHash);
console.log('should be normalized');
return normalized;
}
into the class
import JSONSerializer from '@ember-data/serializer/json';
export default class ApplicationSerializer extends JSONSerializer {
normalize(modelClass, resourceHash) {
const normalized = super.normalize(modelClass, resourceHash);
console.log('should be normalized');
return normalized;
}
}
No worries 🙂 Lots to take in right now lol
hmm
can you try restarting the ember dev server
it's like the serializer is not being used
ok, and that file is definitely in app/serializers/application.js?
yessir
can you search your codebase for JSONAPISerializer
normalize isn’t guaranteed to be called
The public contract is normalizeResponse
Implementations of that may or may not delegate to other methods (like normalize) depending on the payload shape and their implementation specifics
ok, but shouldn't the JSONSerializer be normalizing the JSON into JSON:API without needing to add anything to the application serializer?
unsure, the original format seems closer to REST and the JSONSerializer is really very basic
@proven granite whats the response you're getting from the api network request?
can you show the JSON that the tasks request returns, is it
[
{
"title": "Updated Task",
"description": null,
"status": "to-do",
"dueDate": null,
"id": 1,
"createdAt": "2024-01-06T16:50:29.755Z",
"updatedAt": "2024-01-06T16:51:26.436Z"
}
]
yeah
this one
let's make sure the serializer is being loaded and calling normalizeResponse then
import JSONSerializer from '@ember-data/serializer/json';
console.log('I am in your app');
export default class ApplicationSerializer extends JSONSerializer {
normalizeResponse(store, primaryModelClass, payload, id, requestType) {
const normalized = super.normalizeResponse(store, primaryModelClass, payload, id, requestType);
console.log('should be normalized');
return normalized;
}
}
well that's odd, you should at least see I am in your app logged
Are we sure about what’s coming back in the network tab?
And are we sure the task serializer was deleted and only the application serializer remains?
i see the problem 😄
not sure when but you renamed the folder from serializers to serializer
You're welcome
especially you @glad pine bro
Any other issues just ask again
Aight, I wont close this thread, so we can continue here
next step is get a sleep
been doing this for 15hrs
Sleep well
