Do something when Ember component is instantiated? - ember.js

I call a component like this:
{{Gd-text-input label="Specify" name="Specify" key="entry.810220554" triggerKey="tada" hideIf="Client"}}
I would like to run some javascript-code that sets an additional property to this component.
What I'm trying to run is something like this
//Convert string ot array.
GdRadioInput = Em.Component.extend({
init: function(){
var cs = this.get('contentString');
console.log('doesthiswork?');
if(cs){
this.set('content', eval(cs));
}
}
});
But it doesn't run. If someone could just provide a sample that console.logs a the value of a property of a component whenever that component is created, that would be very helpful.

You can run this code in the init method
init:function(){
this._super();
hideIf = this.get('hideIf');
key = this.get('key')
if(hideIf === key){
this.set('class', 'hide');
}
}
Good luck
PD: now this method is private: http://emberjs.com/api/classes/Ember.Component.html#method_init

I know this is an old question, but I wanted to update it with the new way to do things. Instead of overriding the init function, you can now cause a function to run on initialization with .on('init'). Example:
GdRadioInput = Em.Component.extend({
setupFunc: function(){
var cs = this.get('contentString');
console.log('doesthiswork?');
if(cs){
this.set('content', eval(cs));
}
}.on('init')
});

A follow-up: Just in case you are depending on a fully loaded DOM tree, you can use .on('didInsertElement') instead of on('init'):
GdRadioInput = Em.Component.extend({
setupFunc: function(){
var cs = this.get('contentString');
console.log('doesthiswork?');
if(cs){
this.set('content', eval(cs));
}
}.on('didInsertElement')
});
That event is fired when the view's element has been inserted into the DOM: http://emberjs.com/api/classes/Ember.Component.html#event_didInsertElement

Related

How to initialize a Controller's computed property to model in Ember?

I have a computed property myArray defined on an Ember controller that returns an array. The array should be initialized to the contents of model and then recompute by filtering model depending on a user-input query.
myArray: function() {
// return a value that filters model with query
}.property('model', 'query')
The problem is that I can't figure out how to do both at the same time. The below does not work to initialize myArray to model; I'm guessing because model is loaded asynchronously and init() runs before it's finished.
// doesn't work
init: function() {
this._super();
this.set('myArray', this.get('model'));
}
So I thought that setupController() would be the place to set it, but I found that setting myArray there caused the filter update not to work, maybe because I was overwriting the definition.
// route definition...
setupController: function(controller, model) {
controller.set('model', model);
controller.set('myArray', model); // breaks updating
}
How can I accomplish my goal?
You want a computed property depending on model - you don't have to initialize it, it just has to return the right thing.
// untested, just to show the idea
myArray: function() {
var query = this.get('query');
var model = this.get('model');
if (query)
return doSomethingWith(model, query);
else
return model;
}.property('model', 'query')
Since it is a property, the initializing and updating will take place for itself.
That said, Twitter lore is that the usage of .property shall be discouraged, better use Ember.computed.
// untested, just to show the idea
myArray: Ember.computed('model', 'query', function() {
var query = this.get('query');
var model = this.get('model');
if (query)
return doSomethingWith(model, query);
else
return model;
})
It's just another way to write it, and may be mor future-proof in the long run.
try:
myArray: function() {
this.set('myArray', this.get('model');
}.property('model', 'query')
EDIT: I don't know why I went with the roundabout way of doing things. I guess I just wanted to illustrate that computed properties act as setters too. This will also work the same way:
myArray: function() {
return this.get('model').filter(function(item) {
return (item.property_you_want_to_filter_by === true);
});
}.property('model.#each.property_you_want_to_filter_by', 'query')
The Ember shorthand will also work:
myArray: Ember.computed.filter('model', function(item) {
return (item.property_you_want_to_filter_by === true);
});
You need to take advantage of the fact that computed properties are getters and setters.
myArray: function(key, value) {
// This is the setter
if (arguments.length > 1) {
this.set('_myArray', value);
}
// This is the getter
// Do your filtering with `query` here
return this.get('_myArray').filter(function(item) {
return (item.selected === true);
});
}.property('_myArray', 'query')
Since you only set it when you get a new model, you can just store the value in a private property on the controller (in this case _myArray). Then for the getter, you can use the value stored in that property combined with your query to return the value you want. In my example above, I've filtered out every non-selected item.

How to make a computed property that depends on a global class attribute?

I wanna create a property that depends on a global attribute:
App.Test= Em.Object.extend();
App.Test.reopenClass({ all: Em.A() });
App.Other = Em.object.extend({
stuff: function() {
return "calculated stuff from this.get('foo') and App.Test.all";
}.property('foo', 'App.Test.all.#each.bar')
});
As a workarround I could create a observer and always set a dummy property with a new random value to trigger the property change, but is there a better way to do this?
I need this for some caching. I've a really crazy, and single threaded backend. So I write my own Model classes. So I try to reimplement a bit of the logic in the client for a better caching.
Ive an Item class (App.Item) and another class where each instance has a calculated reduced list of Items.
App.Model = Em.Object.extend({
});
App.Model.reopenClass({
all: Em.A(),
load: function(hash) {
return this.get('all').pushObject(this.create(hash));
}
});
App.Item = App.Model.extend({
});
App.List = App.Model.extend({
loadedInitItems: false,
items: function() {
if(!this.get('loadedInitItems')) { this.set('loadedInitItems', true); Backend.call('thelist', function(item) { App.Item.load(this); }); }
return App.Item.all.filter(function(item) {
// heavy filter stuff, depends on a lot of propertys on the current list instance
});
}.property('someprops', 'App.Item.all.#each.foo')
});
Backend.call represents some AJAX stuff
the point is, that now any item could change so that the filter will return something diffrent. And there are other places om the application, where the user can add Items. I dont want to call the backend again, because its very slow! And I know that the backend will not modify the list! So I wanna cache it.
This is just a reduced example of my use case, but I think've described the point. In reallity I have this dozend of times, with over 25000 objects.
have you tried adding 'Binding' to your property and then the value you want to bind to ?, something like this:
App.PostsController = Em.ArrayController.extend({
nameOfYourVariableBinding: "App.SomeObject.propertyYouWantToBindTo"
})
It looks like the problem is the double uppercase letter. So App.test ist working, but not App.Foo.test.
But I was able to find a Solution with the ArrayProxy.
Its about this:
App.Model = Em.Object.extend({
});
App.Model.reopenClass({
all: Em.A(),
load: function(hash) {
return this.get('all').pushObject(this.create(hash));
}
});
App.Item = App.Model.extend({
});
App.List = App.Model.extend({
loadedInitItems: false,
items: function() {
var self = this;
if(!this.get('loadedInitItems')) {
this.set('loadedInitItems', true);
Backend.call('thelist', function(item) {
App.Item.load(this);
});
}
return Em.ArrayProxy.extend({
content: App.Item.all,
arrangedContent: function() {
return this.get('content').filter(function(item) {
// heavy filter stuff, depends on a lot of propertys on the current list instance
// use self.get('someprops')
})
}.property('content.#each.foo')
});
}.property('someprops')
items: function() {
if(!this.get('loadedInitItems')) { this.set('loadedInitItems', true); Backend.call('thelist', function(item) { App.Item.load(this); }); }
return App.Item.all.filter(function(item) {
// heavy filter stuff, depends on a lot of propertys on the current list instance
});
}.property('someprops', 'App.Item.all.#each.foo')
});

How to manually update an ArrayController

In my Application I have the following rawNodes property, which I am using as an application-wide cache:
var App = Ember.Application.createWithMixins({
...
/**
The rawNodes property is a nodes deposit to be used
to populate combo boxes etc.
**/
rawNodes: null,
getNodes: function () {
if (!this.rawNodes) {
this.rawNodes = this.Node.find();
}
},
...
});
In some of my controllers I am modifying data which should also be updated in this generic cache. I would like to implement a couple of functions, to update a given node, and to delete a given node. Something like:
updateNode: function(node_id, node) {
this.rawNodes.update(node_id, node);
},
deleteNode: function(node_id) {
this.rawNodes.delete(node_id);
}
But I do not really know how to work with an ArrayController, not even if those operations are at all possible. I see no examples of this kind of procedures in the ArrayController documentation. Could somebody offer an example, or point me in the right direction?
Rather than using a rawNodes property, I think it might be more useful to
maintain a Node model and a NodesController. Assign the model property
with setupController so you can be sure that nodes are always fetched.
Since this is an application-wide cache, use needs in ApplicationController so it can delegate to its methods.
App.ApplicationRoute = Em.Route.extend({
setupController: function() {
this.controllerFor("nodes").set("model", App.Node.find());
}
});
App.ApplicationController = Em.Controller.extend({
needs: "nodes",
});
App.NodesController = Em.ArrayController.extend({
getNodes: function() {
// ...
}
});
App.NodeController = Em.ObjectController.extend({
updateNode: function() {
// ...
},
deleteNode: function() {
// ...
}
});

ComputedProperty doesn't get updated

I have this test application which should print "filtered" and "changed" each time the applications view is clicked, because a computed property is called. However the property binding is only triggered when updating the property with an empty array:
window.App = Ember.Application.create({
Settings: Ember.Object.create({
filter: []
}),
ApplicationView: Ember.View.extend({
click: function(event) {
filter = App.get('Settings.filter');
console.dir(filter);
if (App.get('Room.filtered')) {
filter = filter.filter(function(item) {
return item != App.get('Room.name');
});
} else {
filter.push(App.get('Room.name'));
}
App.set('Settings.filter', filter);
}
})
});
Room = Ember.Object.extend({
name: "test",
_filterer: function() {
console.dir("changed");
}.observes('filtered'),
filtered: function() {
console.dir("filtered");
filter = App.get('Settings.filter');
for (var i = 0; i < filter.length; i++) {
if (filter[i] == this.get('name')) return true;
}
return false;
}.property('App.Settings.filter', 'name').volatile()
});
App.Room = Room.create();
setTimeout(function() {App.set('Settings.filter', ['test']); }, 3000);
Here is the jsbin: http://jsbin.com/edolol/2/edit
Why is the property binding only triggered when setting the Setting to an empty array? And why does it trigger when doing it manually in the timeout?
Update
Here is a working jsbin: http://jsbin.com/edolol/11/edit
When you're going to add/remove items from an array, and not change the entire array, you need to inform the computed property to observe the items inside the array, not only the array itself:
The syntax is:
function() {
}.property('App.settings.filter.#each')
The reason it was working with setTimeout is because you were replacing the entire array instead of the items inside it.
I fixed your jsbin: http://jsbin.com/edolol/10/edit
I fixed some minor other stuff such as filter.push is now filter.pushObject (Using Ember's MutableArray).
And after changing the filter array (filter = filter.filter()) you need to set the new filter variable as the property: App.set('Settings.filter', filter);
The Problem is that I have used .push() to add to App.Settings.filter and .filter() to remove from it. The first approach does not create a new array, the latter does. Thats why removing from that array has worked, but not adding.
I assume that using Ember.ArrayProxy and an observer for .#each would have worked. But thats out of my knowledge. This little problem is solved by just creating a new array though.
filter = App.get('Settings.filter').slice(0);

How to call Controllers method by name in new (ember-1.0.0-pre.4) version

I used Ember.ready function to add some keyboard binding like this https://github.com/greggyNapalm/firebat-overlord/blob/master/web_static/static/js/app/app.js#L24 is there any proper way to do this in new version, asking because can't anymore call method by its name?
The best place to do this sort of thing is from the view. Based on your example the appropriate view in this case is probably TestsView. From there you can bind and unbind keyboard bindings when the view is inserted/removed. For example:
var TestsView = Ember.View.extend({
templateName: 'testsTemplate',
didInsertElement: function() {
console.log("Controller: " + this.get('controller').toString());
var self = this;
Mousetrap.bind('ctrl+right', function() {
self.get('controller').goToPage('next');
});
},
wilLRemoveElement: function() {
Mousetrap.unbind('ctrl+right');
}
});
To see what controller is set to, try adding console.log("Controller: " + self.get('controller').toString());