I am having a problem adding an array controller as an item controller of another array controller.
Error I am getting is:
Error while loading route: TypeError {} ember.min.js:15
Uncaught TypeError: Object # has no method 'addArrayObserver'
JSFiddle: http://jsfiddle.net/t5Uyr/3/
Here is my HTML:
<script type="text/x-handlebars">
<table>
<thead>
<tr>
<th>id</th>
<th>items</th>
</tr>
</thead>
<tbody>
{{#each}}
<tr>
<td>{{id}}</td>
<td>
<ul>
{{#each items}}
<li>{{formattedName}}</li>
{{/each}}
</ul>
</td>
</tr>
{{/each}}
</tbody>
</table>
</script>
As you can see, inside the template I iterate over a collection of data with each loop, inside the each loop I want to iterate over a subcollection of the data.
Here is my JS code:
window.App = Ember.Application.create({});
App.ApplicationRoute = Ember.Route.extend({
model: function () {
var data = [
{
id: "111",
items: [
{
name: "foo"
},
{
name: "bar"
}
]
},
{
id: "222",
items: [
{
name: "hello"
},
{
name: "world"
}
]
}
];
return data;
}
});
App.ApplicationController = Ember.ArrayController.extend({
itemController: "row"
});
App.RowController = Ember.ArrayController.extend({
itemController: "item"
});
App.ItemController = Ember.ObjectController.extend({
formattedName: function () {
return "My name is " + this.get("name");
}.property("name")
});
App.RowController should be an objectController your items (rows) are objects with an array in one of their properties and not arrays themselves...
You can assing the controller in the inner each directly and remove itemController from the App.RowController.
JavaScript
App.RowController = Ember.ObjectController.extend()
Handlebars
{{each items itemController='item'}}
JsFiddle http://jsfiddle.net/mUJAa/3/
Related
I want to check all table rows and if all values are same alert('ok) in Ember.js
Here is my Code.
actions:{
checkValue: function(){
var r = this.$('.cellValue').text();
if(r == '|'){
console.log('ok');
}else{
console.log('bad');
}
}
}
I'd suggest you're going about this problem in a non-Ember way. Why don't you try something like this:
Component.js
import Ember from 'ember';
export default Ember.Component.extend({
rows: [
{name: 'bob', value: "24"},
{name: 'peter', value: "32"}
],
checkValue: Ember.observer('rows.#each.value', function () {
const unique = Ember.get(this, 'rows').uniqBy('value')
if (unique.length === 1) alert('matching')
})
});
Template.hbs
<table>
{{#each rows as |row|}}
<tr>
<td>{{row.name}}</td><td><input value={{row.value}} onblur={{action (mut row.value) value='target.value'}}></td>
</tr>
{{/each}}
</table>
There is a Twiddle here
I have an object with a couple dozens of settings, some settings depend on other settings, so, I need to observe if some setting changed.
import Ember from 'ember';
export default Ember.Controller.extend({
allPermissionChanged: function () {
alert('!');
}.observes('hash.types.[].permissions'),
permissionsHash: {
orders:{
types: [
{
label: 'All',
permissions: {
view: true,
edit: false,
assign: false,
"delete": false,
create: true
}
},
}
],
permissions:[
{
label:'Просмотр',
code:'view'
},
{
label:'Редактирование',
code:'edit'
},
{
label:'Распределение',
code:'assign'
},
{
label:'Удаление',
code:'delete'
},
{
label:'Создание',
code:'create'
}
]
}
}
});
Next I try to bind each setting to input
<table class="table table-bordered">
<thead>
<tr>
{{#each hash.types as |type|}}
<th colspan="2">{{type.label}}</th>
{{/each}}
</tr>
</thead>
<tbody>
{{#each hash.permissions as |perm|}}
<tr>
{{#each hash.types as |type|}}
{{#if (eq (mut (get type.permissions perm.code)) null)}}
<td> </td>
<td> </td>
{{else}}
<td>{{perm.label}}</td>
<td>{{input type="checkbox" checked=(mut (get type.permissions perm.code)) }}</td>
{{/if}}
{{/each}}
</tr>
{{/each}}
</tbody>
</table>
But observer doesn't work.
Also I prepared Jsbin example - http://emberjs.jsbin.com/havaji/1/edit?html,js,output
You are using the wrong syntax for that. hash.types.[] should only be used if you want to observe an actual array, when something is added or removed from it. To observe a property in an array you you hash.types.#each.permissions.
allPermissionChanged: function () {
alert('!');
}.observes('hash.types.#each.permissions')
You can read more about it in the Ember Guides.
You could change booleans to objects with boolean property so you could properly observe value of checkbox.
Controller:
App.IndexController = Ember.Controller.extend({
testData: Ember.ArrayProxy.create({
content: [
{ value: true },
{ value: false },
{ value: true }
]
}),
//...
Template:
{{input type='checkbox' checked=data.value}}
Observer:
arrayChanged: Ember.observer('testData.#each.value', function () {
console.log('changed');
})
Working demo.
My data model is as given below.
Module
Fields (ObservableArray)
Actions(ObservableArray)
Fields (?)
name (dyanmic from field list)
type (dynamic from field list)
selected (entered by user in UI)
Module is main object. Fields and Actions are observable arrays. Field lists under each action needs to be have updated field list and also will have an additional property which is captured from UI.
How the Fields under action model should be populated? Fields list under each action will have unique value for selected field.
Do I need to subscribe to fields ObservableArray and manipulate the Fields list under each action manually or is there any other better way doing this?
This is how I handle this situation
http://plnkr.co/edit/sWVqrFHdzWUXob42xS7Z?p=preview
Javascript
var childObject = function(data){
var self = this;
//No special mapping needed here, the mapping plugin does it for us
ko.mapping.fromJS(data, {}, self);
this.Select = function(){
self.selected(!self.selected());
};
};
var parentObject = function(data){
var self = this;
//Map the object to myself, using the mapping object we made earlier
ko.mapping.fromJS(data, {}, self);
//Remap the actions column to observable's
this.Actions = ko.observableArray(_.map(self.Actions(), function(item){
return new childObject(item);
}));
};
var myViewModel = function(){
var self = this;
this.RootObject = ko.observable();
var objectData = {
"Fields": [1, 2, 3, 4],
"Actions": [
{
"name": "David",
"type": "string",
"selected": false
},
{
"name": "Nish",
"type": "string",
"selected": true
}]
};
this.Init = function(){
//Pass the object data to the parent object.
self.RootObject(new parentObject(objectData))
};
};
$(function(){
myApp = new myViewModel();
myApp.Init();
ko.applyBindings(myApp);
})
Html
<div data-bind="with: RootObject">
<table>
<thead>
<tr>
<th>Name</th>
<th>Type</th>
<th>Selected</th>
</tr>
</thead>
<tbody data-bind="foreach: Actions">
<tr data-bind="click: Select">
<td data-bind="text: name"></td>
<td data-bind="text: type"></td>
<td data-bind="text: selected"></td>
</tr>
</tbody>
</table>
</div>
I'm trying to display the names of each department. I handmade a 'department' model based off of another model i made that does work. Despite them being identical, #each will not loop through the 'departments' and list them.
departments.hbs >
{{#each model}}
<tr>
<td>
{{#linkTo 'department' this}}{{this.departmentName}}{{/linkTo}}
</td>
<td>{{this.departmentName}}</td>
</tr>
{{/each}}
No errors. It just doesn't list the departments.
VpcYeoman.DepartmentsView = Ember.View.extend({
templateName: 'departments'});
VpcYeoman.DepartmentView = Ember.View.extend({
templateName: 'department'
});
VpcYeoman.DepartmentsController = Ember.ObjectController.extend({
// Implement your controller here.
});
VpcYeoman.Department = DS.Model.extend({
departmentName: DS.attr('string'),
departmentMembers: DS.attr('string')
});
VpcYeoman.Department.reopen({
// certainly I'm duplicating something that exists elsewhere...
attributes: function(){
var attrs = [];
var model = this;
Ember.$.each(Ember.A(Ember.keys(this.get('data'))), function(idx, key){
var pair = { key: key, value: model.get(key) };
attrs.push(pair);
});
return attrs;
}.property()
});
VpcYeoman.Department.FIXTURES = [
{
id: 0,
departmentName: "Sickness",
departmentMembers: "61"
},
{
id: 1,
departmentName: "Health",
departmentMembers: "69"
}
];
'department/#/' DOES work. Why is {{#each model}} not able to find the list of departments?
EDIT:
VpcYeoman.DepartmentsController = Ember.ArrayController.extend({
// Implement your controller here.
});
Upon entering {{log model}} before the {{#each model)) loop, I get this response:
[nextObject: function, firstObject: undefined, lastObject: undefined, contains: function, getEach: function…]
__ember1386699686611_meta: Meta
length: 0
__proto__: Array[0]
VpcYeoman.DepartmentsRoute = Ember.Route.extend({
renderTemplate: function() {
this.render();
}
});
VpcYeoman.DepartmentRoute = Ember.Route.extend({});
You need to declare a DepartmentsRoute with the following:
VpcYeoman.DepartmentsRoute = Ember.Route.extend({
model: function() {
return this.store.find('department');
}
});
DepartmentsController should probably be an ArrayController, and you can view the model in the console to validate it has something using ((log model)) before your each
You need to implement a model hook, returning the departments
VpcYeoman.DepartmentsRoute = Ember.Route.extend({
model: function(){
return this.store.find('department');
},
renderTemplate: function() {
this.render();
}
});
the department route is guessing based on the route name and implementing the default model hook.
I am trying to create a modular tabular form that takes an input of 1) an array of objects (the rows) and 2) an array of property names of those objects (the columns). Through these two arrays, it should retrieve properties that can be modified through Ember.TextFields in the form.
I cannot figure out how to do this. I can retrieve the values of the properties (as shown in the code below) but they are raw values, rather than references, so the bindings to these do not update the objects' properties.
View
App.SomeTabularForm = Em.View.extend({
template: <see below>,
things: [
Em.Object.create({ foo: 'a', bar: 'b' }),
Em.Object.create({ foo: 1, bar: 2 })
],
fieldNames: ['bar', 'foo'],
thingsWithFields: function() {
var fieldNames = this.get('fieldNames');
var thingWithFieldsProxy = Em.ObjectProxy.extend({
fields: function() {
var thing = this;
return fieldNames.map(function(fn) {
// FIX: this returns a raw value which is not bindable in a template
return thing.get(fn);
});
}.property()
});
return this.get('things').map(function(t) {
return thingWithFieldsProxy.create({ content: t });
});
}.property('things.[]', 'fields.[]')
});
Template
<table>
<tr>
{{#each view.fieldNames}}
<th>{{this}}</th>
{{/each}}
<tr>
{{#each view.thingsWithFields}}
<tr>
{{#each fields}}
<td>
{{! FIX: does not actually bind to thing's property }}
{{input type="text" valueBinding="this"}}
</td>
{{/each}}
</tr>
{{#each}}
</table>
You need to specify template name,
App.SomeTabularForm = Em.View.extend({
templateName: "mytemp",
....
the template
<script type="text/x-handlebars" data-template-name="mytemp">
{{blah}}
<table>
<tr>
{{#each view.fieldNames}}
<th>{{this}}</th>
{{/each}}....
http://jsfiddle.net/EdWG3/3/
If you want to use the template property,
App.SomeTabularForm = Em.View.extend({
template: Em.Handlebars.compile("<div>{{#each field in view.fieldNames}}{{field}}<br/>{{/each}}</div>"),
things: [...
http://jsfiddle.net/EdWG3/4/
If you use it in the context of routing then you need to modify the name of the view,
http://jsfiddle.net/EdWG3/2/
EDIT
In order to bind to the proxy objects
the template needs to modified as follows,
{{#each view.thingsWithFields}}
<tr>
<td>
{{! FIX: does not actually bind to thing's property }}
{{input type="text" valueBinding="content.bar"}}{{input type="text" valueBinding="content.foo"}}
</td>
</tr>
{{/each}}
http://jsfiddle.net/EdWG3/7/