EmberJS - Get controller created in {{render}} helper - ember.js

I have a {{render 'B' model}} helper in template A, so B/BView/BController are essentially children of A/AView/AController. BController even has AController as its parentController.
Is there a way to (easily) reference BController from AController? I'd prefer not to set something to B's parentController because it's not always A.

Ember lets you use a needs property for this purpose: http://emberjs.com/guides/controllers/dependencies-between-controllers/
App.AController = Ember.ObjectController.extend({
needs: ['b']
actions: {
somethingWithA: function() {
var bController= this.get('controllers.b');
// ...
}
}
});

Related

Compare two Ember objects (created with Ember.Object.create method)

I created a custom object with the Comparable mixin and added it to a model as an attribute with Ember transforms
var customObject = Ember.Object.extend(Ember.Comparable, {
compare: function() {
debugger;
}
});
Once the model is ready I create a copy of the custom object and add it as an attribute to the so that I can compare the custom object when it changes to this original value
export default DS.Model.extend({
custom: DS.attr("custom-object"),
ready: function() {
this.set("originalCustom", Ember.Object.create(this.get("custom")));
},
isUpdated: function() {
return Ember.compare(this.get("custom"), this.get("originalCustom"));
}
});
I manually call isUpdated to check if the compare method is invoked, but it never gets hit.
What am I missing here?

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.

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(),

EmberJS Inheritance Conundrum

Consider this situation. I have a common logic which I want to reuse across Ember.ArrayController and Ember.ObjectController instances.
Both Ember.ArrayController and Ember.ObjectController are derived from a basic Ember.Object so I am trying something like:
AbstractController = Ember.Object.extend({
// some methods with common logic
});
AbstractArrayController = AbstractController.extend({});
AbstractObjectController = AbstractController.extend({});
The problem is I also need AbstractArrayController and AbstractObjectController to extend from their parents (Ember.ArrayController and Ember.ObjectController).
How would I achieve this sort of inheritance?
I am looking at reopen and reopenClass methods right now, maybe they could be useful: http://emberjs.com/api/classes/Ember.Object.html#method_reopen
I tried just doing something like:
Ember.Object.reopenClass({
foo: function () {
return "foo";
}.property("foo")
});
But that doesn't seem to work.
Another way to put the problem:
App.HelloController = Ember.ObjectController.extend({
foo: function () {
return "foo";
}.property("foo")
});
App.WorldController = Ember.ObjectController.extend({
foo: function () {
return "foo";
}.property("foo")
});
How to abstract the foo computed property?
reopenClass adds methods on the class object, not the instance objects. When you do:
Ember.Object.reopenClass({
foo: function () {
return "foo";
}.property("foo")
});
You are creating Ember.Object.foo().
You need to use reopen if you want to methods at an instance level, for example Ember.Object.create().foo().
To answer you question, the best way to abstract a function that many types of objects can use is with a mixin. To create a mixin you use.
var mixin = Ember.Mixin.create({
foo: function() {
return 'foo';
}
});
And to have your objects take advantage of that mixin you can use.
var MyController = Ember.ObjectController.extend(mixin, {
// ...
});
More about mixins: http://codingvalue.com/blog/emberjs-mixins/ and http://emberjs.com/api/classes/Ember.Mixin.html