I setting up a scenario very similar to the Editable Row example from the x-editable demo site. In this scenario, a there is a simple table with three columns for data and a fourth for edit and delete buttons. A third button outside of the table adds a row to the table. When the form is editable, the data columns become editable (the primary feature of x-editable library). For this demo, the first column becomes a simple text edit and the second two columns become drop lists.
我设置的场景非常类似于来自x-editable演示站点的可编辑行示例。在这个场景中,有一个简单的表,其中有三列表示数据,第四列表示编辑和删除按钮。表外的第三个按钮向表添加一行。当表单可编辑时,数据列可编辑(x-editable库的主要特性)。对于这个演示,第一列变成了一个简单的文本编辑,第二列变成了下拉列表。
The table is created by having an ng-repeat on a row template. I need to do a few different things that all involve accessing the scope created by the ng-repeat. I need to
这个表是通过在行模板上使用一个ng重复来创建的。我需要做一些不同的事情,这些事情都涉及到访问ng-repeat创建的范围。我需要
- detect when the row is editable and when it is not
- 检测何时该行是可编辑的,何时不是。
- filter the options for the second drop list when the first drop list changes
- 当第一个删除列表改变时,过滤第二个删除列表的选项
In order to try to work with this demo, I've added a controller for the individual row. That has given me some access to the form (name = rowform), but I'm still not able to set a watch on the "make" property. I can't even find what property of the form is changing when the user makes a selection.
为了尝试使用这个演示,我为每一行添加了一个控制器。这给了我一些对表单的访问权限(name = rowform),但是我仍然不能在“make”属性上设置监视。当用户进行选择时,我甚至找不到表单的哪个属性正在改变。
How do I set up a watch on the 'make' property?
如何在“make”属性上设置手表?
Page Controller
页面控制器
angular.module('app').controller("quoteBuckingRaterController",
function ($scope, $q, $filter, listService, transactionDataService) {
$scope.equipment = [];
$scope.makes = [];
$scope.models = [];
$scope.showModel = function(equip) {
if(equip.model) {
var selected = $filter('filter')($scope.models, {id: equip.model});
return selected.length ? selected[0].name : 'Not set';
} else {
return 'Not set';
}
};
$scope.showMake = function(equip) {
if (equip.model) {
var selected = $filter('filter')($scope.models, { id: equip.model });
if (selected.length && selected.length > 0) {
if (equip.make != selected[0].make)
equip.make = selected[0].make;
return selected[0].make;
}
else {
return 'Not set';
}
} else {
return 'Not set';
}
};
$scope.checkName = function (data, id) {
if (!data) {
return "Description is required";
}
};
$scope.checkModel = function (data, id) {
if (!data) {
return "Model is required";
}
};
$scope.saveEquipment = function (data, id) {
$scope.inserted = null;
};
$scope.cancelRowEdit = function (data, id) {
$scope.inserted = null;
};
$scope.removeEquipment = function(index) {
$scope.equipment.splice(index, 1);
};
$scope.addEquipment = function() {
$scope.inserted = {
id: $scope.equipment.length+1,
name: '',
make: null,
model: null
};
$scope.equipment.push($scope.inserted);
};
$scope.filterModels = function (make) {
$scope.models = _.where($scope.allModels, function(item) {
return item.make == make;
});
};
//called by another process when page loads
$scope.initialize = function (loaded) {
return $q(function (resolve, reject) {
if (!loaded) {
listService.getEquipmentModels().then(function (data) {
$scope.allModels = data;
$scope.models = data;
//uses underscore.js
$scope.makes = _.chain(data)
.map(function (item) {
var m = {
id: item.make,
name: item.make
};
return m;
})
.uniq()
.value();
resolve();
});
}
});
}
});
Row Controller
行控制器
angular.module('app').controller("editRowController",
function ($scope) {
$scope.testClick = function () {
alert('button clicked');
};
$scope.make = null;
$scope.$watch('make', function () {
alert('how do I tell when the make has been changed?');
this.$parent.$parent.filterModels(make.id);
});
});
HTML
HTML
<div>
<div class="col-md-12" style="margin-bottom: 3px">
<div class="col-md-4 col-md-offset-1" style="padding-top: 6px; padding-left: 0px"><label>Equipment</label></div>
<div class="col-md-offset-10">
<button class="btn btn-primary btn-sm" ng-click="addEquipment()">Add row</button>
</div>
</div>
<div class="col-md-10 col-md-offset-1">
<table class="table table-bordered table-hover table-condensed">
<tr style="font-weight: bold; background-color: lightblue">
<td style="width:35%">Name</td>
<td style="width:20%">Make</td>
<td style="width:20%">Model</td>
<td style="width:25%">Edit</td>
</tr>
<tr ng-repeat="equip in equipment" ng-controller="editRowController">
<td>
<!-- editable equip name (text with validation) -->
<span editable-text="equip.name" e-name="name" e-form="rowform" onbeforesave="checkName($data, equip.id)" e-required>
{{ equip.name || 'empty' }}
</span>
</td>
<td>
<!-- editable make (select-local) -->
<span editable-select="equip.make" e-name="make" e-form="rowform" e-ng-options="s.value as s.name for s in makes">
{{ showMake(equip) }}
</span>
</td>
<td>
<!-- editable model (select-remote) -->
<span editable-select="equip.model" e-name="model" e-form="rowform" e-ng-options="g.id as g.name for g in models" onbeforesave="checkModel($data, equip.id)" e-required>
{{ showModel(equip) }}
</span>
<button type="button" ng-disabled="rowform.$waiting" ng-click="testClick()" class="btn btn-default">
test
</button>
</td>
<td style="white-space: nowrap">
<!-- form -->
<form editable-form name="rowform" onbeforesave="saveEquipment($data, equip.id)" ng-show="rowform.$visible" class="form-buttons form-inline" shown="inserted == equip">
<button type="submit" ng-disabled="rowform.$waiting" class="btn btn-primary">
save
</button>
<button type="button" ng-disabled="rowform.$waiting" ng-click="rowform.$cancel()" class="btn btn-default">
cancel
</button>
</form>
<div class="buttons" ng-show="!rowform.$visible">
<button class="btn btn-primary" ng-click="rowform.$show()">edit</button>
<button class="btn btn-danger" ng-click="removeEquipment($index)">del</button>
</div>
</td>
</tr>
</table>
</div>
</div>
3 个解决方案
#1
5
ng-repeat
creates a child scope for each row (for each equipment
). The scope of the EditRowController
is therefore a childScope of the parent quoteBuckingRaterController
.
ng-repeat为每一行(每个设备)创建子范围。EditRowController的范围因此是其父quotebuckingham gratercontroller的子范围。
This childScope contains:
这个childScope包含:
- all properties of the parent scope (e.g.
equipment
,makes
,models
) - 父范围的所有属性(例如,设备、制作、模型)
- the property
equip
with one value of theequipment
array, provided by ng-repeat - 该属性具有由ng-repeat提供的设备阵列的一个值
- any additional scope property that is defined inside the ng-repeat block, e.g.
rowform
- 在ng-repeat块中定义的任何附加的范围属性,例如rowform。
Therefore you are able to access these properties in the childController editRowController
using the $scope variable, e.g.:
因此,您可以使用$scope变量访问childController editRowController中的这些属性,例如:
$scope.equip.make
$scope.equipment
and inside the ng-repeat
element in the html file by using an angular expression, e.g:
在html文件的ng-repeat元素中使用一个角表达式,例如:
{{equip.make}}
{{equipment}}
Now to $scope.$watch
: If you provide a string as the first argument, this is an angular expression like in the html file, just without surrounding brackets {{}}
. Example for equip.make
:
现在美元范围。$watch:如果您提供一个字符串作为第一个参数,这是一个角表达式,就像在html文件中一样,只是不包含括号{}。equip.make的例子:
$scope.$watch('equip.make', function (value) {
console.log('equip.make value (on save): ' + value);
});
However, angular-xeditable updates the value of equip.make only when the user saves the row. If you want to watch the user input live, you have to use the $data property in the rowform object, provided by angular-xeditable:
然而,angular-xeditable只在用户保存行时更新设备的值。如果想实时查看用户输入,必须使用rowform对象中的$data属性,由angular-xeditable提供:
$scope.$watch('rowform.$data.make', function (value) {
console.log('equip.make value (live): ' + value);
}, true);
You can also use ng-change:
你也可以使用ng-change:
<span editable-select="equip.make" e-name="make" e-ng-change="onMakeValueChange($data)" e-form="rowform" e-ng-options="s.value as s.name for s in makes">
JS:
JS:
$scope.onMakeValueChange = function(newValue) {
console.log('equip.make value onChange: ' + newValue);
}
That should solve your first question: How to watch the make
property.
这应该可以解决你的第一个问题:如何监视make属性。
Your second question, how to detect when the row is editable and when it is not, can be solved by using the onshow / onhide attributes on the form or by watching the $visible property of the rowform
object in the scope as documented in the angular-xeditable reference
您的第二个问题是,如何检测行何时可编辑,何时不可编辑,可以通过使用表单上的onshow / onhide属性来解决,也可以通过查看angular-xeditable引用中记录的rowform对象的$visible属性来解决
<form editable-form name="rowform" onshow="setEditable(true)" onhide="setEditable(false)">
$scope.setEditable = function(value) {
console.log('is editable? ' + value);
};
// or
$scope.$watch('rowform.$visible', function(value) {
console.log('is editable? ' + value);
});
You might ask why the rowform object is in the current childScope. It is created by the <form>
tag. See the Angular Reference about the built-in form directive:
您可能会问为什么rowform对象在当前的子作用域中。它是由
Directive that instantiates FormController.
实例化FormController指令。
If the name attribute is specified, the form controller is published onto the current scope under this name.
如果指定了name属性,表单控制器将在此名称下发布到当前范围。
A working snippet with your example code:
使用示例代码的工作代码片段:
angular.module('app', ["xeditable"]);
angular.module('app').controller("editRowController", function ($scope) {
$scope.testClick = function () {
alert('button clicked');
};
$scope.$watch('equip.make', function (value) {
console.log('equip.make value (after save): ' + value);
});
$scope.$watch('rowform.$data.make', function (value) {
console.log('equip.make value (live): ' + value);
}, true);
// detect if row is editable by using onshow / onhide on form element
$scope.setEditable = function(value) {
console.log('is equip id ' + $scope.equip.id + ' editable? [using onshow / onhide] ' + value);
};
// detect if row is editable by using a watcher on the form property $visible
$scope.$watch('rowform.$visible', function(value) {
console.log('is equip id ' + $scope.equip.id + ' editable [by watching form property]? ' + value);
});
});
angular.module('app').controller("quoteBuckingRaterController", function ($scope, $filter) {
$scope.equipment = [];
$scope.makes = [{value: 1, name: 'Horst'}, {value: 2, name: 'Fritz'}];
$scope.models = [{id: 1, name: 'PC', make: 1}];
$scope.showModel = function(equip) {
if(equip.model) {
var selected = $filter('filter')($scope.models, {id: equip.model});
return selected.length ? selected[0].name : 'Not set';
} else {
return 'Not set';
}
};
$scope.showMake = function(equip) {
if (equip.model) {
var selected = $filter('filter')($scope.models, { id: equip.model });
if (selected.length && selected.length > 0) {
if (equip.make != selected[0].make)
equip.make = selected[0].make;
return selected[0].make;
}
else {
return 'Not set';
}
} else {
return 'Not set';
}
};
$scope.checkName = function (data, id) {
if (!data) {
return "Description is required";
}
};
$scope.checkModel = function (data, id) {
if (!data) {
return "Model is required";
}
};
$scope.saveEquipment = function (data, id) {
$scope.inserted = null;
};
$scope.cancelRowEdit = function (data, id) {
$scope.inserted = null;
};
$scope.removeEquipment = function(index) {
$scope.equipment.splice(index, 1);
};
$scope.addEquipment = function() {
$scope.inserted = {
id: $scope.equipment.length+1,
name: '',
make: null,
model: null
};
$scope.equipment.push($scope.inserted);
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-xeditable/0.1.9/js/xeditable.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/angular-xeditable/0.1.9/css/xeditable.css" rel="stylesheet"/>
<link href="http://netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet"/>
<div ng-app="app" ng-controller="quoteBuckingRaterController">
<div class="col-md-12" style="margin-bottom: 3px">
<div class="col-md-4 col-md-offset-1" style="padding-top: 6px; padding-left: 0px"><label>Equipment</label></div>
<div class="col-md-offset-10">
<button class="btn btn-primary btn-sm" ng-click="addEquipment()">Add row</button>
</div>
</div>
<div class="col-md-10 col-md-offset-1">
<table class="table table-bordered table-hover table-condensed">
<tr style="font-weight: bold; background-color: lightblue">
<td style="width:35%">Name</td>
<td style="width:20%">Make</td>
<td style="width:20%">Model</td>
<td style="width:25%">Edit</td>
</tr>
<tr ng-repeat="equip in equipment" ng-controller="editRowController">
<td>
<!-- editable equip name (text with validation) -->
<span editable-text="equip.name" e-name="name" e-form="rowform" onbeforesave="checkName($data, equip.id)" e-required>
{{ equip.name || 'empty' }}
</span>
</td>
<td>
<!-- editable make (select-local) -->
<span editable-select="equip.make" e-name="make" e-form="rowform" e-ng-options="s.value as s.name for s in makes">
{{ showMake(equip) }}
</span>
</td>
<td>
<!-- editable model (select-remote) -->
<span editable-select="equip.model" e-name="model" e-form="rowform" e-ng-options="g.id as g.name for g in models" onbeforesave="checkModel($data, equip.id)" e-required>
{{ showModel(equip) }}
</span>
<button type="button" ng-disabled="rowform.$waiting" ng-click="testClick()" class="btn btn-default">
test
</button>
</td>
<td style="white-space: nowrap">
<!-- form -->
<form editable-form name="rowform" onbeforesave="saveEquipment($data, equip.id)" ng-show="rowform.$visible" class="form-buttons form-inline" shown="inserted == equip" onshow="setEditable(true)" onhide="setEditable(false)">
<button type="submit" ng-disabled="rowform.$waiting" class="btn btn-primary">
save
</button>
<button type="button" ng-disabled="rowform.$waiting" ng-click="rowform.$cancel()" class="btn btn-default">
cancel
</button>
</form>
<div class="buttons" ng-show="!rowform.$visible">
<button class="btn btn-primary" ng-click="rowform.$show()">edit</button>
<button class="btn btn-danger" ng-click="removeEquipment($index)">del</button>
</div>
</td>
</tr>
</table>
</div>
</div>
#2
2
If you simply want to $watch
the make
property of equipment
, try changing to:
如果您只是想要$watch设备的make属性,请尝试更改为:
$scope.$watch('equipment.make', function(){(...)})
#3
1
You could write your own directive for this.
你可以为此写你自己的指令。
The main advantage is that directives have isolated scope and can have their own controller.
主要优点是指令具有隔离的范围,并且可以有自己的控制器。
see the directive documentation to know if it's for you.
请参阅指导文档,了解它是否适合您。
#1
5
ng-repeat
creates a child scope for each row (for each equipment
). The scope of the EditRowController
is therefore a childScope of the parent quoteBuckingRaterController
.
ng-repeat为每一行(每个设备)创建子范围。EditRowController的范围因此是其父quotebuckingham gratercontroller的子范围。
This childScope contains:
这个childScope包含:
- all properties of the parent scope (e.g.
equipment
,makes
,models
) - 父范围的所有属性(例如,设备、制作、模型)
- the property
equip
with one value of theequipment
array, provided by ng-repeat - 该属性具有由ng-repeat提供的设备阵列的一个值
- any additional scope property that is defined inside the ng-repeat block, e.g.
rowform
- 在ng-repeat块中定义的任何附加的范围属性,例如rowform。
Therefore you are able to access these properties in the childController editRowController
using the $scope variable, e.g.:
因此,您可以使用$scope变量访问childController editRowController中的这些属性,例如:
$scope.equip.make
$scope.equipment
and inside the ng-repeat
element in the html file by using an angular expression, e.g:
在html文件的ng-repeat元素中使用一个角表达式,例如:
{{equip.make}}
{{equipment}}
Now to $scope.$watch
: If you provide a string as the first argument, this is an angular expression like in the html file, just without surrounding brackets {{}}
. Example for equip.make
:
现在美元范围。$watch:如果您提供一个字符串作为第一个参数,这是一个角表达式,就像在html文件中一样,只是不包含括号{}。equip.make的例子:
$scope.$watch('equip.make', function (value) {
console.log('equip.make value (on save): ' + value);
});
However, angular-xeditable updates the value of equip.make only when the user saves the row. If you want to watch the user input live, you have to use the $data property in the rowform object, provided by angular-xeditable:
然而,angular-xeditable只在用户保存行时更新设备的值。如果想实时查看用户输入,必须使用rowform对象中的$data属性,由angular-xeditable提供:
$scope.$watch('rowform.$data.make', function (value) {
console.log('equip.make value (live): ' + value);
}, true);
You can also use ng-change:
你也可以使用ng-change:
<span editable-select="equip.make" e-name="make" e-ng-change="onMakeValueChange($data)" e-form="rowform" e-ng-options="s.value as s.name for s in makes">
JS:
JS:
$scope.onMakeValueChange = function(newValue) {
console.log('equip.make value onChange: ' + newValue);
}
That should solve your first question: How to watch the make
property.
这应该可以解决你的第一个问题:如何监视make属性。
Your second question, how to detect when the row is editable and when it is not, can be solved by using the onshow / onhide attributes on the form or by watching the $visible property of the rowform
object in the scope as documented in the angular-xeditable reference
您的第二个问题是,如何检测行何时可编辑,何时不可编辑,可以通过使用表单上的onshow / onhide属性来解决,也可以通过查看angular-xeditable引用中记录的rowform对象的$visible属性来解决
<form editable-form name="rowform" onshow="setEditable(true)" onhide="setEditable(false)">
$scope.setEditable = function(value) {
console.log('is editable? ' + value);
};
// or
$scope.$watch('rowform.$visible', function(value) {
console.log('is editable? ' + value);
});
You might ask why the rowform object is in the current childScope. It is created by the <form>
tag. See the Angular Reference about the built-in form directive:
您可能会问为什么rowform对象在当前的子作用域中。它是由
Directive that instantiates FormController.
实例化FormController指令。
If the name attribute is specified, the form controller is published onto the current scope under this name.
如果指定了name属性,表单控制器将在此名称下发布到当前范围。
A working snippet with your example code:
使用示例代码的工作代码片段:
angular.module('app', ["xeditable"]);
angular.module('app').controller("editRowController", function ($scope) {
$scope.testClick = function () {
alert('button clicked');
};
$scope.$watch('equip.make', function (value) {
console.log('equip.make value (after save): ' + value);
});
$scope.$watch('rowform.$data.make', function (value) {
console.log('equip.make value (live): ' + value);
}, true);
// detect if row is editable by using onshow / onhide on form element
$scope.setEditable = function(value) {
console.log('is equip id ' + $scope.equip.id + ' editable? [using onshow / onhide] ' + value);
};
// detect if row is editable by using a watcher on the form property $visible
$scope.$watch('rowform.$visible', function(value) {
console.log('is equip id ' + $scope.equip.id + ' editable [by watching form property]? ' + value);
});
});
angular.module('app').controller("quoteBuckingRaterController", function ($scope, $filter) {
$scope.equipment = [];
$scope.makes = [{value: 1, name: 'Horst'}, {value: 2, name: 'Fritz'}];
$scope.models = [{id: 1, name: 'PC', make: 1}];
$scope.showModel = function(equip) {
if(equip.model) {
var selected = $filter('filter')($scope.models, {id: equip.model});
return selected.length ? selected[0].name : 'Not set';
} else {
return 'Not set';
}
};
$scope.showMake = function(equip) {
if (equip.model) {
var selected = $filter('filter')($scope.models, { id: equip.model });
if (selected.length && selected.length > 0) {
if (equip.make != selected[0].make)
equip.make = selected[0].make;
return selected[0].make;
}
else {
return 'Not set';
}
} else {
return 'Not set';
}
};
$scope.checkName = function (data, id) {
if (!data) {
return "Description is required";
}
};
$scope.checkModel = function (data, id) {
if (!data) {
return "Model is required";
}
};
$scope.saveEquipment = function (data, id) {
$scope.inserted = null;
};
$scope.cancelRowEdit = function (data, id) {
$scope.inserted = null;
};
$scope.removeEquipment = function(index) {
$scope.equipment.splice(index, 1);
};
$scope.addEquipment = function() {
$scope.inserted = {
id: $scope.equipment.length+1,
name: '',
make: null,
model: null
};
$scope.equipment.push($scope.inserted);
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-xeditable/0.1.9/js/xeditable.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/angular-xeditable/0.1.9/css/xeditable.css" rel="stylesheet"/>
<link href="http://netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet"/>
<div ng-app="app" ng-controller="quoteBuckingRaterController">
<div class="col-md-12" style="margin-bottom: 3px">
<div class="col-md-4 col-md-offset-1" style="padding-top: 6px; padding-left: 0px"><label>Equipment</label></div>
<div class="col-md-offset-10">
<button class="btn btn-primary btn-sm" ng-click="addEquipment()">Add row</button>
</div>
</div>
<div class="col-md-10 col-md-offset-1">
<table class="table table-bordered table-hover table-condensed">
<tr style="font-weight: bold; background-color: lightblue">
<td style="width:35%">Name</td>
<td style="width:20%">Make</td>
<td style="width:20%">Model</td>
<td style="width:25%">Edit</td>
</tr>
<tr ng-repeat="equip in equipment" ng-controller="editRowController">
<td>
<!-- editable equip name (text with validation) -->
<span editable-text="equip.name" e-name="name" e-form="rowform" onbeforesave="checkName($data, equip.id)" e-required>
{{ equip.name || 'empty' }}
</span>
</td>
<td>
<!-- editable make (select-local) -->
<span editable-select="equip.make" e-name="make" e-form="rowform" e-ng-options="s.value as s.name for s in makes">
{{ showMake(equip) }}
</span>
</td>
<td>
<!-- editable model (select-remote) -->
<span editable-select="equip.model" e-name="model" e-form="rowform" e-ng-options="g.id as g.name for g in models" onbeforesave="checkModel($data, equip.id)" e-required>
{{ showModel(equip) }}
</span>
<button type="button" ng-disabled="rowform.$waiting" ng-click="testClick()" class="btn btn-default">
test
</button>
</td>
<td style="white-space: nowrap">
<!-- form -->
<form editable-form name="rowform" onbeforesave="saveEquipment($data, equip.id)" ng-show="rowform.$visible" class="form-buttons form-inline" shown="inserted == equip" onshow="setEditable(true)" onhide="setEditable(false)">
<button type="submit" ng-disabled="rowform.$waiting" class="btn btn-primary">
save
</button>
<button type="button" ng-disabled="rowform.$waiting" ng-click="rowform.$cancel()" class="btn btn-default">
cancel
</button>
</form>
<div class="buttons" ng-show="!rowform.$visible">
<button class="btn btn-primary" ng-click="rowform.$show()">edit</button>
<button class="btn btn-danger" ng-click="removeEquipment($index)">del</button>
</div>
</td>
</tr>
</table>
</div>
</div>
#2
2
If you simply want to $watch
the make
property of equipment
, try changing to:
如果您只是想要$watch设备的make属性,请尝试更改为:
$scope.$watch('equipment.make', function(){(...)})
#3
1
You could write your own directive for this.
你可以为此写你自己的指令。
The main advantage is that directives have isolated scope and can have their own controller.
主要优点是指令具有隔离的范围,并且可以有自己的控制器。
see the directive documentation to know if it's for you.
请参阅指导文档,了解它是否适合您。