How to work with REST API using Ember data? - javascript

I'm trying to use ember data with REST API from server. The project is quiet simple and works fine with Fetch API, but i would like to implement the same result using Ember data and understand it's principles. I could not use ember twiddle with this configuration. Below will be the main parts of the project. It was made by Ember CLI version 4.0.1.
The JSON responce from url http://localhost:3000/api/v1/items looks like:
[
{
"id": 1,
"name": "Item 1",
"description": "12345",
"created_at": "2022-01-07T11:43:21.755Z",
"updated_at": "2022-01-07T11:43:21.755Z"
},
{
"id": 2,
"name": "Item 2",
"description": "22222",
"created_at": "2022-01-07T11:43:29.787Z",
"updated_at": "2022-01-07T11:43:29.787Z"
},
{
"id": 3,
"name": "Item 3",
"description": "33333",
"created_at": "2022-01-07T11:43:37.885Z",
"updated_at": "2022-01-07T11:43:37.885Z"
}
]
The responce from a single item request such as http://localhost:3000/api/v1/items/1:
{
"id": 1,
"name": "Item 1",
"description": "12345",
"created_at": "2022-01-07T11:43:21.755Z",
"updated_at": "2022-01-07T11:43:21.755Z"
}
app/models/item.js:
import Model, { attr } from '#ember-data/model';
export default class ItemModel extends Model {
#attr name;
#attr description;
}
app/routes/items.js:
import Route from '#ember/routing/route';
import { inject as service } from '#ember/service';
export default class ItemsRoute extends Route {
#service store;
async model() {
return this.store.findAll('item');
}
}
app/routes/item.js:
import Route from '#ember/routing/route';
import { inject as service } from '#ember/service';
export default class ItemRoute extends Route {
#service store;
async model(params) {
return this.store.findRecord('item', params.id);
}
}
app/templates/items.hbs:
{{#each #model as |item|}}
<LinkTo #route='item' #model={{item}}>{{item.name}}</LinkTo>
{{/each}}
app/adapters/application.js:
import RESTAdapter from '#ember-data/adapter/rest';
export default class ItemAdapter extends RESTAdapter {
host = 'http://localhost:3000';
namespace = 'api/v1';
buildURL(...args) {
return `${super.buildURL(...args)}.json`;
}
}
app/serializers/application.js:
import RESTSerializer from '#ember-data/serializer/rest';
export default class ItemSerializer extends RESTSerializer {}
app/router.js:
import EmberRouter from '#ember/routing/router';
import config from 'dist/config/environment';
export default class Router extends EmberRouter {
location = config.locationType;
rootURL = config.rootURL;
}
Router.map(function () {
this.route('items');
this.route('item', { path: '/:id' });
});
When I move to route /items on the ember client, the following error appears in the console:
WARNING: Encountered "0" in payload, but no model was found for model
name "0" (resolved model name using
<dist#serializer:item::constructor>.modelNameFromPayloadKey("0"))
And the same errors for "1" and "2". The template doesn't render links in the each loop.
Probably, there are some features in Ember data with REST API, and I would really like to figure it out. Thanks for attention!

I believe you have this issue because your data is not in the RESTSerializer format but rather in the JSONSerializer one:
the JSONSerializer expects the response to be a JSON object that looks
similar to this
{
"id": "1",
"title": "Rails is omakase",
"tag": "rails",
"comments": ["1", "2"]
}
So i guess you should just use this one.
If for any reason you wanna keep the RESTSerializer you should return your data similar to this format:
{
"item":
{
"id": 1,
"name": "Item 1",
"description": "12345",
"created_at": "2022-01-07T11:43:21.755Z",
"updated_at": "2022-01-07T11:43:21.755Z"
}
}
or for a list
{
"items": [
{
"id": 1,
"name": "Item 1",
"description": "12345",
"created_at": "2022-01-07T11:43:21.755Z",
"updated_at": "2022-01-07T11:43:21.755Z"
},
...
]

Related

Nativescript-vue TabView unable to render first item coming from API

Situation:
Im working on nativescript-vue app where im using TabView Items to create tabs. The title for tab and the information inside the tab is being for looped from JSON data file.
Problem:
The first tabviewitem doesnt render any information. Inorder to see the data, i have to click on other tabs first go back to first tab. After that, i can see the items
The markup i have to create tab using nativescript-vue:
<TabView
android:tabBackgroundColor="#ffffff"
android:tabTextColor="#ffa801"
android:selectedTabTextColor="#f53b57"
androidSelectedTabHighlightColor="#ffffff"
androidTabsPosition="Bottom">
<TabViewItem v-for="subcategory in subcategories" :title="subcategory.name" >
<FlexboxLayout>
<Label :text="subcategory.name" />
</FlexboxLayout>
</TabViewItem>
</TabView>
Subcategories is JSON array coming from API with following structure.
[
{
"id": 3,
"category_id": 1,
"name": "Vegetarian",
"ar_name": null,
"description": "Vegetarian",
"ar_description": null,
"image": null,
"created_at": "2020-03-04 04:12:13",
"updated_at": "2020-03-04 04:12:13",
"items": [
{
"id": 6,
"subcategory_id": 3,
"name": "Salad",
"ar_name": null,
"description": "test",
"ar_description": null,
"price": "100",
"status": "1",
"ar_price": null,
"discount": null,
"discount_colour": null,
"created_at": "2020-05-07 05:32:17",
"updated_at": "2020-05-07 05:32:17"
}
]
},
{
"id": 2,
"category_id": 1,
"name": "Sea Food",
"ar_name": null,
"description": "sea food",
"ar_description": null,
"image": null,
"created_at": "2020-03-04 04:11:38",
"updated_at": "2020-03-04 04:11:38",
"items": [
{
"id": 4,
"subcategory_id": 2,
"name": "some item",
"ar_name": null,
"description": "desc",
"ar_description": null,
"price": "100",
"status": "1",
"ar_price": null,
"discount": null,
"discount_colour": null,
"created_at": "2020-05-06 12:02:58",
"updated_at": "2020-05-06 12:02:58"
}
]
}
]
Solutions that i have tried
https://github.com/nativescript-vue/nativescript-vue/issues/515
but no luck
[Update] here is my script:
<script >
import axios from 'axios';
import Login from "./Login";
import FoodItems from "./FoodItems"
import SubCategories from "./parts/SubCategories"
import CategoryButtons from "./parts/CategoryButtons"
import Popular from "./parts/Popular"
// import Items from "./Items"
export default {
components: {SubCategories, CategoryButtons, Popular},
props: ['parentCategory'],
data() {
return {
subcategories: [],
populars: [],
categories:this.$store.getters.getCategories,
}
},
mounted(){
axios({
method:"GET",
"url":this.$store.state.apiUrl+"categories/"+this.parentCategory.id+"/subcategories",
headers:{
'Accept': 'application/json',
'Authorization': 'Bearer ' + this.$store.getters.getToken,
}
}).then(
response =>
{
//
this.subcategories =response.data.fooditems;
this.populars = response.data.popularitems;
// console.log(response.data.popularitems);
}, error => {
console.error(error);
}
);
},
methods: {
checkToken() {
this.$store.commit("loadFromStorage");
if(!this.$store.getters.getToken) { this.$navigateTo(Login, { clearHistory: true }) }
},
goToShowCase(args) {
// console.log(args);
this.$navigateTo(Showcase,
{
props:
{ parentCategory: args }
});
}
}
}
</script>
Update 2:
I created a similar issue on nativescript playground.
https://play.nativescript.org/?template=play-vue&id=7mNLr7
Thank you in advance!
Does adding a :key to your iterated items solve the issue?
<TabViewItem v-for="subcategory in subcategories"
:key="some_unique_value_between_them"
:title="subcategory.name"
loaded="mainContentLoaded">

Ember unknown relationship

I'm currently building software with Rails + Ember 3.12, but hitting a strange issue.
My models are the following:
// test-case-run
import DS from 'ember-data';
const { Model } = DS;
export default Model.extend({
testCase: DS.belongsTo('test-case'),
testCaseRunLogs: DS.hasMany('test-case-run-logs')
});
// test-case-run-log
import DS from 'ember-data';
const { Model } = DS;
export default Model.extend({
testCaseRun: DS.belongsTo('test-case-run'),
payload: DS.attr('')
});
And, my backend is returning the following payload:
{
"data": {
"id": "83",
"type": "test_case_run",
"relationships": {
"test_case": {
"data": {
"id": "90",
"type": "test_case"
}
},
"test_case_run_logs": {
"data": []
}
}
}
}
{
"data": {
"id": "83",
"type": "test_case_run",
"relationships": {
"test_case": {
"data": {
"id": "90",
"type": "test_case"
}
},
"test_case_run_logs": {
"data": [
{
"id": "426",
"type": "test_case_run_log"
}
]
}
}
},
"included": [
{
"id": "426",
"type": "test_case_run_log",
"attributes": {
"payload": "SCREENSHOT"
},
"relationships": {
"test_case_run": {
"data": {
"id": "83",
"type": "test_case_run"
}
}
}
}
]
}
I've got a custom adapter defining:
pathForType(type) {
return underscore(pluralize(type));
}
So, I think that everything should go well.
However, when I get into the ember inspector, I've got the following:
It seems that my relationship is not loaded properly.
And, I cannot access any data, such as:
log.get('testCaseRun') // that is null
run.get('testCaseRunLogs.length') // it returns 0
This is quite strange, as my records are loaded in the store, but not their relationships.
I have no idea on how to troubleshoot this, since the amount of information I can get from ember is quite limited (there is no error, the format looks good, ...).
Could someone help me to understand what's wrong with my calls? I've tried many things, such as renaming my models, but this does not improve the situation.
Moreover, this model is the only one, which I have problem with. All my other models don't have this problem. So, that's a bit weird.
Thanks a lot
The unknown in <(unknown):ember264> refers to the name of the class. That doesn't mean that your relationship is not loaded correctly. It's just Ember Data using anonymous classes.
To see the data of the relationship you could click on that string and afterwards on content. Another option is passing the full record to the console using the $E link in top right corner. Afterwards you could interact with the record on console, e.g. do a $E.get('testCaseRun.id').
By the way: You don't need to explicitly declare the model name on relationship definition if it's matches the dasherized property name. So testCaseRun: DS.belongsTo('test-case-run') is the same as testCaseRun: DS.belongsTo().
Try to declare hasMany relationship with the model name without 's'
testCaseRunLogs: DS.hasMany('test-case-run-log')
Finally, I've found the answer of my question.
The problem was that I was using the "underscore" form of the relationships:
"included": [
{
"id": "426",
"type": "test_case_run_log", <= HERE
"attributes": {
"payload": "SCREENSHOT"
},
"relationships": {
"test_case_run": {
"data": {
"id": "83",
"type": "test_case_run" <= HERE
}
}
}
}
]
And, changing pathForType was not sufficient.
So, I made my backend use dashes. And, it worked.

Vue Router - build from JSON

I'm quite new to Vue.js. I'm used to vanilla JavaScript.
I need to be able to generate route paths from a JSON file.
How can I achieve it?
EDIT
For example, say this is my JSON:
[
{
"name": "Product 1",
"url": "product-1",
},
{
"name": "Product 2",
"url": "product-2",
}
]
I basically need Vue Router to redirect URL/product-1 to Product 1's component (which will be <Product-1></Product-1>), and URL/product-2 to Product 2's component (<Product-2></Product-2>)
Assuming your component's name is name in the object, we need to match your structure to Vue Router API.
Thus, we can do as follows:
const myRoutes = [
{
"name": "Product 1",
"url": "product-1",
},
{
"name": "Product 2",
"url": "product-2",
}
]
const router = new VueRouter({
routes: myRouters
.map(({name, url})=>({component: name, path: `/${url}`)),
})

How to fetch data from json in Angular2 (TypeScript) without HTTP?

I have task (auth):
All data should be stored in LocalStorage.
Application should be written without back-end languages or services
You should create a json file with 10 test users and get it content while application forming.
Save user to LocalStorage.
I create a json file with data of users, but i don't understand how to fetch it in my service.
{
"Users": [
{
"Id": 1,
"name": "FirstUser",
"email": "1stemail#gmail.com",
"password": "test123"
},
{
"Id": 2,
"name": "2thUser",
"email": "2themail#gmail.com",
"password": "test123"
}
]
}
I tried some tricks like
"import as data from '../shared/users.json'
"const users = data.Users" (error on Users)
Do you have idea "how to" ?
I think it should be import * as data from '../shared/users.json but your system.config might add .ts or .js extension at the end of it.
The way I prefer to do it is to export it as a variable inside a ts file
export var data = {
"Users": [
{
"Id": 1,
"name": "FirstUser",
"email": "1stemail#gmail.com",
"password": "test123"
},
{
"Id": 2,
"name": "2thUser",
"email": "2themail#gmail.com",
"password": "test123"
}
]
}
then import that variable inside your component
import { data } from '../shared/users';
Full plunker example: http://plnkr.co/edit/CGtxYJkcjYt2cEzrbL00?p=info

Ember, hasMany and belongsTo doesn't work

I tried a simple emberapp to display some userdata.
My API returns me this json:
{
"roles":[
{
"id":5,
"name":"admin",
"alias":"Administrator",
"users":[
{
"id":1,
"username":"Evolutio",
"email":"mail#evolutio.tld",
"display_role":"Administrator"
}
]
},
{
"id":2,
"name":"user",
"alias":"Benutzer",
"users":[
]
},
{
"id":1,
"name":"banned",
"alias":"Gesperrt",
"users":[
]
},
{
"id":3,
"name":"mod",
"alias":"Moderator",
"users":[
]
},
{
"id":4,
"name":"support",
"alias":"Supporter",
"users":[
]
}
]
}
my user/model.js:
import DS from 'ember-data';
export default DS.Model.extend({
username: DS.attr('string'),
email: DS.attr('string'),
display_role: DS.attr('string'),
roles: DS.belongsTo('role'),
});
and my role/model.js
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string'),
alias: DS.attr('string'),
users: DS.hasMany('user'),
});
With this setup I got this error in my developer console:
Error while processing route: team.index Assertion Failed: Passing classes to store methods has been removed. Please pass a dasherized string instead of undefined EmberError#
I didn't get the mistake. Maybe anyone can help me for this.
You would need to sideload the data and make your API return data like this:
{
"users": [{
"id": 1,
"username": "Evolutio",
"email": "mail#evolutio.tld",
"display_role": "Administrator",
"roles": [5]
}],
"roles": [{
"id": 5,
"name": "admin",
"alias": "Administrator",
"users": [1]
}, {
"id": 2,
"name": "user",
"alias": "Benutzer",
"users": []
}, {
"id": 1,
"name": "banned",
"alias": "Gesperrt",
"users": []
}, {
"id": 3,
"name": "mod",
"alias": "Moderator",
"users": []
}, {
"id": 4,
"name": "support",
"alias": "Supporter",
"users": []
}]
}
If you must embed the data like you did, you might want to look into EmbeddedRecordsMixin[http://emberjs.com/api/data/classes/DS.EmbeddedRecordsMixin.html]
But I would like to suggest few things:
1) follow json-api(http://jsonapi.org/) since ember data follows it and supports it out of the box( starting from ver 1.13.0 I think)
2) Take it or throw it idea - I don't know your requirement but ideally user model would have hasMany relationship to roles. So I would do things bit differently like this:
//user/model.js:
import DS from 'ember-data';
export default DS.Model.extend({
username: DS.attr('string'),
email: DS.attr('string'),
//display_role: DS.attr('string'),//you don't need this, since user can have multiple roles and also you can access it from the user model itself by doing model.roles.forEach((role)=>{ role.alias or role.name})
roles: DS.belongsTo('role')
});
//role/model.js
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string'),
alias: DS.attr('string')
});
because generally there will be fewer roles than users and also you wouldn't want to load all that data(roles and users) in one request call.

Categories