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.
Related
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 ...
I am new to using Ember and have been following an online video tutorial (see weblink below, though its dated as it uses .NET Core 1.0) that demonstrates how to setup a JSON API back-end with an Ember front-end - I am using Visual Studio Code. I have successfully completed the first video and receive responses from the JSON API back-end. However, I am unable to get the second video working by having Ember send a request to the api-back end for data retrieval. I know this because I am monitoring the calls to the server. So, while I can hit the back-end server and receive a JSON response, the front-end response is HTTP Error 404 - page not found and there is no request to the back-end.
HTTP Error:
Error: Ember Data Request GET /todo-items returned a 404
Payload (text/html; charset=utf-8)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>Cannot GET /todo-items</pre>
</body>
</html>
My best guess is that changes have been made to .NET Core and Ember with respect to routing that are not covered in the videos. Unfortunately, a 404 error is very little to go on and I am unable to find the problem. Does anybody know where the problem is or how I can troubleshoot this?
Video tutorial: https://www.youtube.com/watch?v=_d53rG2i9pY&index=2&list=PLu4Bq53iqJJAo1RF0TY4Q5qCG7n9AqSZf
router.js
import EmberRouter from '#ember/routing/router';
import config from './config/environment';
const Router = EmberRouter.extend({
location: config.locationType,
rootURL: config.rootURL
});
Router.map(function() {
this.route('todo-items');
});
export default Router;
environment.js
'use strict';
module.exports = function(environment) {
let ENV = {
modulePrefix: 'todo-list-client',
environment,
rootURL: '/',
locationType: 'auto',
EmberENV: {
FEATURES: {
// Here you can enable experimental features on an ember canary build
// e.g. 'with-controller': true
},
EXTEND_PROTOTYPES: {
// Prevent Ember Data from overriding Date.parse.
Date: false
}
},
APP: {
host: 'http://localhost:5000',
namespace: 'api/v1'
}
};
if (environment === 'development') {
// ENV.APP.LOG_RESOLVER = true;
// ENV.APP.LOG_ACTIVE_GENERATION = true;
// ENV.APP.LOG_TRANSITIONS = true;
// ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
// ENV.APP.LOG_VIEW_LOOKUPS = true;
}
if (environment === 'test') {
// Testem prefers this...
ENV.locationType = 'none';
// keep test console output quieter
ENV.APP.LOG_ACTIVE_GENERATION = false;
ENV.APP.LOG_VIEW_LOOKUPS = false;
ENV.APP.rootElement = '#ember-testing';
ENV.APP.autoboot = false;
}
if (environment === 'production') {
// here you can enable a production-specific feature
}
return ENV;
};
application.js
import DS from 'ember-data';
import ENV from './config/environment';
export default DS.JSONAPIAdapter.extend({
namespace: ENV.APP.namespace,
host: ENV.APP.host
})
todo-items.js
import Route from '#ember/routing/route';
export default Route.extend({
model(){
return this.store.findAll('todo-item');
}
});
Model Files:
todo-item.js
import DS from 'ember-data';
const { attr, belongsTo} = DS;
export default DS.Model.extend({
description: attr('string'),
owner: belongsTo('person')
});
person.js
import DS from 'ember-data';
const { attr, hasMany} = DS;
export default DS.Model.extend({
firstName: attr('string'),
lastName: attr('string'),
todoItems: hasMany('todo-item')
});
UPDATE 1:
API server is running on port 5000 while Ember is running on 4200.
API URL call: http://localhost:5000/api/v1/people
Ember URL call: http://localhost:4200/todo-items
Update 2
Server messages:
API: Now listening on: http://localhost:5000
Ember: Serving on http://localhost:4200/
Do you run ember serve with the proxy parameter pointing to your backend?
Try to run this in your terminal:
ember s -pr=http://localhost:5000
then your requests should go to the right endpoint.
I found the problem. I needed to setup CORS.
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
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'));
}
});
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.