Ember data fetch json file - javascript

I'm trying to get data from json file from server. My code:
adapters/application.js:
import DS from 'ember-data';
export default DS.JSONAPIAdapter.extend({
host: 'http://exampleweb.com',
namespace: 'file.json'
});
models/item.js
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string')
});
routes/index.js
import Ember from 'ember';
export default Ember.Route.extend({
model() {
return this.get('store').findAll('item');
}
});
templates/application.hbs
{{#each model as |item|}}
{{item.name}}<br>
{{/each}}
JSON file on server look like this:
{
"Products": [
{
"name": "Aviator"
},
{
"name": "Dark"
}]
}
Now ember requst http://exampleweb.com/file.json/items. How can I get this Products correctly and display them in template?

Depending on your need, you have two options.
If you need to substitute backend for app development, the best choice is using ember-cli-mirage
If you need to get and display file's contents, you may not use ember data and do smth like
return new Promise((resolve, reject) => {
Ember.$.get('/path/to/file.json').then(resolve, reject);
});
in model hook

Related

Ember - No Data in hasMany Relationship On Initial Load

ember-cli - 3.20, ember-data - 3.30
I am trying to modify the data in a hasMany relationship in the controller setup but the relationship has no data. However, all the data is there after the page is fully loaded (i.e. in my template/actions, all relationship data is there)
I have a Quiz application with Many-Many relationship with Questions.
models/Quiz.js
import { computed } from '#ember/object';
import DS from 'ember-data';
const { attr, hasMany, Model } = DS;
export default Model.extend({
description: attr('string'),
questions: hasMany('question', {async: true}) //also tried with async false
});
models/Question.js
export default Model.extend({
question: attr('string'),
quizzes: hasMany('quiz', {async: true}) //also tried with async false
});
Go to url '/quiz/1' and Route calls findRecord on quiz
routes/quizzes/quiz.js
import Route from '#ember/routing/route';
export default Route.extend({
model(params) { return this.store.findRecord('quiz', params.quiz_id); }
});
controllers/quizzes/quiz.js
import { computed } from '#ember/object';
import Controller from '#ember/controller';
export default Controller.extend({
quiz: computed.alias('model'),
//also attempted in setupController/afterModel in router
modelChanged: function() {
let quiz = this.get('quiz');
let questions = quiz.get('questions'); //questions has no data
questions.then(questions => {
Promise.all(questions.map(question => {
//modify questions/answers here
}));
});
}.observes('model')
actions: {
getQuestions() {
let questions = this.get('quiz.questions'); //questions now has data
}
})};
I have tried to get the question data in both setupController() and afterModel() with no luck.
Note:
The quizzes are nested routes able to select between each quiz to display. So if you navigate from '/quiz/1' to '/quiz/2' and then back to 'quiz/1', the question data is available in the observer, setupController, afterModel, etc. So, the second time you access a specific quiz, the data is available in setup. (data is always available in template/actions).
Any ideas?
Temporary Workaround:
Use an observer on 'quiz.questions' along with a flag to check if first time hitting observer.
import { computed } from '#ember/object';
import Controller from '#ember/controller';
export default Controller.extend({
quiz: computed.alias('model'),
areAnswersSet: false,
observeQuestions: function() {
let questions = this.get('quiz.questions');
if (!this.areAnswersSet && questions.length !== 0) {
this.toggleProperty('areAnswersSet');
questions.forEach(question => { //modify question });
}
}.observes('quiz.questions.[]')
Drawback: Observer will still get called on every questions change. Only needed on initial load.
There were a few bugs in Ember Data 3.3.0 that were related to relationships. It’s worth upgrading to Ember Data 3.3.1 to see if your issue goes away ...

Dynamic segments in ember js

I'm new to ember js. I was trying to use dynamic segments in my ember project and it give me an error.I tried localhost/4200/profile/john in my browser to get the info of "john".I think it is complaining about api end point in server.js.. Please help me to find what i have done wrong.
error display in console:
GET localhost:4500/api/users/john 404 (Not Found)
These are my files;
router.js
Router.map(function() {
this.resource('profile', { path: '/profile/:username' });
});
model/user.js
import DS from 'ember-data';
export default DS.Model.extend({
docType:DS.attr('string'),
firstName:DS.attr('string'),
userName:DS.attr('string'),
password:DS.attr('string'),
lastName:DS.attr('string'),
mobileNo:DS.attr('string'),
landNo:DS.attr('string'),
address:DS.attr(
{
no:'string',
street:'string',
city:'string'
}
),
nicNo:DS.attr('string'),
created_at:DS.attr('date'),
updated_at:DS.attr('date')
});
route/profile.js
import Ember from 'ember';
export default Ember.Route.extend({
model: function(params, transition) {
return this.get('store').find('user', params.username);
}
});
server.js
app.get('/api/users', function(req,res) {
UserModel.find({},function(err,docs) {
if(err) {
res.send({error:err});
}
else {
res.send({user:docs});
}
});
});
template/profile.hbs
<h2>Welcome user</h2>
{{#each item in model}}
{{item.userName}}
{{/each}}
You need to add an adapter to your application and tell it where your API is.
//app/adapters/application.js
import DS from 'ember-data';
export default DS.JSONAPIAdapter.extend({
namespace: 'api' //All requests will be made to api/*
});
Beware that the example I gave you is using the JSONAPI Adapter (Ember 2.0) but there's also a RESTAdapter, you have to choose the right one for you.

Promise in Ember route model not resolving/updating

I'm using Ember Simple Auth and a service that gets injected into application controller to keep track of currently logged in user. I can use {{accountName}} for the currently logged in user in my application template by doing the following:
//controllers/applications.js
import Ember from 'ember';
export default Ember.Controller.extend({
session: Ember.inject.service(),
userFromSession: Ember.inject.service('session-user'),
accountName: Ember.computed('session.data.authenticated.userId', function(){
this.get('userFromSession.user').then((user)=>{
if (Ember.isEmpty(user.get('company'))) {
this.set('accountName', user.get('firstName') + ' ' + user.get('firstName'));
} else {
this.set('accountName', user.get('company.name'));
}
});
})
});
My session-user service looks like the following:
//services/session-user.js
import Ember from 'ember';
import DS from 'ember-data';
const { service } = Ember.inject;
export default Ember.Service.extend({
session: service('session'),
store: service(),
user: Ember.computed('session.data.authenticated.userId', function() {
const userId = this.get('session.data.authenticated.userId');
if (!Ember.isEmpty(userId)) {
return DS.PromiseObject.create({
promise: this.get('store').find('user', userId)
});
}
})
});
Now, a user has a company, and a company has opportunities. I would like to retrieve the company opportunities, based on the currently logged in user. How do I do this? In my opportunities route I have tried the following:
//routes/opportunities/index.js
import Ember from 'ember';
export default Ember.Route.extend({
sessionUser: Ember.inject.service('session-user'),
model: function(){
this.get('sessionUser.user').then((user)=>{
let companySlug = user.get('company.slug');
console.log(companySlug);
return this.store.findRecord('company', companySlug);
});
}
});
When using {{model.opportunities}} in my template, it just stays blank and looks like the promise never resolves. However, I can see the data populating in the Ember Inspector, as well as the expected output of my console logs. Further, when I do the following, it works fine:
//routes/opportunities/index.js
import Ember from 'ember';
export default Ember.Route.extend({
model: function(){
let companySlug = 'known-company-slug';
return this.store.findRecord('company', companySlug);
}
});
Which means that the problem lies with model not resolving/updating for some reason. What am I doing wrong here?
Okay so I was trying to get the company model when I already had access to it through the sessionUser service.
//routes/opportunities/index.js
import Ember from 'ember';
export default Ember.Route.extend({
sessionUser: Ember.inject.service('session-user'),
model: function(){
return this.get('sessionUser.user').then(user => user.get('company'));
}
});

return first element from array as computed property from Ember controller subclass

I am trying to return the first element of my array as a computed property to my template from my controller. My code is below. I am 100% about my array and template. Problem is my syntax about in the controller.
Basically my array is works made of Work objects. And I would ideally return the first element Work. Is that even possible in javascript?
Best.
//controller works.js
import Ember from "ember";
export default Ember.Controller.extend({
firstElement: function () {
var arr = this.get('model');
return arr[0];
console.log(arr[0]);
}.property('Work')
});
//template works.js
<div class="right">
{{#liquid-with model as |currentModel|}}
{{firstElement}}
{{/liquid-with}}
</div>
//route works.js
import Ember from 'ember';
var Work = Ember.Object.extend({
name: '',
year: '',
description:'',
image:'',
logo:'',
work_id: function() {
return this.get('name').dasherize();
}.property('name'),
});
var minibook = Work.create({
id: 1,
name: 'MINIBOOK',
year: '2014',
description:'MiniBook is an iphone app that explores storytelling in its own format. The format',
image:'assets/images/minibook_iphone.png',
logo:'assets/images/minibook_logo.png'
});
var poetics = Work.create({
id: 2,
name: 'POETICS',
year: '2013',
description:'Lorem Ipsum Poetics',
image:'assets/images/poetics_iphone.png',
logo:'assets/images/poetics_logo.png'
});
var WorksCollection = Ember.ArrayProxy.extend(Ember.SortableMixin, {
sortProperties: ['id'],
sortAscending: true,
content: []
});
var works = WorksCollection.create();
works.pushObjects([poetics, minibook]);
export default Ember.Route.extend({
model: function() {
return works;
}
});
This would work.
Long way (just to improve your computed property code):
// controller work.js
import Ember from "ember";
export default Ember.Controller.extend({
firstElement: function () {
return this.get('model').get('firstObject'); // or this.get('model.firstObject');
}.property('model.[]')
});
1) you set works as model in route, so you could get it as model in controller
2) .property(model.[]) means computed property on array, so adding and deleting array element will fire update. You could also choose some specific property i.e. .property(model.#each.modelProperty)
3) fistObject is proper method (not [0]), since you are working with Ember.ArrayProxy, see http://emberjs.com/api/classes/Ember.ArrayProxy.html
4) you could use {{firstElement}} in template
Lazy way:
1) set model in route as array or as promise resolved in array
// works = ... as is
export default Ember.Route.extend({
model: function() {
return works;
}
});
2) get model.firstObject in directly in template
//template works
{{model.firstObject}} {{!-- first object of model array --}}
{{model.firstObject.name}} {{!-- name of first object --}}
UPDATE:
use proper iteration syntax http://ef4.github.io/liquid-fire/#/helpers/liquid-with/10
{{#liquid-with model as currentModel}}
{{currentModel.firstObject.name}}
{{/liquid-with}}
A bit more updated (or different) example
import Controller from '#ember/controller';
import { readOnly } from '#ember/object/computed';
export default Controller.extend({
firstElement: readOnly('model.firstObject')
});
Or if you want bidirectional data flow (can set firstElement)
import Controller from '#ember/controller';
import { alias } from '#ember/object/computed';
export default Controller.extend({
firstElement: alias('model.firstObject')
});

Edit RESTAdapter url for API end point

I have an api endpoint that requires a / at the end of it, but Ember does not add the /. Is there a way to edit the URL that the RESTAdapter creates so that it adds this slash?
Currently the URL ember sends is http://www.myapi.com/v1/roles
I need the URL to look like this: http://www.myapi.com/v1/roles/
Here is my current ApplicationAdapter:
import DS from 'ember-data';
export default DS.RESTAdapter.extend({
ajaxError: function() {
console.log('error');
},
host: 'http://www.myapi.com',
namespace: 'v1'
});
Here is my router:
import Ember from 'ember';
export default Ember.Route.extend({
model: function(params) {
return this.store.find('role');
}
});
You'll want to override the buildURL function on your ApplicationAdapter to append the trailing slash. You can just call the default buildURL that DS.RESTAdapter provides and then append the slash.
Here's what the code will look like:
import DS from 'ember-data';
export default DS.RESTAdapter.extend({
ajaxError: function() {
console.log('error');
},
host: 'http://www.myapi.com',
namespace: 'v1',
buildURL: function(type, id, record) {
//call the default buildURL and then append a slash
return this._super(type, id, record) + '/';
}
});
Here's the documentation for buildURL.

Categories

Resources