Promise in Ember route model not resolving/updating - javascript

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'));
}
});

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 ...

Ember.js project - TypeError: session is undefined

I'm trying to run an Ember project that I had to upgrade it's packages, but now I run into deprecation issues.
My current issue is that when I press 'login' I see a TypeError: session is undefined message when I open the developer console in firefox.
I have no knowledge of Ember really, so to my very limited understanding is that there's a build javascript file that's based off an MCR architecture of little javascript files.
So here's the part of the big one:
define('genesis/controllers/login', ['exports', 'ember'], function (exports, _ember) {
exports['default'] = _ember['default'].Controller.extend({
loginMessage: "",
actions: {
authenticate: function authenticate() {
var credentials = this.getProperties('identification', 'password'),
authenticator = 'simple-auth-authenticator:jwt';
this.set("loginMessage", "");
var session = this.get('session');
session.authenticate(authenticator, credentials);
}
}
});
});
And this is the small one it's based off:
templates/login.js
import Ember from 'ember';
export default Ember.Controller.extend({
loginMessage: "",
actions: {
authenticate: function() {
var credentials = this.getProperties('identification', 'password'),
authenticator = 'simple-auth-authenticator:jwt';
this.set("loginMessage", "");
var session = this.get('session');
session.authenticate(authenticator, credentials);
}
}
});
I had to change Ember.ObjectController.extend to Ember.Controller.extend due to deprecation.
Assuming session is a service, you need to inject it into the controller in order to be able to use it. Without injecting, this.get('session') would return undefined.
Check this documentation on more details about dependency injection.
Turned out that during the upgrade I had removed one package from package.json named
ember-simple-auth-token, but I'll give credit to Charizard for letting me find it due to his answer.
I also had to make some tiny changes to the template file.
templates/login.js:
import Ember from 'ember';
export default Ember.Controller.extend({
session: Ember.inject.service(),
loginMessage: "",
actions: {
authenticate: function() {
var credentials = this.getProperties('identification', 'password'),
authenticator = 'authenticator:jwt';
this.set("loginMessage", "");
this.get('session').authenticate(authenticator, credentials);
}
}
});

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.

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')
});

Error Nothing handled the action 'sessionInvalidationSucceeded' using Simple Auth Ember JS

So I have been setting up a auth manager through my ember for the past week a and finally got it working. However, I'm still getting a error when invalidating the user.
Nothing handled the action 'sessionInvalidationSucceeded'
Can't figure out what the best way to handle the error?
import Ember from 'ember';
import DS from 'ember-data';
export default Ember.Object.extend({
authenticate: function(controller, user) {
var app = this.container.lookup('controller:application');
var session = app.get('session').authenticate('simple-auth-authenticator:oauth2-password-grant', user);
session.then(function() {
console.log('Session Started');
controller.transitionToRoute('brands');
});
},
endSession: function() {
var app = this.container.lookup('controller:application');
var session = app.get('session').invalidate();
session.then(function() {
app.store = DS.Store.create();
console.log('Session Ended');
app.transitionToRoute('index');
app.store.destroy();
});
}
});
import Ember from 'ember';
export default Ember.Controller.extend({
actions: {
sessionEnded: function() {
this.authManagerService.endSession();
}
},
currentUser: function() {
return this.store.find('user', this.session.get('user_id');
}.property('#each.user')
});
You need to include the Simple Auth Route mixin on the route you are authenticating
import ApplicationRouteMixin from 'simple-auth/mixins/application-route-mixin';
or handle the action in your initializer
Ember.Application.initializer({
name: 'authentication',
after: 'simple-auth',
initialize: function(container, application) {
var applicationRoute = container.lookup('route:application');
var session = container.lookup('simple-auth-session:main');
// handle the session events
session.on('sessionInvalidationSucceeded', function() {
applicationRoute.transitionTo('index');
});
}
});
Take a look at the api, it's really helpful
http://ember-simple-auth.com/ember-simple-auth-api-docs.html#SimpleAuth-ApplicationRouteMixin-sessionInvalidationSucceeded

Categories

Resources