EmberJS access properties without model object - ember.js

I've got my route setup like so
export default Ember.Route.extend({
model: function(params) {
return this.store.findRecord('organization', params.organization_id);
}
});
When I'm printing out the properties, I have to do this in my template.
{{ model.name }}
Is there anyway I can drop the model part so it's just name?

You can do it if you want, though it's not great practice. In the setupController hook,
setupController(controller, model) {
this._super(controller, model);
controller.set('name', model.get('name'));
}
It'll set a property on the controller so you can access it in the template as {{name}}

Related

set alias for `model` hook

HELP
If there is a model hook in app/routes/post.js say
model() {
return this.store.query('post');
}
in template the returned promised is accessed using
{{#each model as |post|}}
...
{{/each}}
Is there any way to set alias for the model? Something like this in route or controller?
posts: alias('model')
So I can access the returned promise in the template as
{{#each posts as |post|}}
...
{{/each}}
Is this something which is already present or something that got missed from ember documentation?
you can create alias for model property in your controller,
import Controller from '#ember/controller';
import { alias } from '#ember/object/computed';
export default Controller.extend({
posts: alias('model')
})
or using setupController in your route,
export default Route.extend({
setupController(controller, model) {
controller.set('posts', model);
},
});
Reference:
alias api documentation - alias computed property
alias your model - alias-model-rule

Ember identifying current user

I'm trying to build a simple chat app using Ember CLI with a rails API backend, using simple-auth-devise. I am very new to Ember. I have the following setup:
A conversation model which has_many messages. Each message belongs_to a user.
A conversations/show route which loads a conversation and displays all messages
The problem I have is figuring out which messages were written by the current user. I can identify the current user in the route and pass this to the controller, but I'm not sure how to do something in the template which is conditional on this. Here is the code:
Route for conversations/show:
import Ember from 'ember';
export default Ember.Route.extend({
model: function(params) {
return this.store.find('conversation', params.conversation_id);
},
// Get the current user
setupController: function(controller, model){
this._super(controller, model);
var currentUser = this.get('session').get('currentUser');
controller.set("currentUser", currentUser);
}
});
Then in the template I want to do something like this:
{{#each message in messages}}
{{message.body}}
{{#if message.isByCurrentUser }}
Written by me!
{{/if}}
{{/each}}
I don't know how or where to define that message.isByCurrentUser method to use the currentUser variable defined in the route.
Many thanks in advance.
I would recommend you to separate message resource and let it have its own template and controller.
//messages.hbs
{{#each message in messages}}
{{render "message" message}}
{{/each}}
//message.hbs
{{message.body}}
{{#if isByCurrentUser }}
Written by me!
{{/if}}
//message controller
import Ember from 'ember';
export default Ember.Controller.extend({
currentUser: Ember.computed.alias('session.currentUser'),
isByCurrentUser: function() {
return this.get('model.creator.id') === this.get('currentUser.id');
}.property('model.creator', 'currentUser')
});
P.S: I assumed your message model has attribute named creator. change it due to your model
simple-auth injects session into controllers and routes so you don't have to set it explicitly.

Ember model hook not re-evaluated when transitioning to current route [duplicate]

While I am not new to web development, I am quite new to to client-side MVC frameworks. I did some research and decided to give it a go with EmberJS. I went through the TodoMVC guide and it made sense to me...
I have setup a very basic app; index route, two models and one template. I have a server-side php script running that returns some db rows.
One thing that is very confusing me is how to load multiple models on the same route. I have read some information about using a setupController but I am still unclear. In my template I have two tables that I am trying to load with unrelated db rows. In a more traditional web app I would have just issued to sql statements and looped over them to fill the rows. I am having difficulty translating this concept to EmberJS.
How do I load multiple models of unrelated data on the same route?
I am using the latest Ember and Ember Data libs.
Update
although the first answer gives a method for handling it, the second answer explains when it's appropriate and the different methods for when it isn't appropriate.
BEWARE:
You want to be careful about whether or not returning multiple models in your model hook is appropriate. Ask yourself this simple question:
Does my route load dynamic data based on the url using a slug :id? i.e.
this.resource('foo', {path: ':id'});
If you answered yes
Do not attempt to load multiple models from the model hook in that route!!! The reason lies in the way Ember handles linking to routes. If you provide a model when linking to that route ({{link-to 'foo' model}}, transitionTo('foo', model)) it will skip the model hook and use the supplied model. This is probably problematic since you expected multiple models, but only one model would be delivered. Here's an alternative:
Do it in setupController/afterModel
App.IndexRoute = Ember.Route.extend({
model: function(params) {
return $.getJSON('/books/' + params.id);
},
setupController: function(controller, model){
this._super(controller,model);
controller.set('model2', {bird:'is the word'});
}
});
Example: http://emberjs.jsbin.com/cibujahuju/1/edit
If you need it to block the transition (like the model hook does) return a promise from the afterModel hook. You will need to manually keep track of the results from that hook and hook them up to your controller.
App.IndexRoute = Ember.Route.extend({
model: function(params) {
return $.getJSON('/books/' + params.id);
},
afterModel: function(){
var self = this;
return $.getJSON('/authors').then(function(result){
self.set('authors', result);
});
},
setupController: function(controller, model){
this._super(controller,model);
controller.set('authors', this.get('authors'));
}
});
Example: http://emberjs.jsbin.com/diqotehomu/1/edit
If you answered no
Go ahead, let's return multiple models from the route's model hook:
App.IndexRoute = Ember.Route.extend({
model: function() {
return {
model1: ['red', 'yellow', 'blue'],
model2: ['green', 'purple', 'white']
};
}
});
Example: http://emberjs.jsbin.com/tuvozuwa/1/edit
If it's something that needs to be waited on (such as a call to the server, some sort of promise)
App.IndexRoute = Ember.Route.extend({
model: function() {
return Ember.RSVP.hash({
model1: promise1,
model2: promise2
});
}
});
Example: http://emberjs.jsbin.com/xucepamezu/1/edit
In the case of Ember Data
App.IndexRoute = Ember.Route.extend({
var store = this.store;
model: function() {
return Ember.RSVP.hash({
cats: store.find('cat'),
dogs: store.find('dog')
});
}
});
Example: http://emberjs.jsbin.com/pekohijaku/1/edit
If one is a promise, and the other isn't, it's all good, RSVP will gladly just use that value
App.IndexRoute = Ember.Route.extend({
var store = this.store;
model: function() {
return Ember.RSVP.hash({
cats: store.find('cat'),
dogs: ['pluto', 'mickey']
});
}
});
Example: http://emberjs.jsbin.com/coxexubuwi/1/edit
Mix and match and have fun!
App.IndexRoute = Ember.Route.extend({
var store = this.store;
model: function() {
return Ember.RSVP.hash({
cats: store.find('cat'),
dogs: Ember.RSVP.Promise.cast(['pluto', 'mickey']),
weather: $.getJSON('weather')
});
},
setupController: function(controller, model){
this._super(controller, model);
controller.set('favoritePuppy', model.dogs[0]);
}
});
Example: http://emberjs.jsbin.com/joraruxuca/1/edit
NOTE: for Ember 3.16+ apps, here is the same code, but with updated syntax / patterns: https://stackoverflow.com/a/62500918/356849
The below is for Ember < 3.16, even though the code would work as 3.16+ as fully backwards compatible, but it's not always fun to write older code.
You can use the Ember.RSVP.hash to load several models:
app/routes/index.js
import Ember from 'ember';
export default Ember.Route.extend({
model() {
return Ember.RSVP.hash({
people: this.store.findAll('person'),
companies: this.store.findAll('company')
});
},
setupController(controller, model) {
this._super(...arguments);
Ember.set(controller, 'people', model.people);
Ember.set(controller, 'companies', model.companies);
}
});
And in your template you can refer to people and companies to get the loaded data:
app/templates/index.js
<h2>People:</h2>
<ul>
{{#each people as |person|}}
<li>{{person.name}}</li>
{{/each}}
</ul>
<h2>Companies:</h2>
<ul>
{{#each companies as |company|}}
<li>{{company.name}}</li>
{{/each}}
</ul>
This is a Twiddle with this sample: https://ember-twiddle.com/c88ce3440ab6201b8d58
Taking the accepted answer, and updating it for Ember 3.16+
app/routes/index.js
import Route from '#ember/routing/route';
import { inject as service } from '#ember/service';
export default class IndexRoute extends Route {
#service store;
async model() {
let [people, companies] = await Promise.all([
this.store.findAll('person'),
this.store.findAll('company'),
]);
return { people, companies };
}
}
Note, it's recommended to not use setupController to setup aliases, as it obfuscates where data is coming from and how it flows from route to template.
So in your template, you can do:
<h2>People:</h2>
<ul>
{{#each #model.people as |person|}}
<li>{{person.name}}</li>
{{/each}}
</ul>
<h2>Companies:</h2>
<ul>
{{#each #model.companies as |company|}}
<li>{{company.name}}</li>
{{/each}}
</ul>
I use something like the answer that Marcio provided but it looks something like this:
var products = Ember.$.ajax({
url: api + 'companies/' + id +'/products',
dataType: 'jsonp',
type: 'POST'
}).then(function(data) {
return data;
});
var clients = Ember.$.ajax({
url: api + 'clients',
dataType: 'jsonp',
type: 'POST'
}).then(function(data) {
return data;
});
var updates = Ember.$.ajax({
url: api + 'companies/' + id + '/updates',
dataType: 'jsonp',
type: 'POST'
}).then(function(data) {
return data;
});
var promises = {
products: products,
clients: clients,
updates: updates
};
return Ember.RSVP.hash(promises).then(function(data) {
return data;
});
If you use Ember Data, it gets even simpler for unrelated models:
import Ember from 'ember';
import DS from 'ember-data';
export default Ember.Route.extend({
setupController: function(controller, model) {
this._super(controller,model);
var model2 = DS.PromiseArray.create({
promise: this.store.find('model2')
});
model2.then(function() {
controller.set('model2', model2)
});
}
});
If you only want to retrieve an object's property for model2, use DS.PromiseObject instead of DS.PromiseArray:
import Ember from 'ember';
import DS from 'ember-data';
export default Ember.Route.extend({
setupController: function(controller, model) {
this._super(controller,model);
var model2 = DS.PromiseObject.create({
promise: this.store.find('model2')
});
model2.then(function() {
controller.set('model2', model2.get('value'))
});
}
});
The latest version of JSON-API as implemented in Ember Data v1.13 supports bundling of different resources in the same request very well, if you don't mind modifying your API endpoints.
In my case, I have a session endpoint. The session relates to a user record, and the user record relates to various models that I always want loaded at all times. It's pretty nice for it all to come in with the one request.
One caveat per the spec is that all of the entities you return should be linked somehow to the primary entity being received. I believe that ember-data will only traverse the explicit relationships when normalizing the JSON.
For other cases, I'm now electing to defer loading of additional models until the page is already loaded, i.e. for separate panels of data or whatever, so at least the page is rendered as quickly as possible. Doing this there's some loss/change with the "automatic" error loading state to be considered.

EmberRoute renderTemplate: with model:

I am trying to have a custom Ember Route, like the following:
App.ResultRoute = Ember.Route.extend({
renderTemplate: function(controller, model) {
alert(model);
this.render('result/main', {
outlet: 'content',
controller: controller
});
},
model: function(params) {
return this.get('store').find('result', params.id).then(function(result) {
return result;
})
},
})
However, looks like renderTemplate: doesn't like model:. In other words, I can get into renderTemplate when accessing the route directly, but when I try to implement the model like this it just wont work.
Any ideas on how to make this work?
Thanks,
I think, you should not use then:
model: function(params) {
return this.get('store').find('result', params.id);
}
The find method return a promise object which is used by ember routing process.
I would also recommend to let Ember renderTemplate based on its defaults. You should not normally implement this method in your routes if you follow the conventions.
In your case, App.ResultController must extend Ember.ObjectController.

Ember.js - IndexRoute's model

I've been over the guide and some questions around the net. This has to be simple but I'm just missing something..The worst is that other stuff is working out great but not something that should be real trivial (imho).
I have a basic Ember app set up. I have an index template defined, as a landing page. There are elements I want it to reference right off the bat from a 'model' i have as just a javascript object in the script.
var company = {
id: '1',
name: 'Some Inc.',
email: 'guy#some-inc.com'
};
App.IndexRoute = Ember.Route.extend({
setupController: function(controller, company) {
// Set the IndexController's `title`
controller.set('title', "Publisher Dashboard");
controller.set('model', company);
}
});
In my HTML I have within the index template:
<script type="text/x-handlebars" data-template-name="index">
...
<span class="name">{{name}} ( {{email}} )</span>
I don't know if things have changed over versions. I have seen different syntax do/claim to do the same stuff. I'm using v1.0.0.
You have to declare a model that you are going to use for the IndexRoute
App.IndexRoute = Ember.Route.extend({
model: function() {
return 'SOMETHING' //could be 'company'
},
setupController: function(controller, company) {
// Set the IndexController's `title`
controller.set('title', "Publisher Dashboard");
controller.set('model', company);
}
});
SetupController model uses the model that is returned by the model hook on the route. So if you return 'company' in the model hook. You will have access to the that company object.
have you tried doing this ?
setupController: function(controller, company) {
this._super(controller, model);
// Set the IndexController's `title`
controller.set('title', "Publisher Dashboard");
controller.set('model', company);
}
I added one line, which is to call super.