Ember 2.0 router does not load model data? - javascript

I have in my router.js:
Router.map(function() {
this.route('portfolio', function() {
this.route('company', { path:'/company/:id' });
});
}
And in my routes/portfolio/company.js:
import Ember from 'ember';
export default Ember.Route.extend({
model: function(params) {
var companyId = params.id;
return new Ember.RSVP.hash({
company: Ember.$.ajax({ url: '/api/company/'+companyId, dataType: "json", type: 'GET' })
}).then(function(message) {
return message;
}, function(error) {
console.log( error );
});
}
});
My route and template is loading fine, when I navigate to app/portfolio/company/1, but for some reason when I navigate to that route, Ember wont load the model (no error, but the {{model}} variable does not get populated in template). Only when I refresh the page, Ember loads the model?! I am a bit confused now...
Edit: added missing param and added better description

I think in your template or in controller you are using model like so
model.company replace it with model, and remove extraneous RSVP.hash
because Ember.$.ajax already returns promise which model hooks can handle
so in ES6 (ember-cli supports it) your model hook should look like this
model({ id }) {
return Ember.$.ajax('/api/company/' + id);
}
with above things everything should work, what was happening I think you were passing just model to {{link-to}} while your controller or template expecting model.company so was breaking things

Related

ember set component data from application controller

I am new to Ember and trying to figure out how the data routing works. I have a 'page-notices' component and template thats included in my application.hbs file. It handles showing error or other notifications to users. I cannot figure out how to set the data inside the component from the application controller.
When a user triggers a logout action in application controller, I send it to the server via ajax json request and then need to update the page-notices component if there was an error returned. What does the page-notices controller need to look like to get this done? Am I asking the wrong question and shouldn't be using the controller for this?
//app/templates/application.hbs
{{app-header}}
<div id="pagecontent">
{{page-notices}}
<div id="wrapper">
{{outlet}}
<div class="push"></div>
</div>
</div>
{{app-footer}}
//app/controllers/application.js
import Ember from 'ember';
import ENV from '/config/environment';
var $ = Ember.$;
export default Ember.Controller.extend({
session: Ember.inject.service('session'),
pagenotices: Ember.inject.controller("page-notices")
actions: {
logout: function() {
var self = this;
$.ajax({
dataType: "json",
method: 'GET',
url: ENV.APP.apiHost,
data: {p: 'logout'},
success: function( response ){
if( response.success || (response.loggedin == false) ){
self.get('session').invalidate();
self.transitionToLoginRoute();
} else {
self.get('pagenotices').set('pageerrors', response.error);
self.get('pagenotices').set('pageerrorsview', '');
}
}
});
},
},
transitionToLoginRoute: function() {
this.transitionToRoute('login');
},
});
//app/templates/components/page-notices.js
<div id="pagenotices" class="{{pagenoticeview}}">
<div id="pageerrors" class="error centered {{pageerrorsview}}">{{pageerrors}}</div>
<div id="pagemessages" class="notice centered {{pagemessagesview}}">{{pagemessages}}</div>
</div>
//app/components/page-notices.js
import Ember from 'ember';
import ENV from '/config/environment';
const { inject: { service }, Component } = Ember;
export default Component.extend({
pagenoticeview: 'hide',
pageerrors: '',
pageerrorsview: 'hide',
pagemessages: '',
pagemessagesview: 'hide',
});
I did not quite understand why you injected page-notices controller to application controller; because you have already put page-notices component directly to application.hbs. I might be wrong but I got the sense that you are confusing a controller and a component.
Anyway,the following should work.
Remove pagenotices: Ember.inject.controller("page-notices") this; since we have no work with pagenotices controller as I explained above.
Change the else part in ajax handler of logout action within application.js as follows:
self.set('pageerrors', response.error);
self.set('pageerrorsview', '');
So that the corresponding attributes are directly saved to application controller itself.
Pass the corresponding attributes to page-notices component from within application.hbs with
{{page-notices pageerrors=pageerrors pageerrorsview=pageerrorsview}}
Declare the initial values of pageerrors and pageerorsview within application.js and remove them from page-notices component if you want. I mean declarations of pageerrors: '', pageerrorsview: 'hide'
This should work if I got what you are asking right, best regards.

Content is undefined when trying to work with a record in Ember

I am trying to update a record in the Ember store. When I try to do this, it returns the following error:
Uncaught Error: Assertion Failed: Cannot delegate set('name', test) to the 'content' property of object proxy : its 'content' is undefined.
The controller looks like this:
import Ember from 'ember';
export default Ember.Controller.extend({
model: null,
event: {
name: "test",
id: "adfg8943224xcvsdf"
},
actions: {
editEvent (event) {
var Event = this.store.find('event', event.id);
Event.set('name', event.name);
Event.save()
}
}
});
The route looks like this:
import Ember from 'ember';
export default Ember.Route.extend(AuthenticatedRouteMixin, {
model () {
return {
event: this.store.find('event')
}
},
setupController (controller, model) {
controller.set('model', model);
}
});
The template triggers the action, sending along a object called event, which has properties like name and id. The values of the event object come from the controller and have been set before triggering the editEvent action:
<form {{action 'editEvent' event on="submit"}}>
I believe what is happening is that your model hook is returning a POJO that contains a promise that will resolve. If you want to pass that to your action then you need to do
<form {{action 'editEvent' model.event on="submit"}}>
That being said you should really just return a promise from your model hook so that Ember will wait for your data to load before rendering the template. With the way you have it setup now, if your data takes a long time to load, someone could submit the form before the model is loaded and you'll get an error.
I think you want your route to look like this (no need to override setupController):
import Ember from 'ember';
export default Ember.Route.extend(AuthenticatedRouteMixin, {
model () {
return this.store.find('event');
}
});
Then in your template:
<form {{action 'editEvent' model on="submit"}}>
If you need to load multiple models then you should use Ember.RSVP.hash.
See this answer: EmberJS: How to load multiple models on the same route?
Also, I'm not quite sure what your action is trying to do but you don't need to find the record again. The code you posted for your action doesn't actually do anything. It gets the event and then sets the event's name to its own name.
actions: {
editEvent (event) {
// you already have the event, you passed it in as a parameter
// you don't need to query the store again for it.
var Event = this.store.find('event', event.id);
// This doesn't do anything as it just sets the event.name to itself
Event.set('name', event.name);
Event.save()
}
}
I think you mean to do this:
actions: {
editEvent (event) {
event.set('name', 'updated name');
event.save();
}
}

Calling controller method from controller action in Ember 2.0

Ember 2.0 is really giving me a hard time to understand the new functionality. I want to call a method in the controller from the action function, but dont seem to find a way how. Wasted some time on this already
I have read this: calling method from action of controller in emberjs but it is only working for Ember 1.x as in Ember 2.0 there is no ArrayController anymore and I cannot use this.send().
Basically what I would need is:
App.SomeController = Ember.Controller.extend({
methodFunction: function(data) {
console.log("YEY", data);
}
actions: {
sessionAction: function(data) {
this.methodFunction(data);
}
}
});
Problem is that this.methodFunction is not available
Code you've provided in question has an error:
SyntaxError: controllerName.js: unknown: Unexpected token (7:3)(…)
You're missing , after methodFunction declaration. Fix:
App.SomeController = Ember.Controller.extend({
methodFunction: function(data) {
console.log("YEY", data);
},
actions: {
sessionAction: function(data) {
this.methodFunction(data);
}
}
});
For template:
<button {{action 'sessionAction' 'Example data'}}>Send session action</button>
It logs correctly:
YEY Example data
By the way, you can also take advantage of ES2015 syntax:
export default Ember.Controller.extend({
methodFunction(data) {
console.log("YEY", data);
},
actions: {
sessionAction(data) {
this.methodFunction(data);
}
}
});
Working demo.
Full code behind demo.

Model reloading with Ember Data

I'm trying to poll for more data using the documented model.reload() function
App.ModelViewRoute = Ember.Route.extend({
actions: {
reload: function() {
this.get('model').reload();
}
}
});
But i'm getting an error message saying...
undefined is not a function TypeError: undefined is not a function
Is there a better way of doing this, it seems like I cannot access the model in this way from the route?
Here is the router
App.Router.map(function() {
this.route('video', { path: '/videos/:video_id' });
});
Here is the route
App.VideoRoute = Ember.Route.extend({
model: function(params) {
return this.store.find('video', params.video_id);
},
actions: {
reloadModel: function() {
// PROBLEM HERE
// this.get('model').reload();
Ember.Logger.log('reload called!');
}
}
});
Here is the model
App.Video = DS.Model.extend({
title: DS.attr('string'),
status: DS.attr('string')
});
And the templates
<script type="text/x-handlebars" data-template-name="application">
<h1>Testing model reloading</h1>
{{#link-to "video" 1}}view problem{{/link-to}}
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="video">
<h1>Video</h1>
<h2>{{title}}</h2>
{{model.status}}
<p><button {{action 'reloadModel'}}>Reload model</button></p>
</script>
I've made a jsbin of the issue here:
http://jsbin.com/wofaj/13/edit?html,js,output
I really can't understand why the reload gives me this error. Any advice would be much appreciated.
Thanks
Since model already exists as a hook on Ember.Route, you cannot get that as a property.
Instead you can do the following:
this.modelFor('video').reload();
Technically you could do this.get('currentModel').reload(); too, but that's undocumented and probably won't be available in the future.
The refresh method of the route would do what you're after
App.VideoRoute = Ember.Route.extend({
model: function(params) {
return this.store.find('video', params.video_id);
},
actions: {
reloadModel: function() {
this.refresh()
}
}
});
API docs
The route model function provides a hook to load your controller data. There is a specific section at the ember guide.
1) If you want to access your content, it would be like:
reload: function() {
this.controller.get('content');
}
2) reload is a method available of ember-data objects. In your example, you are loading a js object ({ id:2, title:"Test video title 2", status:"downloading"}).

Loading data into root route of ember router when it's created outside of application.create()

When you want to use classes you created in Em.Application.create() in your router you need to specify the router outside of the application.create. But because the application is automatically initialized the router doesn't route to the / route.
You used to be able to defer the initialization by adding autoinit: false to the application.create. Now you are supposed to use App.deferReadiness() and App.advanceReadiness(). However this doesn't appear to work.
And I can't seem to escape the feeling that you are "supposed" to do it differently.
I added the minimal code to show the problem below. There is also a jsfiddle here
EDIT:
Apparently there is a new router in ember I kinda sorta overlooked that. I've changed the code to the new router, but guess what it still doesn't work :P
window.App = App = Em.Application.create({
ApplicationController: Em.Controller.extend({}),
ApplicationView: Em.View.extend({
template: Em.Handlebars.compile('{{outlet}}'),
}),
ExtendedPatientController: Em.ObjectController.extend({}),
ExtendedPatientView: Em.View.extend({
classNames: ['patient-view', 'extended'],
template: Em.Handlebars.compile('{{name}}')
}),
Patient: Em.Object.extend({
name: undefined,
}),
});
App.Router.map(function (match) {
match('/').to('application', function (match) {
match('/').to('extendedPatient');
})
});
App.deferReadiness();
App.ExtendedPatientRoute = Em.Route.extend({
setupController: function (controller) {
controller.set('', App.Patient.create({
name: "Bert"
}));
},
renderTemplates: function () {
this.render('extendedPatient', {
into: 'application'
});
}
});
App.advanceReadiness();
You're actually doing a lot more work than you need to here.
Here's all the code that you need to make your example work.
Template:
<script type="text/x-handlebars" data-template-name="index">
<div class="patient-view extended">
<p>Name: {{name}}</p>
</div>
</script>
App:
window.App = Em.Application.create();
App.Patient = Em.Object.extend({
name: null
});
App.IndexRoute = Em.Route.extend({
model: function() {
return App.Patient.create({
name: "Bert"
});
}
});
The working fiddle is at: http://jsfiddle.net/NXA2S/23/
Let me explain it a bit:
When you go to /, you are entering the automatic index route. All you need to do to show something on the screen for that route is to implement an index template. The easiest way to do that when you're getting up and running is to put your template in your index.html. Later, you will probably want to use build tools (see my answer here for more information).
You can control what model is displayed in a route's template by overriding the model hook in its route handler. In the case of index, the route handler is App.IndexRoute. In this case, the model is a brand new App.Patient.
You will probably want to implement controllers and events. You can learn more about the router on the Ember.js website
So the new router does solve this problem and does feel a bit shinier.
I finaly found out how to do this basic example this is what happens in the router:
App.Router.map(function (match) {
match('/').to('extendedPatient');
});
This what needs to happen in the views:
ExtendedPatientView: Em.View.extend({
classNames: ['patient-view', 'extended'],
//You need to specify the defaultTemplate because you extend the view class
//instead on initializing it.
defaultTemplate: Em.Handlebars.compile('{{name}}')
}),
You do not have to defer the readiness in the app the new router fixes that.
And in the route you do not need to specify the renderTemplates so the router now looks like:
App.ExtendedPatientRoute = Em.Route.extend({
setupController: function (controller) {
controller.set('content', App.Patient.create({
name: "Bert"
}));
},
});
http://jsfiddle.net/NXA2S/28/

Categories

Resources