Ember template renders function as string - ember.js

I'm new to ember and got a problem with a template.
My route
import Ember from 'ember';
import AuthenticatedRouteMixin from 'simple-auth/mixins/authenticated-route-mixin';
export default Ember.Route.extend(AuthenticatedRouteMixin, {
model: function(params) {
var pageNum = params.page || 1,
pageRows = 8;
return this.store.find('account', {
page: pageNum,
rows: pageRows
});
},
setupController: function(controller, model) {
controller.set('model', model);
controller.set('greeting', 'Hello World');
}
});
My Controller
import Ember from 'ember';
export default Ember.ArrayController.extend({
contentLength: function() {
// console.log(this);
// console.log('length: ' + this.get('content').length);
// return this.get('content').length;
return 'Test string';
},
actions: {}
});
Template
{{ greeting }}
{{ contentLength }}
The {{ greeting }} gets rendered correctly. But {{ contentLength }} gets rendered out as a string function..
Hello World function () { // console.log(this); // console.log('length: ' + this.get('content').length); // return this.get('content').length; return 'Test string'; }
Anyone who can help me solve this issue?
Thanks

You need to add .property() at the end of the contentLength function in order to display it in a template:
import Ember from 'ember';
export default Ember.ArrayController.extend({
contentLength: function() {
// console.log(this);
// console.log('length: ' + this.get('content').length);
// return this.get('content').length;
return 'Test string';
}.property(),
actions: {}
});
If you want the property to update whenever another property of your controller changes simply add it as a "parameter" of your property like this .property("thepropertytoobserve") and the length property of an arrayController is already aviable as {{length}} in the template.
Have a look at the doc for more details on computerd properties.

You can just use {{ length }} in your template as ArrayControllers already have that property.
The reason your contentLength function is not doing what you want is because it is not a computed property. You need to either use Ember.computed(function() { ..}) or append .property(...) to your contentLength function.
Eg:
contentLength: function() {
return this.get('content.length');
}.property('content.length')

Related

passing a parent route's model to a child route in emberjs

I have the following router defined:
export default Router.map(function() {
this.route('user', { path: '/users/:username'}, function () {
this.route('tasks', { path: '/tasks'});
});
});
I then have the following routes:
// routes/user.js
export default Ember.Route.extend({
model(params) {
return params.username;
}
});
// routes/user/index.js
export default Ember.Route.extend({
model() {
// how do i get the username?
return ajax(/* get user's profile */);
}
});
// routes/user/tasks.js
export default Ember.Route.extend({
model() {
// how do i get the username?
return ajax(/* get user's tasks */);
}
});
And this template:
{{!-- templates/user.hbs --}}
<h1>{{model}}</h1>
{{outlet}}
How can I get username in the child routes to use in an ajax request?
You can use paramsFor or modelFor:
modelFor: http://emberjs.com/api/classes/Ember.Route.html#method_modelFor
return ajax(baseUrl + "/" + this.modelFor('user') + "/profile");
paramsFor: http://emberjs.com/api/classes/Ember.Route.html#method_paramsFor
return ajax(baseUrl + "/" this.paramsFor('user').username + "/tasks");

Ember: count number of keys in object?

I have data that looks roughly like this:
"id": "1",
"slug": "WD",
"name": {
"en": "Working Draft",
"de": "Arbeitsentwurf",
"fr": "Version de travail",
"ja": "草案",
"ru": "Рабочий черновик"
}
And I am passing the name object to a component:
{{title-name name=model.name lang='en'}}
In the component template I would like to output the number of translations
<p>Translated {{translationCount}} times.</p>
I tried a few different things in my component to come up with this total but none of them work. How would I count the number of objects?
export default Ember.Component.extend({
// did not work:
translationCount: Ember.computed.alias('Object.keys(name).length'),
// did not work:
// translationCount: Ember.computed.alias('name.length'),
});
Being a little more explicit about it seems to work:
export default Ember.Component.extend({
translationCount: Ember.computed('name', function() {
return Object.keys('name').length;
})
});
Check out this ember-twiddle for an implementation of this.
Application Template
<h1>Welcome to the {{appName}}</h1>
{{title-name name=data.name lang='en'}}
{{outlet}}
Application Controller
import Ember from 'ember';
export default Ember.Controller.extend({
appName:'Stephanie Hobson App',
data: {
id: 1,
slug: 'WD',
name: {
en: 'Working Draft',
de: 'Arbeitsentwurf',
fr: 'Version de travail',
ja: '草案',
ru: 'Рабочий черновик'
}
}
});
title-name.js component
import Ember from 'ember';
var { computed, get } = Ember;
export default Ember.Component.extend({
translationCount: computed('name', function() {
var name = get(this, 'name');
return Object.keys(name).length;
})
});
title-name.hbs component template
{{yield}}
{{translationCount}}

How to set default values for query params based on dynamic segment?

I am creating universal grid for some entities. For that I added this in routes:
this.route('record', { path: '/record' },function() {
this.route('index', {path: '/:entity'});
this.route('view', {path: '/:entity/:record_id'});
});
and created new "index" route:
export default Ember.Route.extend({
entity: '',
queryParams: {
sort: {
refreshModel: true
}
},
beforeModel: function(transition) {
var entity = transition.params[this.get('routeName')].entity;
this.set('entity', entity);
},
model: function(params) {
delete params.entity;
return this.store.findQuery(this.get('entity'), params);
},
}
my controller
export default Ember.ArrayController.extend({
queryParams: ['sort'],
sort: ''
}
how can I set default value for the "sort" based on dynamic segment?
For example in my settings I store sort values for all entites:
'settings.user.sort': 'email ASC',
'settings.company.sort': 'name ASC',
I tried to define "sort" as computed property, but its "get" method is called in time when I can't get a value of dynamic segment from currentHadlerInfo or from route.
Also defining of the property "sort" as computed property has strange effect, for example, when I define it as
sort: 'email ASC'
in my template it is displayed via {{sort}} as expected (email ASC).
but when I return a value from computed property, I see empty value in my template and this affects on a work of components (I can't get current sorted column)
What can I do?..
Here is a rough implementation of setting the sort properties based on dynamic segment values. The code will look like
<script type="text/x-handlebars" data-template-name="index">
{{#link-to 'sort' model.settings.user.sort}}Sort{{/link-to}}
</script>
<script type="text/x-handlebars" data-template-name="sort">
<ul>
{{#each arrangedContent as |item|}}
<li>{{item.val}}</li>
{{/each}}
</ul>
</script>
var settings = Em.Object.create({
settings: {
user: {
sort: 'val ASC'
}
}
});
App.Router.map(function() {
this.route('sort', {path: '/:sortParams'});
});
App.SortRoute = Ember.Route.extend({
params: null,
model: function(params) {
this.set('params', params);
return [{val:1}, {val:5}, {val:0}];
},
setupController: function(controller, model) {
this._super(controller, model);
var props = this.get('params').sortParams.split(' ');
var property = [props[0]];
var order = props[1] === 'ASC'? true : false;
controller.set('sortProperties', property);
controller.set('sortAscending', order);
}
});
The working demo can be found here..
As you can see I access the params object in the model hook and store it on the route. In the setupController hook I access the params and set the required values on the controller.

property in route undefined in controller

In the IndexRoute of my Ember hello world app, I start a setInterval function that I wish to allow the end user to turn off (with clearInterval) by clicking a dom element in the template, which triggers an action in the IndexController. So, the setIntervalId is set in the IndexRoute, and I need to pass it to clearInterval in the IndexController, but the way I have it below, the setIntervalId is undefined. I also tried to use App.IndexRoute.setIntervalId to no avail.
How would I accomplish this?
(function() {
window.App = Ember.Application.create({
LOG_TRANSITIONS: true,
LOG_ACTIVE_GENERATION: true
});
App.IndexRoute = Ember.Route.extend({
setIntervalId: 0,
model: function() {
this.setIntervalId = setInterval(this.someInterval, 5000)
},
someInterval: function(){
var datasource = 'http://hackernews/blahblah';
return new Ember.$.ajax({url: datasource, dataType: "json", type: 'GET'}).then(function(data){
return data;
})
},
});
App.IndexController = Ember.ObjectController.extend({
actions: {
clearTimeout: function(){
console.log('clearing interval', this.setIntervalId); //undefined
clearInterval(this.setIntervalId);
}
}
})
})();
template
<script type="text/x-handlebars" data-template-name="index">>
<h1>Hi Babe</hi>
{{ outlet }}
<label {{action "clearTimeout" on="click"}}>clear timeout</label>
</script>
To set the model, you need to return the value in the route’s model function:
model: function() {
return this.setIntervalId = setInterval(this.someInterval, 5000)
}
To access the model in the controller, you need to use this.get('model').
actions: {
clearTimeout: function(){
console.log('clearing interval', this.get('model');
clearInterval(this.get('model'));
}
}

Ember View is rendering too early

I have a Route for creating new documents, making a copy of an existing document.
App.DocumentsNewRoute = Ember.Route.extend({
model: function (params) {
this.modelParams = params; // save params for reference
return App.Document.createRecord();
},
setupController: function (controller, model) {
// use the params to get our reference document
var documentModel = App.Document.find(this.modelParams.document_id);
documentModel.one('didLoad', function () {
// once loaded, make a serialized copy
var documentObj = documentModel.serialize();
// and set the properties to our empty record
model.setProperties(documentObj);
console.log('didLoad');
});
}
});
I added some logs in my View.
App.DocumentView = Ember.View.extend({
init: function () {
this._super();
// fires before the didLoad in the router
console.log('init view');
},
willInsertElement: function () {
// fires before the didLoad in the router
console.log('insert elem');
}
});
And this is my template
{{#if model.isLoaded }}
{{ view App.DocumentView templateNameBinding="model.slug" class="document portrait" }}
{{ else }}
Loading...
{{/if}}
The problem it seems is that my model isLoaded, but not populated when my template is rendered, so the templateNameBinding doesn't exist at this point, and doesn't seem to be updated when the data gets populated.
Should I use something else than model.isLoaded in my template, or should I force a re-render of my template, and if so, how and where? Thanks!
It seem that you are overcomplicating the things. It should be:
EDIT
I misunderstood your question in first read, so
App.DocumentsNewRoute = Ember.Route.extend({
model: function (params) {
var originalDoc = App.Document.find(params.document_id),
newDoc = App.Document.createRecord();
originalDoc.one('didLoad', function () {
newDoc.setProperties(this.serialize());
});
return newDoc;
},
setupController: function (controller, model) {
controller.set('content', model);
}
});
If everything is right, ember will re-render things automatically..