Ember - how to create and bind a Checkbox controller? - ember.js

This question is linked to the answer given here.
Having a checkbox in a view
App.RoleCheckbox = Em.Checkbox.extend({
userRolesBinding: 'parentView.user.roles', // Points to the roles of the user
checked: function () {
var userRoles = this.get('userRoles');
return userRoles.contains(this.get('content'));
}.property('content', 'userRoles.#each'),
click: function (evt) {
//do something
var controller = this.get("controller");
controller.clicked(evt);
}
});
I would like that the click function calls the clicked function from the RoleCheckboxController:
App.RoleCheckboxController = Em.Controller.extend({
clicked: function(evt){
//Really do the thing
}
});
But this does not work. How could I fix this ?
JSFiddle: http://jsfiddle.net/3fMpD/

You can instantiate and associate the controller to the view using the correct naming conventions.
For example, this would associate the controller to the view:
// Instead of App.RoleCheckBoxController
App.ApplicationController = Ember.Controller.extend( /* ... */ );
App.ApplicationView = Ember.View.extend( /* .. */ );
JSFiddle: http://jsfiddle.net/YL5rQ/

#c4p is definitely right and the problem there is that your controller is not being created, and furthermore App.RoleCheckbox has no way of knowing it should use App.RoleCheckboxController as its controller.
I am not quite sure if this is the most Ember-y way of doing this but you can set the controller in the init (constructor function) of the Checkbox view, and then just make sure you send to the controller all the properties it needs to work with:
App.RoleCheckbox = Em.Checkbox.extend({
init: function(){
this._super();
this.set('controller', new App.RoleController());
},
userRolesBinding: 'parentView.user.roles',
checked: function () {
var userRoles = this.get('userRoles');
return userRoles.contains(this.get('content'));
}.property('content', 'userRoles.#each'),
click: function (evt) {
this.get('controller').send('clicked',this.checked, this.content);
}
});
And the controller's code (just changing the parameters used in the function);
App.RoleCheckboxController = Em.ObjectController.extend({
clicked: function(checked,role){
var userRoles = App.User.roles;
console.log("userRoles = ", userRoles);
console.log("role = ", role);
console.log("will be: ", !checked ? "removed" : "added");
if (checked) {
userRoles.pushObject(role);
} else {
userRoles.removeObject(role);
}
console.log("updated userRoles = ", userRoles);
}
});
Working fiddle here: http://jsfiddle.net/cfSwq/3/
Hope this helps!

Your App.RoleCheckboxController is never created. The way you have things set up there will only be an instance of ApplicationController.
You can move the logic back into the view's click event to have everything work:
App.RoleCheckbox = Em.Checkbox.extend({
userRolesBinding: 'parentView.user.roles',
checked: function () {
var userRoles = this.get('userRoles');
return userRoles.contains(this.get('content'));
}.property('content', 'userRoles.#each'),
click: function (evt) {
console.log("event triggered:", evt);
//var controller = this.get("controller");
//controller.clicked(evt);
var isPresent = this.get('checked'),
userRoles = this.get('userRoles'),
role = this.get('content');
console.log("userRoles = ", userRoles);
console.log("role = ", role);
console.log("will be: ", isPresent ? "removed" : "added");
if (!isPresent) {
userRoles.pushObject(role);
} else {
userRoles.removeObject(role);
}
}
});
Updated JSFiddle

Related

How do I send a object into the Jquery $('calendar') selector from outside

In the below code, in the didInsertElement, I want to send myController object in to the JQuery Selector ('#calendar').How can I do this. Is it possible to do this. This might be easy. Any one please
(function () {
App.EventDisplayCalendarDisplayView = Ember.View.extend({
didInsertElement: function () {
//I got the controller object here, now I want to pass this in to the JQuery calendar selector below.
// How to do this
var myController = this.get('controller');
$('#calendar').fullCalendar({
weekends: true,
events: this.get('controller'),
dayClick: function(start, allDay, jsEvent, view) {
//I want to access the controller here how to do this.
var controller = myController; //This does not work.
},
eventLimit: true, // for all non-agenda views
views: {
agenda: {
eventLimit: 6 // adjust to 6 only for agendaWeek/agendaDay
}
},
eventClick: function(xEvent, jsEvent, view) {
debugger;
alert("Title: " + xEvent.title //Get the event title
+ "\n StartTime: " + xEvent.start //Get the event start date
+ "\n EndTime: " + xEvent.end //Get the event end date
);
console.log(xEvent); //See your console for all event properties/objects
}
});
},
willDestroyElement: function () {
}
});
}());
Look into calling the function with .bind(variable): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

Delay ember view render till $getJSON isLoaded

The problem with this code is that the render code is entered twice, and the buffer is not where I expect it. Even when I get the buffer, the stuff I push in is not rendered to the screen.
App.FilterView = Ember.View.extend({
init: function() {
var filter = this.get('filter');
this.set('content', App.ViewFilter.find(filter));
this._super();
},
render: function(buffer) {
var content = this.get('content');
if(!this.get('content.isLoaded')) { return; }
var keys = Object.keys(content.data);
keys.forEach(function(item) {
this.renderItem(buffer,content.data[item], item);
}, this);
}.observes('content.isLoaded'),
renderItem: function(buffer, item, key) {
buffer.push('<label for="' + key + '"> ' + item + '</label>');
}
});
And the App.ViewFilter.find()
App.ViewFilter = Ember.Object.extend();
App.ViewFilter.reopenClass({
find: function(o) {
var result = Ember.Object.create({
isLoaded: false,
data: ''
});
$.getJSON("http://localhost:3000/filter/" + o, function(response) {
result.set('data', response);
result.set('isLoaded', true);
});
return result;
}
});
I am getting the data I expect and once isLoaded triggers, everything runs, I am just not getting the HTML in my browser.
As it turns out the answer was close to what I had with using jquery then() on the $getJSON call. If you are new to promises, the documentation is not entirely straight forward. Here is what you need to know. You have to create an object outside the promise - that you will return immediately at the end and inside the promise you will have a function that updates that object once the data is returned. Like this:
App.Filter = Ember.Object.extend();
App.Filter.reopenClass({
find: function(o) {
var result = Ember.Object.create({
isLoaded: false,
data: Ember.Object.create()
});
$.getJSON("http://localhost:3000/filter/" + o).then(function(response) {
var controls = Em.A();
var keys = Ember.keys(response);
keys.forEach(function(key) {
controls.pushObject(App.FilterControl.create({
id: key,
label: response[key].label,
op: response[key].op,
content: response[key].content
})
);
});
result.set('data', controls);
result.set('isLoaded', true);
});
return result;
}
});
Whatever the function inside then(), is the callback routine that will be called once the data is returned. It needs to reference the object you created outside the $getJSON call and returned immediately. Then this works inside the view:
didInsertElement: function() {
if (this.get('content.isLoaded')) {
var model = this.get('content.data');
this.createFormView(model);
}
}.observes('content.isLoaded'),
createFormView: function(data) {
var self = this;
var filterController = App.FilterController.create({ model: data});
var filterView = Ember.View.create({
elementId: 'row-filter',
controller: filterController,
templateName: 'filter-form'
});
self.pushObject(filterView);
},
You can see a full app (and bit more complete/complicated) example here

ContainerView childViews binding

Is it possible to bind the childViews property to one within the controller? Thus,
App.DashboardView = Ember.ContainerView.extend({
tagName: 'section',
childViewsBinding: 'controller.viewChildren',
...
});
And within the controller, a view object is dynamically created (qryView) and then appended to the controller's array:
this.get('viewChildren').pushObject(qryView.create());
I've been trying this but I don't see any change in the containerView's rendering after the array is populated.
Bryan
I found a way to do this, as binding didn't seem to work. I created an observer in the containerView:
App.DashboardView = Ember.ContainerView.extend({
tagName: 'section',
updChildViews: (function() {
var children, ths;
try {
ths = this;
children = this.get('controller.viewChildren');
children.forEach(function(chld) {
if (!ths.get('childViews').contains(chld)) {
ths.pushObject(chld);
}
});
} catch (e) {
console.error("DashboardView.updChildViews error:", e);
}
}).observes('controller.viewChildren.#each')

How do I call an action method on Controller from the outside, with the same behavior by clicking {{action}}

Please look at this code...
```
App.BooksRoute = Ember.Route.extend({
model: return function () {
return this.store.find('books');
}
});
App.BooksController = Ember.ArrayController.extend({
actions: {
updateData: function () {
console.log("updateData is called!");
var books = this.filter(function () {
return true;
});
for(var i=0; i<books.length; i++) {
//doSomething…
}
}
}
});
```
I want to call the updateData action on BooksController from the outside.
I tried this code.
App.__container__.lookup("controller:books").send('updateData');
It works actually. But, in the updateData action, the this is different from the one in which updateData was called by clicking {{action 'updateData'}} on books template.
In the case of clicking {{action 'updateData'}}, the this.filter() method in updateData action will return books models.
But, In the case of calling App.__container__.lookup("controller:books").send('updateData');, the this.filter() method in updateData action will return nothing.
How do I call the updateData action on BooksController from the outside, with the same behavior by clicking {{action 'updateData'}}.
I would appreciate knowing about it.
(I'm using Ember.js 1.0.0)
You can use either bind or jQuery.proxy. bind is provided in JS since version 1.8.5, so it's pretty safe to use unless you need to support very old browsers. http://kangax.github.io/es5-compat-table/
Either way, you're basically manually scoping the this object.
So, if you have this IndexController, and you wanted to trigger raiseAlert from outside the app.
App.IndexController = Ember.ArrayController.extend({
testValue : "fooBar!",
actions : {
raiseAlert : function(source){
alert( source + " " + this.get('testValue') );
}
}
});
With bind :
function externalAlertBind(){
var controller = App.__container__.lookup("controller:index");
var boundSend = controller.send.bind(controller);
boundSend('raiseAlert','External Bind');
}
With jQuery.proxy
function externalAlertProxy(){
var controller = App.__container__.lookup("controller:index");
var proxySend = jQuery.proxy(controller.send,controller);
proxySend('raiseAlert','External Proxy');
}
Interestingly this seems to be OK without using either bind or proxy in this JSBin.
function externalAlert(){
var controller = App.__container__.lookup("controller:index");
controller.send('raiseAlert','External');
}
Here's a JSBin showing all of these: http://jsbin.com/ucanam/1080/edit
[UPDATE] : Another JSBin that calls filter in the action : http://jsbin.com/ucanam/1082/edit
[UPDATE 2] : I got things to work by looking up "controller:booksIndex" instead of "controller:books-index".
Here's a JSBin : http://jsbin.com/ICaMimo/1/edit
And the way to see it work (since the routes are weird) : http://jsbin.com/ICaMimo/1#/index
This solved my similar issue
Read more about action boubling here: http://emberjs.com/guides/templates/actions/#toc_action-bubbling
SpeedMind.ApplicationRoute = Ember.Route.extend({
actions: {
// This makes sure that all calls to the {{action 'goBack'}}
// in the end is run by the application-controllers implementation
// using the boubling action system. (controller->route->parentroutes)
goBack: function() {
this.controllerFor('application').send('goBack');
}
},
};
SpeedMind.ApplicationController = Ember.Controller.extend({
actions: {
goBack: function(){
console.log("This is the real goBack method definition!");
}
},
});
You could just have the ember action call your method rather than handling it inside of the action itself.
App.BooksController = Ember.ArrayController.extend({
actions: {
fireUpdateData: function(){
App.BooksController.updateData();
}
},
// This is outside of the action
updateData: function () {
console.log("updateData is called!");
var books = this.filter(function () {
return true;
});
for(var i=0; i<books.length; i++) {
//doSomething…
}
}
});
Now whenever you want to call updateData(), just use
App.BooksController.updateData();
Or in the case of a handlebars file
{{action "fireUpdateData"}}

How can I programmatically add/remove models to a controller?

This shouldn't be too hard.
I have a datepicker UI widget, and each time the user clicks on a month, I want to add or remove that month from the MonthsController (an ArrayController). The MonthsController is not associated with a route, so in my ApplicationTemplate I simply have
{{render months}}
A simplified version of my datepicker view is
App.DatepickerView = Ember.View.extend({
click: function(e) {
var id = $(this).datepicker().data('date').replace(" ", "-");
this.get('controller.controllers.months').toggleMonth(id);
}
});
and I handle the event in my MonthsController:
App.MonthsController = Ember.ArrayController.extend({
toggleMonth: function(id) {
var month = App.Month.find(id),
index = this.indexOf(month);
if (index === -1) {
this.pushObject(month);
} else {
this.removeAt(index);
}
}
});
I thought I had this working, but then I realized that month in the last snippet wasn't really an App.Month, it was just (I suppose) an anonymous object.
How can I programmatically add/remove models to a controller?
Your App.Month.find(id) will return a promise. If that month hasn't loaded yet you would also be loading this data from the server. You need to wrap your code in the promise's then.
toggleMonth: function(id) {
var _this = this;
App.Month.find(id).then(function(month) {
var index = _this.indexOf(month);
if (index === -1) {
_this.pushObject(month);
} else {
_this.removeAt(index);
}
});
}