Angular foreach - null values - javascript

I am having an issue where when I try to select multiple devices and add them to a group, the array of selected devices($scope.deviceIDs.push) is not getting the values. Can anyone see the issue or suggest a different method. I used http://www.dotnetawesome.com/2015/12/multiselect-dropdown-with-checkbox-in-angularjs.html as template for the service I built.
Here is the angular code.
var MyApp = angular.module('MyApp', ['ui.bootstrap', 'angularjs-dropdown-multiselect']);
MyApp.controller('GroupsController', ['GroupsService', '$scope', '$log', '$uibModal',
function (GroupsService, $scope, $log, $uibModal) {
$scope.groupSelected = [];
$scope.location = '';
$scope.groupguid = '';
$scope.newGroupName = '';
$scope.devicesNotinGroup = [];
$scope.newGroupAddMember = 'false';
$scope.isCollapsed = false;
$scope.groupSelected.GroupID = 0;
$scope.SelectedDevices = [];
$scope.deviceIDs = [];
$scope.dropdownSetting = {
scrollableHeight: '200px',
scrollable: true,
enableSearch: true
}
// Populate Jobs
GroupsService.GetDeviceList().then(function (d) {
$scope.GroupList = d.data;
console.log(d.data)
}, function (error) {
alert('Error!');
});
$scope.showChilds = function (item) {
$scope.grouplist = [];
item.active = !item.active;
console.log("here item=" + item.GroupName + " active=" + item.GroupName.active);
grouplist = item.SubGroup;
};
$scope.showInfoForGroup = function (item) {
console.log("item = " + item + "Count = " + item.DeviceCount);
GroupsService.GetGroupDeviceInformation(item).then(function (d) {
$scope.groupSelected = d.data.devicegroupitem;
//$scope.devicesNotinGroup = d.data.devicesnotingroup;
angular.forEach(d.data.devicesnotingroup, function (value, index) {
$scope.devicesNotinGroup.push({ label: value.HostName, id: value.HostName });
});
//console.log($scope.devicesNotinGroup)
})
}, function (error) {
console.log("item = " +item + "Count = " + groupSelected.DeviceCount);
};
$scope.DeleteDeviceFromGroup = function (deviceguid, groupguid ) {
console.log("DeviceGuid = " + deviceguid + " GroupGuid = " + groupguid);
GroupsService.DeleteDeviceFromList(deviceguid, groupguid).then(function (d) {
$scope.groupSelected = d.data;
})
}
$scope.AddDeviceToGroup = function (hostname, groupguid) {
GroupsService.AddDeviceToGroup(hostname, groupguid).then(function (d) {
$scope.showInfoForGroup(dt.guid);
//$scope.groupSelected = d.data.devicegroupitem;
//$scope.devicesNotinGroup = d.data.devicesnotingroup;
$scope.newGroupAddMember = 'false';
})
}
$scope.SubmitMultipleDevices = function (groupguid){
$scope.deviceIDs = [];
console.log($scope.SelectedDevices);
angular.forEach($scope.SelectedDevices = function (value) {
$scope.deviceIDs.push({ dname: value.HostName, dguid: groupguid } );
});
console.log('device ids ');
console.log($scope.deviceIDs);
var data = { deviceIDs: deviceIDs };
console.log(data);
angular.toJson(data);
GroupsService.SubmitMultiDevicesToGroup(data)
.success(function () {
})
.error(function (error) {
});
}
$scope.CreateGroup = function (groupID, groupName, newGroupAddMember) {
angular.isUndefinedOrNull = function (groupID) {
return angular.isUndefined(groupID) || groupID === null
}
$scope.GroupList = '';
console.log("check value equals" + groupID)
GroupsService.CreateSubGroup(groupID, groupName, newGroupAddMember).then(function (d) {
$scope.GroupList = d.data;
$scope.newGroupName = '';
$scope.newGroupAddMember = false;
})
}
$scope.DeleteGroup = function (groupID) {
$scope.GroupList = '';
GroupsService.DeleteSubGroup(groupID).then(function (d){
$scope.GroupList = d.data;
$scope.newGroupName = '';
})
}
}])
MyApp.factory('GroupsService', function ($http) { // explained about factory in Part2
var fac = {};
fac.GetDeviceList = function () {
return $http.get('/DeviceGroups/getgrouptree')
}
fac.GetGroupDeviceInformation = function (guid) {
return $http.get('/DeviceGroups/GetGroupDeviceInfo?groupguid=' + guid)
}
fac.DeleteDeviceFromList = function (deviceguid, groupguid) {
return $http.get('/DeviceGroups/DeleteDeviceFromGroup?deviceguid='+ deviceguid + "&groupguid=" + groupguid)
}
fac.AddDeviceToGroup = function (hostname, groupguid) {
return $http.get('/DeviceGroups/AddDeviceToGroup?hostname=' + hostname + "&groupguid=" + groupguid)
}
fac.CreateSubGroup = function (groupID, groupName, newGroupAddMember) {
return $http.get('/DeviceGroups/CreateGroup?GroupID=' + groupID + "&groupName=" + groupName + "&AddMember=" + newGroupAddMember)
}
fac.DeleteSubGroup = function (groupID) {
return $http.get('/DeviceGroups/DeleteGroup?GroupID=' + groupID)
}
fac.SubmitMultiDevicesToGroup = function (data) {
return $http.post('/DeviceGroups/AddMultipleDevicesToGroup', data)
}
return fac;
});
Here is the view code:
#model List<NetworkCafe.Models.DeviceGroup>
#{
ViewBag.Title = "Groups";
}
<style>
#clickable:hover {
cursor: pointer;
}
.body-content{padding-top:50px}
.checkbox{padding:0;margin:0;}
.dropdown-menu{overflow:auto !important;}
.form-group div{display:inline-block; margin-right:10px}
</style>
<link rel="stylesheet" href="../Content/font-awesome.min.css">
<div class="container" style="width:90%" ng-app="MyApp" ng-controller="GroupsController">
<div class="col-lg-3">
<div ng-class="dropdown" class="panel panel-warning">
<div class="panel-heading">
Tree List of Groups
</div>
<div class="panel-body">
<script type="text/ng-template" id="tree-structure">
<span>
<span id="clickable" ng-class="{'glyphicon glyphicon-chevron-up':(!dt.AddMembers) && (!dt.active), 'glyphicon glyphicon-chevron-down':(!dt.AddMembers) && (dt.active), 'childElement':(dt.AddMembers)}" ng-click="showChilds(dt)"></span>
<span id="clickable" ng-click="showInfoForGroup(dt.guid)"> {{dt.GroupName}} </span>
</span>
<ul ng-if="dt.AddMembers">
#*<li>Device Count: {{dt.DeviceCount}}</li>
<li>Total Ports: {{dt.TotalPortCount}}</li>
<li>Open Ports: {{dt.OpenPortCount}}</li>
<li>Reserved Ports: {{dt.ReservedPortCount}}</li>
<li>Percent Used: {{dt.PercentUsed}}</li>*#
#*<li><button ng-click="href"</li>*#
</ul>
<ul style="list-style: none" ng-show="dt.active" class="childElement">
<li ng-repeat="dt in dt.SubGroup" ng-include="'tree-structure'">
</li>
</ul>
</script>
</div>
<ul style="list-style: none" ng-class="list-group-item" class="parentList">
<li ng-repeat="dt in GroupList" ng-include="'tree-structure'" style="list-style: none">
</li>
</ul>
</div>
</div>
<div class="col-lg-3">
<div class="panel panel-info">
<div class="panel-heading">
<h3 class="panel-title">Group {{groupSelected.GroupName}} Information</h3>
</div>
<div class="panel-body">
<span ng-hide="!groupSelected.AddMembers">
This group has devices and can not have subgroups.
</span>
<table class="table table-striped table-hover" ng-hide="!groupSelected.AddMembers">
<tr>
<td>Device Count: </td>
<td></td>
<td>{{groupSelected.DeviceCount}}</td>
</tr>
<tr>
<td>Total Ports: </td>
<td></td>
<td>{{groupSelected.TotalPortCount}}</td>
</tr>
<tr>
<td>Open Ports: </td>
<td></td>
<td>{{groupSelected.OpenPortCount}}</td>
</tr>
<tr>
<td>Reserved Ports: </td>
<td></td>
<td>{{groupSelected.ReservedPortCount}}</td>
</tr>
<tr>
<td>Percent Used:</td>
<td></td>
<td>{{groupSelected.PercentUsed}}</td>
</tr>
</table>
<span ng-hide="groupSelected.AddMembers">
This group has subgroups and can not have members.<br /><br />
<label>CREATE SUBGROUP</label><br />
<input ng-model="newGroupName" placeholder="Group Name" /><br />
<label>Group Type</label><br />
<input type="checkbox" ng-model="newGroupAddMember" ng-checked="!newGroupAddMember" />Group will be used for Device.<br />
<button type="submit" class="btn btn-success" ng-click="CreateGroup(groupSelected.GroupID, newGroupName, newGroupAddMember)">Submit</button><br />----------------------- <br />
</span>
<span>
<a class="btn btn-danger" ng-click="DeleteGroup(groupSelected.GroupID)">Danger Delete Group </a> <br />
Delete Group. This will delete all subgroups and devices under this group.
</span>
</div>
</div>
</div>
<div class="col-lg-6" ng-hide="!groupSelected.AddMembers">
<div class="panel panel-success">
<div class="panel-heading">
<h3 class="panel-title">Device List</h3>
</div>
<div class="panel-body">
<table class="table table-striped table-hover ">
<tr>
<th></th>
<th>Device Name</th>
<th>Total Ports</th>
<th>Open Ports</th>
<th>Reserved Ports</th>
<th>Percent Used</th>
</tr>
<tr ng-repeat="dl in groupSelected.DeviceList">
<td> <i id="clickable" class="fa fa-times" ng-click="DeleteDeviceFromGroup(dl.guid, groupSelected.guid)"></i> </td>
<td>{{dl.DeviceName}}</td>
<td>{{dl.TotalPortCount}}</td>
<td>{{dl.OpenPortCount}}</td>
<td>{{dl.ReservedPortCount}}</td>
<td>{{dl.PercentUsed}}</td>
</tr>
<tr>
<td></td>
</tr>
</table>
#*<ul class="nav navbar-nav">
<li class="dropdown">
----------Add Device from Switch List----------<span class="caret"></span>
<ul class="dropdown-menu" role="menu">
<li ng-repeat="sl in devicesNotinGroup">
<span>
<i id="clickable" class="fa fa-plus-square" ng-click="AddDeviceToGroup(sl.HostName, groupSelected.guid)"></i> {{sl.HostName}} {{sl.Site}} {{sl.Zone}}
</span>
</li>
</ul>
</li>
</ul>*#
<form class="form-inline" name="myForm" role="form" ng-submit="SubmitMultipleDevices(groupSelected.guid)">
<div class="form-group">
<label>Add Devices to Group: </label>
#* Directive *#
<div ng-dropdown-multiselect="" extra-settings="dropdownSetting" options="devicesNotinGroup" selected-model="SelectedDevices" checkboxes="true"></div>
</div>
<br />
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
</div>
</div>
</div>
#section scripts{
<script src="~/Scripts/angular-1.4.9/ui-bootstrap-tpls-1.1.2.min.js"></script>
<script src="~/Scripts/AngularControllers/DeviceGroupsV2.js"></script>
<script src="~/Scripts/angular-1.4.9/angularjs-dropdown-multiselect.min.js"></script>
<script src="~/Scripts/angular-1.4.9/lodash.js"></script>
}

Your forEach loop is incorrect.
angular.forEach($scope.SelectedDevices = function (value) {
$scope.deviceIDs.push({ dname: value.HostName, dguid: groupguid } );
});
should be :
angular.forEach($scope.SelectedDevices, function (value) {
$scope.deviceIDs.push({ dname: value.HostName, dguid: value.groupguid } );
});

Related

bind multiple inputs to each other in vuejs to change together

i want multiple text inputs that when i change one of them i want others to change with the same value too . my inputs are generating in a loop of v-for like below :
<tbody>
<variant-item v-for='(variant, index) in variants' :variant="variant" :key="index" :index="index" "></variant-item>
</tbody>
and here the input is getting generated :
<td>
<div class="control-group" :class="[errors.has(variantInputName + '[price]') ? 'has-error' : '']">
<input type="number" v-model="variant.price" :name="[variantInputName + '[price]']" />
<span class="control-error" v-if="errors.has(variantInputName + '[price]')">#{{ errors.first(variantInputName + '[price]') }}</span></div>
</td>
so with this code if i have 2 products for example the result would be like below :
<td><div class="control-group"><input type="number" name="variants[4344][price]" data-vv-as=""price"" step="any" class="control"> <!----></div></td>
.
.
.
.
<td><div class="control-group"><input type="number" name="variants[4345][price]" data-vv-as=""[price"" > <!----></div></td>
now I want this 2 or multiple inputs change together .
#section('css')
#parent
<style>
.table th.price, .table th.weight {
width: 100px;
}
.table th.actions {
width: 85px;
}
.table td.actions .icon {
margin-top: 8px;
}
.table td.actions .icon.pencil-lg-icon {
margin-right: 10px;
}
</style>
#stop
{!! view_render_event('bagisto.admin.catalog.product.edit_form_accordian.variations.before', ['product' => $product]) !!}
<accordian :title="'{{ __('admin::app.catalog.products.variations') }}'" :active="true">
<div slot="body">
{!! view_render_event('bagisto.admin.catalog.product.edit_form_accordian.variations.controls.before', ['product' => $product]) !!}
<button type="button" class="btn btn-md btn-primary" #click="showModal('addVariant')">
{{ __('admin::app.catalog.products.add-variant-btn-title') }}
</button>
<variant-list></variant-list>
{!! view_render_event('bagisto.admin.catalog.product.edit_form_accordian.variations.controls.after', ['product' => $product]) !!}
</div>
</accordian>
{!! view_render_event('bagisto.admin.catalog.product.edit_form_accordian.variations.after', ['product' => $product]) !!}
<modal id="addVariant" :is-open="modalIds.addVariant">
<h3 slot="header">{{ __('admin::app.catalog.products.add-variant-title') }}</h3>
<div slot="body">
<variant-form></variant-form>
</div>
</modal>
#push('scripts')
#parent
<script type="text/x-template" id="variant-form-template">
<form method="POST" action="{{ route('admin.catalog.products.store') }}" data-vv-scope="add-variant-form" #submit.prevent="addVariant('add-variant-form')">
<div class="page-content">
<div class="form-container">
<div v-for='(attribute, index) in super_attributes' class="control-group" :class="[errors.has('add-variant-form.' + attribute.code) ? 'has-error' : '']">
<label :for="attribute.code" class="required">#{{ attribute.admin_name }}</label>
<select v-validate="'required'" v-model="variant[attribute.code]" class="control" :id="attribute.code" :name="attribute.code" :data-vv-as="'"' + attribute.admin_name + '"'">
<option v-for='(option, index) in attribute.options' :value="option.id">#{{ option.admin_name }}</option>
</select>
<span class="control-error" v-if="errors.has('add-variant-form.' + attribute.code)">#{{ errors.first('add-variant-form.' + attribute.code) }}</span>
</div>
<button type="submit" class="btn btn-lg btn-primary">
{{ __('admin::app.catalog.products.add-variant-title') }}
</button>
</div>
</div>
</form>
</script>
<script type="text/x-template" id="variant-list-template">
<div class="table" style="margin-top: 20px; overflow-x: auto;">
<table>
<thead>
<tr>
<th class="sku">{{ __('admin::app.catalog.products.sku') }}</th>
<th>{{ __('admin::app.catalog.products.name') }}</th>
#foreach ($product->super_attributes as $attribute)
<th class="{{ $attribute->code }}" style="width: 150px">{{ $attribute->admin_name }}</th>
#endforeach
<th class="qty">{{ __('admin::app.catalog.products.qty') }}</th>
<th class="price">{{ __('admin::app.catalog.products.price') }}</th>
<th class="weight">{{ __('admin::app.catalog.products.weight') }}</th>
<th class="status">{{ __('admin::app.catalog.products.status') }}</th>
<th class="actions"></th>
</tr>
</thead>
<tbody>
<variant-item v-for='(variant, index) in variants' :variant="variant" :key="index" :index="index" :variant-price.sync="variantPrice" #onRemoveVariant="removeVariant($event)"></variant-item>
</tbody>
</table>
</div>
</script>
<script type="text/x-template" id="variant-item-template">
<tr>
<td>
<div class="control-group" :class="[errors.has(variantInputName + '[sku]') ? 'has-error' : '']">
<input type="text" v-validate="'required'" v-model="variant.sku" :name="[variantInputName + '[sku]']" class="control" data-vv-as=""{{ __('admin::app.catalog.products.sku') }}"" v-slugify/>
<span class="control-error" v-if="errors.has(variantInputName + '[sku]')">#{{ errors.first(variantInputName + '[sku]') }}</span>
</div>
</td>
<td>
<div class="control-group" :class="[errors.has(variantInputName + '[name]') ? 'has-error' : '']">
<input type="text" v-validate="'required'" v-model="variant.name" :name="[variantInputName + '[name]']" class="control" data-vv-as=""{{ __('admin::app.catalog.products.name') }}""/>
<span class="control-error" v-if="errors.has(variantInputName + '[name]')">#{{ errors.first(variantInputName + '[name]') }}</span>
</div>
</td>
<td v-for='(attribute, index) in superAttributes'>
<div class="control-group">
<input type="hidden" :name="[variantInputName + '[' + attribute.code + ']']" :value="variant[attribute.code]"/>
<input type="text" class="control" :value="optionName(variant[attribute.code])" readonly/>
</div>
</td>
<td>
<button style="width: 100%;" type="button" class="dropdown-btn dropdown-toggle">
#{{ totalQty }}
<i class="icon arrow-down-icon"></i>
</button>
<div class="dropdown-list">
<div class="dropdown-container">
<ul>
<li v-for='(inventorySource, index) in inventorySources'>
<div class="control-group" :class="[errors.has(variantInputName + '[inventories][' + inventorySource.id + ']') ? 'has-error' : '']">
<label>#{{ inventorySource.name }}</label>
<input type="text" v-validate="'numeric|min:0'" :name="[variantInputName + '[inventories][' + inventorySource.id + ']']" v-model="inventories[inventorySource.id]" class="control" v-on:keyup="updateTotalQty()" :data-vv-as="'"' + inventorySource.name + '"'"/>
<span class="control-error" v-if="errors.has(variantInputName + '[inventories][' + inventorySource.id + ']')">#{{ errors.first(variantInputName + '[inventories][' + inventorySource.id + ']') }}</span>
</div>
</li>
</ul>
</div>
</div>
</td>
<td>
<div class="control-group" :class="[errors.has(variantInputName + '[price]') ? 'has-error' : '']">
<input type="number" v-validate="'required|min_value:0.0001'" v-model="variant.price" :name="[variantInputName + '[price]']" class="control" data-vv-as=""{{ __('admin::app.catalog.products.price') }}"" step="any"/>
<span class="control-error" v-if="errors.has(variantInputName + '[price]')">#{{ errors.first(variantInputName + '[price]') }}</span>
</div>
</td>
<td>
<div class="control-group" :class="[errors.has(variantInputName + '[weight]') ? 'has-error' : '']">
<input type="number" v-validate="'required|min_value:0.0001'" v-model="variant.weight" :name="[variantInputName + '[weight]']" class="control" data-vv-as=""{{ __('admin::app.catalog.products.weight') }}"" step="any"/>
<span class="control-error" v-if="errors.has(variantInputName + '[weight]')">#{{ errors.first(variantInputName + '[weight]') }}</span>
</div>
</td>
<td>
<div class="control-group">
<select type="text" v-model="variant.status" :name="[variantInputName + '[status]']" class="control">
<option value="1" :selected="variant.status">{{ __('admin::app.catalog.products.enabled') }}</option>
<option value="0" :selected="!variant.status">{{ __('admin::app.catalog.products.disabled') }}</option>
</select>
</div>
</td>
<td class="actions">
<a :href="['{{ route('admin.catalog.products.index') }}/edit/' + variant.id]"><i class="icon pencil-lg-icon"></i></a>
<i class="icon remove-icon" #click="removeVariant()"></i>
</td>
</tr>
</script>
<script>
$(document).ready(function () {
Vue.config.ignoredElements = [
'variant-form',
'variant-list',
'variant-item'
];
});
var super_attributes = #json(app('\Webkul\Product\Repositories\ProductRepository')->getSuperAttributes($product));
var variants = #json($product->variants);
Vue.component('variant-form', {
data: function() {
return {
variant: {},
super_attributes: super_attributes
}
},
template: '#variant-form-template',
created: function () {
this.resetModel();
},
methods: {
addVariant: function (formScope) {
this.$validator.validateAll(formScope).then((result) => {
if (result) {
var this_this = this;
var filteredVariants = variants.filter(function(variant) {
var matchCount = 0;
for (var key in this_this.variant) {
if (variant[key] == this_this.variant[key]) {
matchCount++;
}
}
return matchCount == this_this.super_attributes.length;
})
if (filteredVariants.length) {
this.$parent.closeModal();
window.flashMessages = [{'type': 'alert-error', 'message': "{{ __('admin::app.catalog.products.variant-already-exist-message') }}" }];
this.$root.addFlashMessages()
} else {
var optionIds = [];
for (var key in this_this.variant) {
optionIds.push(this_this.variant[key]);
}
variants.push(Object.assign({
sku: '{{ $product->sku }}' + '-variant-' + optionIds.join('-'),
name: '',
price: 0,
weight: 0,
status: 1
}, this.variant));
this.resetModel();
this.$parent.closeModal();
}
}
});
},
resetModel: function () {
var this_this = this;
this.super_attributes.forEach(function(attribute) {
this_this.variant[attribute.code] = '';
})
}
}
});
Vue.component('variant-list', {
template: '#variant-list-template',
inject: ['$validator'],
data: function() {
return {
variants: variants,
variantPrice:0,
old_variants: #json(old('variants')),
superAttributes: super_attributes
}
},
created: function () {
var index = 0;
for (var key in this.old_variants) {
var variant = this.old_variants[key];
if (key.indexOf('variant_') !== -1) {
var inventories = [];
for (var inventorySourceId in variant['inventories']) {
inventories.push({'qty': variant['inventories'][inventorySourceId], 'inventory_source_id': inventorySourceId})
}
variant['inventories'] = inventories;
variants.push(variant);
} else {
for (var code in variant) {
if (code != 'inventories') {
variants[index][code] = variant[code];
} else {
variants[index][code] = [];
for (var inventorySourceId in variant[code]) {
variants[index][code].push({'qty': variant[code][inventorySourceId], 'inventory_source_id': inventorySourceId})
}
}
}
}
index++;
}
},
methods: {
removeVariant: function(variant) {
let index = this.variants.indexOf(variant)
this.variants.splice(index, 1)
},
}
});
Vue.component('variant-item', {
template: '#variant-item-template',
props: ['variantPrice','index', 'variant'],
inject: ['$validator'],
data: function() {
return {
inventorySources: #json($inventorySources),
inventories: {},
totalQty: 0,
superAttributes: super_attributes
}
},
created: function () {
var this_this = this;
this.inventorySources.forEach(function(inventorySource) {
this_this.inventories[inventorySource.id] = this_this.sourceInventoryQty(inventorySource.id)
this_this.totalQty += parseInt(this_this.inventories[inventorySource.id]);
})
},
computed: {
variantInputName: function () {
if (this.variant.id)
return "variants[" + this.variant.id + "]";
return "variants[variant_" + this.index + "]";
}
},
methods: {
removeVariant: function () {
this.$emit('onRemoveVariant', this.variant)
},
onVariantPriceInput(event) {
this.$emit('update:variantPrice', Number(event.target.value));
},
optionName: function (optionId) {
var optionName = '';
this.superAttributes.forEach(function(attribute) {
attribute.options.forEach(function(option) {
if (optionId == option.id) {
optionName = option.admin_name;
}
});
})
return optionName;
},
sourceInventoryQty: function (inventorySourceId) {
if (! Array.isArray(this.variant.inventories))
return 0;
var inventories = this.variant.inventories.filter(function(inventory) {
return inventorySourceId === parseInt(inventory.inventory_source_id);
})
if (inventories.length)
return inventories[0]['qty'];
return 0;
},
updateTotalQty: function () {
this.totalQty = 0;
for (var key in this.inventories) {
this.totalQty += parseInt(this.inventories[key]);
}
}
}
});
</script>
#endpush
If it is your objective to have a single price which is shared by all variants, then I would advise against having a price property per variant Object. variant.price suggests a price per variant. I would create a separate data property, say variantPrice that would be passed to each instance of the variant-item component.
(As an aside: If there is a single price that is to be shared by all variant-item components then it may be confusing to your users that you render a price input field per variant-item instead of having a single input field.)
One way to synchronize a value with multiple child components, and assuming you are using a version of Vue >= 2.3 and < 3, is to use the .sync modifier.
Your parent component - the one that references the variants Array - would have a new data property for variantPrice. Here is an example where the parent is the root Vue component:
new Vue({
components: {
variantItem: VariantItem
},
data() {
return {
variantPrice: 0,
variants: [
{
inputName: 'VariantOne'
},
{
inputName: 'VariantTwo'
}
]
};
},
el: "#App"
});
The template of the parent (root) would pass variantPrice as a prop to each variant-item instance using the .sync modifier which will bind the value of variantPrice to the update:variantPrice event emitted by the child. The template would look like:
<div id="App">
<variant-item v-for="(variant, index) in variants" :key="index" :variant="variant" :variant-price.sync="variantPrice"></variant-item>
</div>
Next, we must ensure that our VariantItem component takes a variantPrice as a prop and emits the update:variantPrice event when the input field is modified by the user:
const VariantItem = Vue.component('variant-item', {
methods: {
onVariantPriceInput(event) {
this.$emit('update:variantPrice', Number(event.target.value));
}
},
props: {
variant: {
required: true,
type: Object
},
variantPrice: {
required: true,
type: Number
}
},
template: '#VariantItemTemplate'
});
The template for this component would become:
<div>
<label :for="variant.inputName">{{ variant.inputName }}</label>
<input :id="variant.inputName" :name="variant.inputName" :value="variantPrice" #input="onVariantPriceInput" type="number">
</div>
I have created a fiddle for your reference.

Close previous table row when clicking on another table row

I'm fetching data into the table and on clicking any table row it open a nested colspan inside table row. I want to close previous table row if i click on another table row (Like accordion does )
this is a table body structure
<tbody style="cursor:pointer" v-for="(statement,index) in statements">
<tr #click="statementDetail(index,statement.paper_quality_id.id,statement.paper_brand_id.id,statement.paper_size_id.id,statement.thickness)"
>
<td>{{index+1}}</td>
<td>
{{statement.paper_quality_id.paper_quality}} -
{{statement.paper_size_id.length}} X {{statement.paper_size_id.width}} -
{{statement.paper_brand_id.paper_brand}} -
{{statement.thickness}}
</td>
<td>
<div class="row">
<div
class="col-md-4 text-center"
>{{parseInt((statement.in_total_before - statement.total_out_before)/500)}}</div>
<div
class="col-md-4 text-center"
>{{parseInt((statement.in_total_before - statement.total_out_before)%500)}}</div>
</div>
</td>
<td>
<div class="row">
<div
class="col-md-4 text-center"
>{{parseInt((statement.total_sheets_in_range)/500)}}</div>
<div
class="col-md-4 text-center"
>{{parseInt((statement.total_sheets_in_range)%500)}}</div>
</div>
</td>
<td>
<div class="row">
<div
class="col-md-4 text-center"
>{{parseInt((statement.total_outward_range)/500)}}</div>
<div
class="col-md-4 text-center"
>{{parseInt((statement.total_outward_range)%500)}}</div>
</div>
</td>
<td>
<div class="row">
<div class="col-md-4 text-center">
{{
parseInt((((statement.in_total_before - statement.total_out_before) + (statement.total_sheets_in_range)) - statement.total_outward_range)/500)
}}
</div>
<div class="col-md-4 text-center">
{{
parseInt((((statement.in_total_before - statement.total_out_before) + (statement.total_sheets_in_range)) - statement.total_outward_range)%500)
}}
</div>
</div>
</td>
</tr>
This the nested table it appears when we click on table row
<tr :id="'show_'+index" v-if = "index = indexData" >
<td colspan="6">
<table class="table table-hover">
<tr>
<th>Date</th>
<th>Opening</th>
<th>Inward</th>
<th>Outward</th>
<th>Balance</th>
</tr>
<tr v-for="(_statement,index) in statements_details" >
<td>{{_statement.date}}</td>
<td>
<div class="row">
<div
class="col-md-3 text-center"
>{{parseInt((_statement.opening.total_in - _statement.opening.total_out)/500)}}</div>
<div
class="col-md-3 text-center"
>{{parseInt((_statement.opening.total_in - _statement.opening.total_out)%500)}}</div>
</div>
</td>
<td>
<div class="row">
<div class="col-md-3 text-center">{{parseInt((_statement.inward)/500)}}</div>
<div class="col-md-3 text-center">{{parseInt((_statement.inward)%500)}}</div>
</div>
</td>
<td>
<div class="row">
<div class="col-md-3 text-center">{{parseInt((_statement.outward)/500)}}</div>
<div class="col-md-3 text-center">{{parseInt((_statement.outward)%500)}}</div>
</div>
</td>
<td>
<div class="row">
<div
class="col-md-3 text-center"
>{{parseInt((_statement.balance.total_in - _statement.balance.total_out)/500)}}</div>
<div
class="col-md-3 text-center"
>{{parseInt((_statement.balance.total_in - _statement.balance.total_out)%500)}}</div>
</div>
</td>
</tr>
</table>
</td>
</tr>
</tbody>
This is my js file
export default {
data() {
return {
statements: '',
users: [],
ledger_name: '',
paper_details: '',
account_list: false,
//statement Objects
id: '',
start_date: '',
end_date: '',
paper_quality_id: '',
paper_size_id: '',
paper_brand_id: '',
thickness: '',
statements_details: '',
stockIDS: '',
indexData: ''
};
},
created() {
this.fetchData();
},
components: {
appInventoryNavigation: InventoryNavBar,
},
methods: {
// Fetching Ledger Account List
fetchData() {
var vm = this;
axios.get('/ledger/')
.then((response) => {
console.log(response)
vm.users = response.data
}).catch((err) => {
console.log(err)
});
},
//Setting LedgerID In Hidden Input Field
setLedgerID_1(id, name) {
this.account_name = id;
this.account_list = false;
this.id = id;
this.ledger_name = name;
},
call() {
this.account_list = true;
},
//Posting LedgeID
PostLedgerID(e) {
e.preventDefault();
const AccountDetail = {
id: this.id,
start_date: this.start_date + ' 00:00:00.957761',
end_date: this.end_date + ' 00:00:00.957761'
}
var vm = this;
axios.post('/Statement/', AccountDetail)
.then((response) => {
console.log(response)
vm.statements = response.data;
}).catch((err) => {
console.log(err)
});
},
//Show Hide Nested Table
statementDetail(rowid, paper_id, brand_id, size_id, thickness) {
this.indexData = rowid;
alert(this.indexData)
const userDetail = {
account_access_key_id: $('#ledger_id').val(),
start_date: this.start_date + ' 00:00:00.957761',
end_date: this.end_date + ' 00:00:00.957761',
paper_quality_id: paper_id,
paper_brand_id: brand_id,
paper_size_id: size_id,
thickness: thickness
}
axios.post('/StatementDetail/', userDetail)
.then((response) => {
$('#show_' + rowid).toggle();
this.statements_details = response.data;
}).catch((err) => {
console.log(err)
});
}
},
};
Change v-show="indexData === index".
And in your method check if rowid is changed and move this.indexData = rowid; after axios request:
statementDetail(rowid, paper_id, brand_id, size_id, thickness) {
if (rowid !== this.indexData){
const userDetail = {
account_access_key_id: $('#ledger_id').val(),
start_date: this.start_date + ' 00:00:00.957761',
end_date: this.end_date + ' 00:00:00.957761',
paper_quality_id: paper_id,
paper_brand_id: brand_id,
paper_size_id: size_id,
thickness: thickness
}
axios.post('/StatementDetail/', userDetail)
.then((response) => {
$('#show_' + rowid).toggle();
this.statements_details = response.data;
}).catch((err) => {
console.log(err)
});
this.indexData = rowid;
}
}
Good luck.

how to add auto complete value to the list?

<div class="maincontent">
<div ng-app="autocompleteCustomTemplateDemo" ng-controller="DemoCtrl as ctrl" class="container container1">
<div class="row">
<div class="col-lg-12 col-sm-12">
<h4><strong> Service</strong></h4>
</div>
</div>
<div class="row">
<div class="col-md-12">
<md-content layout-padding layout="column">
<form ng-submit="$event.preventDefault()">
<md-autocomplete ng-disabled="ctrl.isDisabled" md-no-cache="ctrl.noCache" md-selected-item="ctrl.selectedItem" md-search-text- change="ctrl.searchTextChange(ctrl.searchText)" md-search-text="ctrl.searchText" md-selected-item-change="ctrl.selectedItemChange(item)"
md-items="item in ctrl.querySearch(ctrl.searchText)" md-item-text="item.product_name" md-min-length="0" placeholder="Pick an Angular repository" md-menu-class="autocomplete-custom-template">
<md-item-template>
<span class="item-title">
<span> {{item.product_gid}} </span>
</span>
<span class="item-metadata" ng-model="g1" ng-click="type(g1)">
<span>
<strong>{{item.product_name}}</strong>
</span>
</span>
</md-item-template>
</md-autocomplete>
<div class="row">
<div class="col-md-12 text-right">
<md-button ng-click="add()">ADD</md-button>
</div>
</div>
<br/>
<div class="row table-responsive" ng-show="show_servicelist">
<div class="col-md-12 col-lg-12 col-sm-12">
<table class="table table-striped table-bordered table-condensed table-hover md-primary" md-progress="deferred">
<thead class="header">
<tr style="text-align:center">
<th>S No</th>
<th>product name</th>
<th>Quantity</th>
<th style="text-align:center">Amount</th>
</tr>
</thead>
<tbody>
<tr>
<td ng-model="dept_name">{{$index + 1}}</td>
<td>
{{ctrl.selectedItem.product_name}}
</td>
<td style="text-align:center">
<input ng-model="tobuy_remark" />
</td>
<td style="text-align:center">
<input ng-model="tobuy_remarks" />
</td>
</tr>
</tbody>
</table>
<div class="row">
<div class="col-md-12 text-right">
<md-button>Submit</md-button>
</div>
</div>
</div>
</div>
</form>
</md-content>
</div>
</div>
</div>
</div>
JS
(function() {
'use strict';
angular
.module('autocompleteCustomTemplateDemo', ['ngMaterial'])
.controller('DemoCtrl', DemoCtrl);
function DemoCtrl($timeout, $q, $log, $scope) {
var self = this;
$scope.add = function() {
$scope.show_servicelist = true;
$scope.service = [];
alert(JSON.stringify(self.repos));
$scope.service.push({
product_name: self.repos.product_name
});
}
self.simulateQuery = false;
self.isDisabled = false;
self.repos = loadAll();
self.querySearch = querySearch;
self.selectedItemChange = selectedItemChange;
self.searchTextChange = searchTextChange;
function querySearch(query) {
var results = query ? self.repos.filter(createFilterFor(query)) : self.repos,
deferred;
if (self.simulateQuery) {
deferred = $q.defer();
$timeout(function() {
deferred.resolve(results);
}, Math.random() * 1000, false);
return deferred.promise;
} else {
return results;
}
}
function searchTextChange(text) {
$log.info('Text changed to ' + text);
}
function selectedItemChange(item) {
$log.info('Item changed to ' + JSON.stringify(item));
}
function loadAll() {
var repos = [{
'product_gid': '1',
'product_name': 'stabilizer',
'forks': '16,175',
}, {
'product_gid': '2',
'product_name': 'stand',
'forks': '760',
}, {
'product_gid': '3',
'product_name': 'ac',
'forks': '1,241',
},
];
return repos.map(function(repo) {
repo.value = repo.product_name.toLowerCase();
return repo;
});
}
function createFilterFor(query) {
var lowercaseQuery = angular.lowercase(query);
return function filterFn(item) {
return (item.value.indexOf(lowercaseQuery) === 0);
};
}
}
})();
I'm running the following code to display the value in the autocomplete after selecting the add button. I need the value to list one after another after clicking the add button. I have used a material for this. please guide me, and modify the code. The above code just displays the list empty, how to store a value in the list and allocate in order.
I'll change 2 functions for you, it should work.
first thing create the array globally.
self.repos = [{
'product_gid': '1',
'product_name': 'stabilizer',
'forks': '16,175',
}, {
'product_gid': '2',
'product_name': 'stand',
'forks': '760',
}, {
'product_gid': '3',
'product_name': 'ac',
'forks': '1,241',
},
];
function loadAll() {
return self.repos.map(function(repo) {
repo.value = repo.product_name.toLowerCase();
return repo;
});
}
add(object) {
self.repos.push(object);
self.loadAll();
}

Angular Error - ReferenceError: $modal is not defined

I am using code form a tutorial and modifying it a bit. I have run into an issue with the edit feature. I keep getting a "ReferenceError: $modal is not defined" here is my code.
postCtrl:
app.filter('startFrom', function() {
return function(input, start) {
if(input) {
start = +start; //parse to int
return input.slice(start);
}
return [];
}
});
app.filter('dateToISO', function() {
return function(input) {
input = new Date(input).toISOString();
return input;
};
});
app.controller('postsCtrl', function ($scope, $log, $http, $timeout, Data) {
Data.get('posts').then(function(data){
$scope.posts = data.data;
$scope.currentPage = 1; //current page
$scope.filteredItems = $scope.posts.length; //Initially for no filter
$scope.totalItems = $scope.posts.length;
$scope.list_pages = [
{
id: '5',
name: '5'
}, {
id: '10',
name: '10'
}, {
id: '20',
name: '20'
}, {
id: '50',
name: '50'
}, {
id: '100',
name: '100'
}
];
$scope.maxSize = 5;
});
$scope.setPage = function(pageNo) {
$scope.currentPage = pageNo;
};
$scope.filter = function() {
$timeout(function() {
$scope.filteredItems = $scope.filtered.length;
}, 10);
};
$scope.sort_by = function(predicate) {
$scope.predicate = predicate;
$scope.reverse = !$scope.reverse;
};
$scope.changePostStatus = function(post){
post.approved = (post.approved=="1" ? "0" : "1");
Data.put("posts/"+post.id,{approved:post.approved});
};
$scope.changePostAnnounce = function(post){
post.announce = (post.announce=="1" ? "0" : "1");
Data.put("posts/"+post.id,{announce:post.announce});
};
$scope.trashPost = function(post){
//$log.log(post);
if(confirm("Are you sure to remove the post")){
Data.delete("posts/"+post.id).then(function(result){
$scope.posts = _.without($scope.posts, _.findWhere($scope.posts, {id:post.id}));
});
}
};
$scope.open = function (p,size) {
var modalInstance = $modal.open({
templateUrl: 'views/postsEdit.html',
controller: 'postsEditCtrl',
size: size,
resolve: {
item: function () {
return p;
}
}
});
modalInstance.result.then(function(selectedObject) {
if(selectedObject.save == "insert"){
$scope.posts.push(selectedObject);
$scope.posts = $filter('orderBy')($scope.posts, 'id', 'reverse');
}else if(selectedObject.save == "update"){
p.description = selectedObject.description;
p.price = selectedObject.price;
p.stock = selectedObject.stock;
p.packing = selectedObject.packing;
}
});
};
});
app.controller('postsEditCtrl', function ($scope, $modalInstance, item, Data) {
$scope.post = angular.copy(item);
$scope.cancel = function () {
$modalInstance.dismiss('Close');
};
$scope.title = (item.id > 0) ? 'Edit Post' : 'Add Post';
$scope.buttonText = (item.id > 0) ? 'Update Post' : 'Add New Post';
var original = item;
$scope.isClean = function() {
return angular.equals(original, $scope.post);
}
$scope.saveProduct = function (post) {
post.uid = $scope.uid;
if(post.id > 0){
Data.put('posts/'+post.id, post).then(function (result) {
if(result.status != 'error'){
var x = angular.copy(post);
x.save = 'update';
$modalInstance.close(x);
}else{
console.log(result);
}
});
}else{
post.status = 'Active';
Data.post('posts', post).then(function (result) {
if(result.status != 'error'){
var x = angular.copy(post);
x.save = 'insert';
x.id = result.data;
$modalInstance.close(x);
}else{
console.log(result);
}
});
}
};
});
html:
<div class="container">
<div class="row" align="center">
<div class="stats"><i class="fa fa-thumb-tack"></i> Total Posts (<span class="attendStat">{{ totalItems }}</span>)<span class="seperator"> | </span><i class="fa fa-trash-o"></i> Trash (<span class="attendStat">X</span>)</div>
</div>
<div class="row">
<div class="col-md-1">PageSize:
<select ng-model="entryLimit" class="form-control" ng-options="obj.id as obj.name for obj in list_pages" ng-init="entryLimit='10'">
</select>
</div>
<div class="col-md-5"><span class="">Filtered: {{ filtered.length }} of {{ totalItems }} total posts</span>
<input type="text" ng-model="search" ng-change="filter()" placeholder="Filter" class="form-control" />
</div>
<div class="col-md-4 pull-right text-right" ng-show="filteredItems > 0">
<uib-pagination total-items="filteredItems" items-per-page="entryLimit" boundary-link-numbers="true" max-size="maxSize" ng-model="currentPage" class="pagination-sm"></uib-pagination>
</div>
</div>
<br/>
<div class="row">
<div class="table-responsive" ng-show="filteredItems > 0">
<table class="table table-striped table-bordered">
<thead>
<th>Publish Date <a ng-click="sort_by('publishdate');"><i class="glyphicon glyphicon-sort"></i></a></th>
<th>GUID <a ng-click="sort_by('guid');"><i class="glyphicon glyphicon-sort"></i></a></th>
<th>Platform <a ng-click="sort_by('platform');"><i class="glyphicon glyphicon-sort"></i></a></th>
<th>Link Title <a ng-click="sort_by('title');"><i class="glyphicon glyphicon-sort"></i></a></th>
<th>Redirect Url (Base) <a ng-click="sort_by('redirect');"><i class="glyphicon glyphicon-sort"></i></a></th>
<th>Announce <a ng-click="sort_by('announce');"><i class="glyphicon glyphicon-sort"></i></a></th>
<th>Approve <a ng-click="sort_by('approve');"><i class="glyphicon glyphicon-sort"></i></a></th>
<th></th>
</thead>
<tbody ng-repeat="data in filtered = (posts | filter:search | orderBy : predicate :reverse) | startFrom:(currentPage-1)*entryLimit | limitTo:entryLimit">
<tr>
<td class="posts-publishdate">{{data.publishdate | dateToISO | date:'MMM d, y h:mm a' }}</td>
<td>{{data.guid}}</td>
<td>{{data.platform}}</td>
<td>{{data.title}}</td>
<td>{{data.redirect}}</td>
<td>
<button class="btn btn-sm" ng-class="{1:'btn-success', 0:''}[data.announce]" ng-click="changePostAnnounce(data);">{{data.announce == '1' ? "Active" : "Inactive"}}</button>
</td>
<td>
<button class="btn btn-sm" ng-class="{1:'btn-success', 0:''}[data.approved]" ng-click="changePostStatus(data);">{{data.approved == '1' ? "Active" : "Inactive"}}</button>
</td>
<td style="width:100px">
<div class="btn-group">
<button type="button" class="btn btn-default fa fa-edit" ng-click="open(data);"></button>
<button type="button" class="btn btn-danger fa fa-trash-o" ng-click="trashPost(data);"></button>
</div>
</td>
</tr>
<tr>
<td></td>
<td colspan="8">
<table class="table table-striped table-bordered">
<thead>
<th>Image Url <a ng-click="sort_by('img');"><i class="glyphicon glyphicon-sort"></i></a></th>
<th>Link Description <a ng-click="sort_by('description');"><i class="glyphicon glyphicon-sort"></i></a></th>
<th>Tweet <a ng-click="sort_by('dynamic_content');"><i class="glyphicon glyphicon-sort"></i></a></th>
</thead>
<tbody>
<tr>
<td><img src="{{data.img}}" width="200"></td>
<td>{{data.description}}</td>
<td>{{data.dynamic_content}}</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</div>
<div class="col-md-12" ng-show="filteredItems == 0">
<div class="col-md-12">
<h4>No customers found</h4>
</div>
</div>
</div>
</div>
Any help would be much appreciated.
You missed to inject $modal dependency inside postsCtrl controller
app.controller('postsCtrl', function ($scope, $log, $http, $timeout, Data, $modal) {
Make sure you have injected particular dependency before getting
access to it. Assuming you have already added ui.bootstrap module
dependency too.
UPDATE
If you are using latest version of angular ui bootstrap which is 0.14.X would need to inject $uibModal instead of $modal. As they rename all boostrap directive and serviec name prepends with uib prefix.
Same thing will happen with $modalInstance dependency, which need to change to $uibModalInstance

options single use value from json - Knockout JS

I have data coming in from another view model and I'm displaying it in a dropdown menu. I need each option from the other view model to be unique per line, so once the user adds an item to the list, that option isn't available anymore.
Here is the working fiddle: http://jsfiddle.net/QTUqD/9/
window.usrViewModel = new function () {
var self = this;
window.viewModel = self;
self.list = ko.observableArray();
self.pageSize = ko.observable(10);
self.pageIndex = ko.observable(0);
self.selectedItem = ko.observable();
self.extData = ko.observableArray();
extData = ExtListViewModel.list();
self.edit = function (item) {
if($('#usrForm').valid()) {
self.selectedItem(item);
}
};
self.cancel = function () {
self.selectedItem(null);
};
self.add = function () {
if($('#usrForm').valid()) {
var newItem = new Users();
self.list.push(newItem);
self.selectedItem(newItem);
self.moveToPage(self.maxPageIndex());
};
};
self.remove = function (item) {
if (confirm('Are you sure you wish to delete this item?')) {
self.list.remove(item);
if (self.pageIndex() > self.maxPageIndex()) {
self.moveToPage(self.maxPageIndex());
}
}
$('.error').hide();
};
self.save = function () {
self.selectedItem(null);
};
self.templateToUse = function (item) {
return self.selectedItem() === item ? 'editUsrs' : 'usrItems';
};
self.pagedList = ko.dependentObservable(function () {
var size = self.pageSize();
var start = self.pageIndex() * size;
return self.list.slice(start, start + size);
});
self.maxPageIndex = ko.dependentObservable(function () {
return Math.ceil(self.list().length / self.pageSize()) - 1;
});
self.previousPage = function () {
if (self.pageIndex() > 0) {
self.pageIndex(self.pageIndex() - 1);
}
};
self.nextPage = function () {
if (self.pageIndex() < self.maxPageIndex()) {
self.pageIndex(self.pageIndex() + 1);
}
};
self.allPages = ko.dependentObservable(function () {
var pages = [];
for (i = 0; i <= self.maxPageIndex() ; i++) {
pages.push({ pageNumber: (i + 1) });
}
return pages;
});
self.moveToPage = function (index) {
self.pageIndex(index);
};
};
ko.applyBindings(usrViewModel, document.getElementById('usrForm'));
function Users(fname, lname, email, phone, access, usrExtVal){
this.fname = ko.observable(fname);
this.lname = ko.observable(lname);
this.email = ko.observable(email);
this.phone = ko.observable(phone);
this.access = ko.observable(access);
this.usrExtVal = ko.observableArray(usrExtVal);
}
<form id="usrForm">
<h2>Users</h2>
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Email</th>
<th>Phone Number</th>
<th>Access</th>
<th>Extension</th>
<th style="width: 100px; text-align:right;" />
</tr>
</thead>
<tbody data-bind=" template:{name:templateToUse, foreach: pagedList }"></tbody>
</table>
<p class="pull-right"><a class="btn btn-primary" data-bind="click: $root.add" href="#" title="edit"><i class="icon-plus"></i> Add Users</a></p>
<div class="pagination pull-left">
<ul><li data-bind="css: { disabled: pageIndex() === 0 }">Previous</li></ul>
<ul data-bind="foreach: allPages">
<li data-bind="css: { active: $data.pageNumber === ($root.pageIndex() + 1) }"></li>
</ul>
<ul><li data-bind="css: { disabled: pageIndex() === maxPageIndex() }">Next</li></ul>
</div>
<br clear="all" />
<script id="usrItems" type="text/html">
<tr>
<td data-bind="text: fname"></td>
<td data-bind="text: lname"></td>
<td data-bind="text: email"></td>
<td data-bind="text: phone"></td>
<td data-bind="text: access"></td>
<td data-bind="text: usrExtVal"></td>
<td class="buttons">
<a class="btn" data-bind="click: $root.edit" href="#" title="edit"><i class="icon-edit"></i></a>
<a class="btn" data-bind="click: $root.remove" href="#" title="remove"><i class="icon-remove"></i></a>
</td>
</tr>
</script>
<script id="editUsrs" type="text/html">
<tr>
<td><input data-errorposition="b" class="required" name="fname" data-bind="value: fname" /></td>
<td><input data-errorposition="b" class="required" name="lname" data-bind="value: lname" /></td>
<td><input data-errorposition="b" class="required" name="email" data-bind="value: email" /></td>
<td><input data-errorposition="b" class="required" name="phone" data-bind="value: phone" /></td>
<td><select data-bind="value: access"><option>Employee</option><option>Administrator</option><option>PBX Admin</option><option>Billing</option></select></td>
<td><select data-bind="options: $root.extOptions, optionsText: 'extension', value: usrExtVal"></select></td>
<td class="buttons">
<a class="btn btn-success" data-bind="click: $root.save" href="#" title="save"><i class="icon-ok"></i></a>
<a class="btn" data-bind="click: $root.remove" href="#" title="remove"><i class="icon-remove"></i></a>
</td>
</tr>
</script>
</form>
I'm not sure if this is what you are looking for, but here is a fiddle that computes the available values based on the those already in use:
http://jsfiddle.net/jearles/QTUqD/11/
--
HTML
<select data-bind="options: $root.availableExtData, optionsText: 'extension', value: usrExtVal"></select>
JS
self.availableExtData = ko.computed(function() {
var inUse = [];
if (!self.selectedItem()) return inUse;
ko.utils.arrayForEach(self.list(), function(item) {
if (inUse.indexOf(item.usrExtVal().extension) == -1 && self.selectedItem() != item) inUse.push(item.usrExtVal().extension);
});
return ko.utils.arrayFilter(self.extData(), function(item) {
return inUse.indexOf(item.extension) == -1;
});
});
--
The code ensures that when the select item is being edited that it's current value is available. Additionally I also don't show the add button once there is a row for all available values.
Make a computed observable with the available options, that filters away from the complete list (extData) those that have already been assigned to an item - except the one used by the selected item - and then bind the options of the select field to that computed instead of the full list.

Categories

Resources