Dynamic computed properties in Ember.JS deprecated? - ember.js

I am trying to make an ember application. I have a computed property and the controller looks like this:
// The Controller
Todos.Controller = Ember.Controller.create({
// ** SNIP ** //
countCompleted: function()
{
return this.get('todos').filterProperty('completed', true).length
}.property(),
});
// The View
{{Todos.Controller.countCompleted.property}} Items Left
Now the tutorial I'm following is using an older version of Ember.JS. I've fixed every error but this:
Uncaught Error: assertion failed: Ember.Object.create no longer supports defining computed properties.
What's the alternative way to do this?

The computed property is only deprecated on the create() function of an object. If you wish to create a computed property, then you must first extend() the object, and then create() it.
For example:
// The Controller
Todos.TodosController = Ember.Controller.extend({
// ** SNIP ** //
countCompleted: function()
{
return this.get('todos').filterProperty('completed', true).length
}.property(),
});
// Note the lower case 't' here. We've made a new object
Todos.todosController = Todos.TodosController.create();
// The View
// We reference the created object here (note the lower case 't' in 'todosController')
{{Todos.todosController .countCompleted.property}} Items Left

It also seems to work ok if you do a reopen:
Todos.todosController = Ember.Controller.create({
// ** SNIP ** //
});
Todos.todosController.reopen({
countCompleted: function() {
return this.get('todos').filterProperty('completed', true).length
}.property(),
});

Related

this.transitionToRoute not working in my controller Ember

I am using a controller to read the value selected on a drop down menu, take in parameters of some input fields and then save the record. It creates the record and takes in the information just fine. My problem lies when I try to transition to another page at the end of the action. I keep getting the error: Cannot read property 'transitionToRoute' of undefined
I am completely stumped. Any ideas?
Here is my controller code:
var teamId;
export default Ember.Controller.extend({
auth: Ember.inject.service(),
actions: {
onSelectEntityType: function(value) {
console.log(value);
teamId = value;
return value;
},
createProcess: function(processName, processDescription) {
var currentID = this.get('auth').getCurrentUser();
let team = this.get('store').peekRecord('team', teamId);
let user = this.get('store').peekRecord('user', currentID);
let process = this.get('store').createRecord('process', {
team: team,
user: user,
name: processName,
description: processDescription
});
process.save().then(function () {
this.transitionToRoute('teams', teamId);
});
}
}
});
Here is the corresponding route:
export default Ember.Route.extend({
auth: Ember.inject.service(),
model: function() {
var currentID = this.get('auth').getCurrentUser();
return this.store.find('user', currentID);
}
});
You should have clear understanding about this keyword in Javascript. The keyword this only depends on how the function was called, not how/when/where it was defined.
function foo() {
console.log(this);
}
// normal function call
foo(); // `this` will refer to `window`
// as object method
var obj = {bar: foo};
obj.bar(); // `this` will refer to `obj`
// as constructor function
new foo(); // `this` will refer to an object that inherits from `foo.prototype`
Have a look at the MDN documentation to learn more.
You can cache the this in normal variable this and then access inside the call back.
var self = this;
process.save().then(function () {
self.transitionToRoute('teams', teamId);
});
ECMASCript 6 introduced arrow functions whose this is lexically scoped. Here, this is looked up in scope just like a normal variable.
process.save().then(() => {
this.transitionToRoute('teams', teamId);
});

Ember custom view exception: had no action handler for: searchEvent

I have my custom view :
export default Ember.ContainerView.extend({
classNames: ['search-terms'],
eventTypeValue: null,
userSidValue: null,
init: function() {
this._super();
this.eventTypeValue = Ember.TextField.create();
this.userSidValue = Ember.TextField.create();
this.eventTypeValue.set("placeholder", "search by event type");
this.eventTypeValue.addObserver("value", this.userSidValue, this.change);
this.userSidValue.set("placeholder", "earch by user sid");
this.userSidValue.addObserver("value", this.userSidValue, this.change);
this.pushObject(this.eventTypeValue);
this.pushObject(this.userSidValue);
},
change: function() {
this.get("controller").send("searchEvent");
}
});
And controller :
export default Em.Controller.extend({
actions: {
searchEvent : function() {
console.log("controller searchEvent");
}
}
});
And when I change text in some fields, then I have following exception:
Uncaught Error: had no action handler for: searchEvent
But this working when I type some text and then click somewhere out of my custom view.
Your problem is the second argument to addObserver - this is the context that the third argument (this.change) is executed with.
Even though you specify this.change it doesn't use this - it uses the Ember.TextField as the this not the ContainerView.
You need to change the following two lines:
this.eventTypeValue.addObserver("value", this.userSidValue, this.change);
this.userSidValue.addObserver("value", this.userSidValue, this.change);
to:
this.eventTypeValue.addObserver("value", this, this.change);
this.userSidValue.addObserver("value", this, this.change);
This is a working JSBin example
I've commonly seen strings passed as the method (the third argument) - this also works. I would pass strings instead of the actual function itself.
this.eventTypeValue.addObserver("value", this, 'change');
this.userSidValue.addObserver("value", this, 'change');

How do I check if an ember computed property has a setter defined?

Say I have an Ember.Object obj, with a property propPath.
I'm trying to implement:
function isComputedPropertyWithNoSetter(obj, propPath) {
// what do I do here?
// something involving Ember.meta(obj) perhaps?
}
So I can do:
var hasStaticProp = Ember.Object.extend({ prop: 5 }).create();
isComputedPropertyWithNoSetter(hasStaticProp, 'prop');
// => false
var hasComputedPropertyWithSetter = Ember.Object.extend({ prop: function (k, v, d) { }.property() }).create();
isComputedPropertyWithNoSetter(hasComputedPropertyWithSetter, 'prop');
// => false
var hasComputedPropertyNoSetter = Ember.Object.extend({ prop: function () { }.property() }).create();
isComputedPropertyWithNoSetter(hasComputedPropertyNoSetter, 'prop');
// => true
I'm writing 'tree-walking' state serialization code for a large established ember codebase. When I restore state, I want a guard check to make sure I never accidentally overwrite a read-only (getter only) computed property with a static value.
I need to implement this function so I can do....
if (!isComputedPropertyWithNoSetter(obj, propPath) {
// not going to accidentally overwrite a computed property with a static value
Ember.set(obj, propPath, serializedStaticValue);
}
I realize this is fairly dicey, and the solution might be a not entirely recommended hack.

Returning a Promise from a Computed Property

I realize there have been several questions similar to this, but none of those answers seems to be resolving my issue. My objective is to take a list of language's, and filter them so that my template can display a subset of the full list.
I started off by verifying that my computed property is working:
MyController.js
// Works as expected
languagesFiltered: function() {
return this.get('languages');
}.property('languages')
Then I added in a filter function, but here's where I ran into trouble:
MyController.js
languagesFiltered: function() {
// console.log shows that languages is actually a promise
var languages = this.get('languages');
// all of this returns a promise, but Handlebars can't handle the promise
return languages.then( function( languagesArray ) {
return languagesArray.filter( function( item, index, enumerable) {
return item.get('name') !== 'English';
});
})
}.property('languages')
I'm attempting to use the Ember.Array.filter method (http://emberjs.com/api/classes/Ember.ArrayProxy.html#method_filter). The filter seems to be working correctly, but now languagesFiltered returns a promise, and Handlebars can't handle that.
I tried one last alternative:
MyController.js
languagesFiltered: function() {
var languages = this.get('languages');
// "return languages;" works
// But "return languages.filter" returns an empty array
return languages.filter( function( item, index, enumerable ) {
console.log(item);
return true;
});
}.property('languages')
And console.log(item) never gets called. So my questions are:
What is the best way to implement the simple filter I'm after?
This is a read-only computed property, but what are best practices for handling async values in computed properties?
I'm using Ember 1.7.0-beta4, Ember Data 1.0.0-beta10, and ember-cli 0.44. I'd upgrade to Ember 1.7.0, but there's a small bug that affects another part of our app, so we're waiting until 1.7.1. Thanks for your input!
You can try returning a PromiseArray instead of just the promise.
You should be able to do something like..
languagesFiltered: function() {
// all of this returns a promise, but Handlebars can't handle the promise
var promise = this.get('languages').then( function( languagesArray ) {
return languagesArray.filter( function( item, index, enumerable) {
return item.get('name') !== 'English';
});
})
return DS.PromiseArray.create({
promise: promise
});
}.property('languages')
Now there are few better solutions. I use ember-promise-helpers.
So can keep your languagesFiltered code intact and do the following inside your hbs:
{{#each (await languagesFiltered) as|lang|}}
...
...
More – https://emberigniter.com/guide-promises-computed-properties/

Teach me how to design a nested computed property in ember.js

I have a handful of computed properties defined on a component. I'd like to refactor these computed properties to live within a messages object on the component. When I make a call to get one of the computed properties elsewhere, I'm returned an instance of Ember's ComputedProperty object, rather then the translation string I expected. Looking at the documentation, Ember.get should invoke the computed property and return the object itself, the property value or null.
What am I missing? How would I go about structuring these nested computed properties so that I can access them using the get/set interface elsewhere in the component?
App.ValidatedDateComponent = Ember.Component.extend({
format: null,
label: null,
messages: {
invalidDateMsg: (function() {
return I18n.t('%{date} must be a valid date. %{format}', {
date: this.get('label'),
format: this.get('format')
});
}).property('label', 'format')
},
validate: function(value, status) {
if (!moment(value).isValid()) {
return status(false, Ember.get(this.messages, 'invalidDateMsg'));
} else {
return this._super(value, status);
}
}
});
Ember only supports defining computed properties while extending Ember.Object class, the exception to the rule is while defining a Ember.Mixin.
Defining the top level of the nest
var nest = Ember.Object.extend({
foo: function() {
return "something";
}.property()
});
Creating an instance of it
App.IndexController = Em.ArrayController.extend({
messages: nest.create()
});
Template
{{messages.foo}}
http://emberjs.jsbin.com/UhUvOvU/1/edit
So in your case you could, if you really wanted to, do:
messages: Em.Object.extend({
invalidDateMsg: function() {
return I18n.t('%{date} must be a valid date. %{format}', {
date: this.get('label'),
format: this.get('format')
});
}.property('label', 'format')
}).create(),