Here's my angularjs Controller
var $tr = angular.element("#parent" + obj.field_id).find("tbody"),
$nlast = $tr.find("tr:last"),
$clone = angular.copy($nlast);
$clone.find(':text').val('');
var elem = angular.element($clone);
$tr.after($compile(elem)($scope));
When I tried to remove $compile it's working but angularjs does not working at all like validation of fields that's why I need to add a $compile but it doesn't seem working to me , please help me I'm new in angularJS
Here's an example of a simple table built from an object in angularJS. It's not the only way, but it illustrates some basic concepts, and how to add another row.
In the controller
$scope.tabledata = {
header: [{name: "id"}, {name: "name"}, {name: "email"}],
rows: [{
id: 1,
name: "Joe",
email: "joe#1.com"
},
{
id: 2,
name: "Bill",
email: "bill#1.com"
},
{
id: 3,
name: "Sally",
email: "sally#1.com"
}
]
}
// later I want to add another row:
$scope.tabledata.rows.push({
id: 4,
name: "Bob",
email: "bob#1.com"
})
// and like magic, another row is added to the table
The view file:
<table ng-if="tabledata">
<tr>
<th ng-repeat="item in tabledata.header">{{item.name}}</th>
</tr>
<tr ng-repeat="row in tabledata.rows">
<td ng-repeat="(key, value) in row">{{value}}</td>
</tr>
</table>
This is my actual code
<table class="table table-bordered" border="" cellpadding="" cellspacing="" style="background-color:white" id="parent{{form.field_id}}">
<thead>
<tr >
<th ng-repeat="Col in form.GridList">{{Col.field_title}}</th>
</tr>
</thead>
<tbody>
<tr >
<td ng-repeat="gridData in form.GridList">
<div ng-if="gridData.field_type == 'Textbox' && gridData.field_datatype == 'Text'">
<div ng-if="gridData.field_required == 'True'">
<div class="control-group has-feedback">
<div class="Textbox">
<div ng-if="gridData.enable_textformat == 'True'">
<input id="sub{{gridData.field_id }}" name="textinput" ng-model="gridData.value" placeholder="{{gridData.field_textformat}}" type="text" ng-required="{{gridData.field_required | lowercase}} && {{gridData.field_standard | lowercase}} == true" class="col-md-5 rectangle form-control"/>
</div>
<div ng-else>
<input id="sub{{gridData.field_id }}" name="textinput" ng-model="gridData.value" type="text" placeholder="" ng-required="{{gridData.field_required | lowercase}} && {{gridData.field_standard | lowercase}} == true" class="{{gridData.field_required}} col-md-5 rectangle form-control"/>
</div>
</div>
</div>
</div>
<div ng-if="gridData.field_required == 'False'">
<div class="control-group">
<div class="Textbox">
<div ng-if="gridData.enable_textformat == 'True'">
<input id="sub{{gridData.field_id }}" name="textinput" ng-model="gridData.value" placeholder="{{gridData.field_textformat}}" type="text" ng-required="{{gridData.field_required | lowercase}} && {{gridData.field_standard | lowercase}} == false" class="col-md-5 rectangle form-control"/>
</div>
<div ng-else>
<input id="sub{{gridData.field_id }}" name="textinput" ng-model="gridData.value" type="text" placeholder="" ng-required="{{gridData.field_required | lowercase}} && {{gridData.field_standard | lowercase}} == false" class="{{gridData.field_required}} col-md-5 rectangle form-control"/>
</div>
</div>
</div>
</div>
</div>
</td>
</tr>
</tbody>
</table>
and This is my angularjs controller
function createDataTable(field) {
field.GridList = [{}];
var appendValue = "";
$scope.isread = "";
var row = 1;
var col = "";
try {
$http({
method: 'POST',
url: "DefaultForm.aspx/populaGridView",
data: "{'fieldid':" + field.field_id + "}",
headers: { 'Content-Type': 'application/json; charset=utf-8' }
}).then(function onSuccess(response) {
field.GridList = JSON.parse(response.data.d);
$scope.Tables = JSON.parse(response.data.d);
angular.forEach(field.GridList, function (vals, keys) {
if (vals.field_type == "dropdownList") {
$http({
method: 'POST',
url: "DefaultForm.aspx/populaDD",
data: "{'fieldid':" + vals.field_id + "}",
headers: { 'Content-Type': 'application/json; charset=utf-8' }
}).then(function onSuccess(response) {
try {
vals.Newvalue = [];
vals.Newvalue = JSON.parse(response.data.d);
} catch (err) {
}
}, function myError(response) {
});
}
})
}, function myError(response) {
alert(response.statusText);
});
} catch (err) { console.log("new error " + err.message); }
};
button add new row to the table
$scope.AddRow = function (obj) {
var row = { cells: [] },
se = obj.GridList,
rowLen = se.length;
angular.forEach(se, function (vals) {
row.cells.push({ field_type: vals.field_type });
})
se.push(row);
}
when button is clicked it goes to the 1st row not in 2nd row.
Related
I can read the data of one Input Argument with Ng2SearchPipeModule.
Typescript:
public dropdownAuslesen(statustext: number) {
this.nameComp = [];
for (let i = 0; i < this.infoFile.length; i++) {
if (this.infoFile[i]['status'] === statustext) {
this.nameComp.push({
name: this.infoFile[i]['component_Name'],
id: this.componentID[i],
version: this.infoFile[i]['version'],
status: this.getStatusText(this.infoFile[i]['status']),
});
}
}
}
HTML:
<tr *ngFor="let name of this.nameComp | filter: (searchString.valueChanges | async); let i = index">
<td>
<a [routerLink]="['/details', name.id] ">
{{ name.name }}
</a>
</td>
<td>{{ name.version }}</td>
<td [ngStyle]="{'color':name.status === 'geprüft' ? 'green' : 'red' }">{{name.status}}</td>
</tr>
<div class="container mt-4 element1 searchbar inline" id="left">
<input type="text" class="form-control" placeholder="Nach Komponente suchen..." [formControl]="searchString"
autocomplete="on">
</div>
How can I search more than one String in the input field? Maybe with a &?
I expect I can search for example for Sarah & 26.
This code below select only one by one checkbox, how can I transform this code so I can select all checkboxes by one click, but I need my variables to stay defined.
$('.checktip').click(function () {
var iduser = $('.iduser').val();
var idtip = $(this).attr('idtip');
var che = $(this).prop('checked');
$.ajax({
url: UrlSettingsDocument.OrgUnits,
data: { iduser: iduser, idtip: idtip, che: che },
type: "POST",
success: function (result) {
if (result.result == "Redirect") {
window.location = result.url;
}
}
});
});
I using this variables for controller where I save this values in database when I check them or unchecked.
Here is my html code
<input type="hidden" value="#ViewBag.iduser" id="iduser" />
<hr />
<table class="table table-striped grid-table">
<tr>
<th>Samp</th>
<th>Id of Book</th>
<th>
//Checkbox for check all *(NOT IMPLEMENTED)*
<input type="checkbox" id="box" name="checkAll" />
</th>
</tr>
#foreach (var item in (IEnumerable<cit.Models.getCheIdTip_Result>)Model)
{
<tr>
<td>#item.idtip</td>
<td>#item.tipname</td>
<td>
#*#Html.CheckBox(item.id.ToString(), item.iduser == ViewBag.iduser ? true : false, new { idtip = item.idtip, #class = "checktip" })*#
<div class="pure-checkbox">
<input type="checkbox" idtip="#item.idtip" class="checktip" checked="#(item.iduser == ViewBag.iduser ? true : false)" name="#item.id.ToString()" id="#item.id.ToString()" />
<label for="#item.id.ToString()"></label>
</div>
</td>
</tr>
}
</table>
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.
Table has two columns Name, Age. Searching works by name. As you type the name of user the table will trim down to the specific user name.
But I want it to be filtered by age using a comparison operator < or >.
Code pen link
Html
<div id="demo" class="container">
<div class="row">
<div class="col-md-6">
<input v-model="search" class="form-control" placeholder="Username to search">
</div>
<div class="col-md-1">
<select class="form-control" v-model="searchOperator">
<option value=">">></option>
<option value="<"><</option>
</select>
</div>
<div class="col-md-5">
<input v-model="searchName" class="form-control" placeholder="Age">
</div>
</div>
<table class="table table-striped">
<thead>
<tr>
<th v-repeat="column: columns">
<a href="#" v-on="click: sortBy(column)" v-class="active: sortKey == column">
{{ column | capitalize }}
</a>
</th>
</tr>
</thead>
<tbody>
<tr v-repeat="users | filterBy search | orderBy sortKey reverse">
<td>{{ name }}</td>
<td>{{ age }}</td>
</tr>
</tbody>
</table>
</div>
Vue :
new Vue({
el: '#demo',
data: {
sortKey: 'name',
reverse: false,
searchName: '',
searchOperator: '',
searchAge: '',
columns: ['name', 'age'],
newUser: {},
users: [
{ name: 'John', age: 50 },
{ name: 'Jane', age: 22 },
{ name: 'Paul', age: 34 }
]
},
methods: {
sortBy: function(sortKey) {
this.reverse = (this.sortKey == sortKey) ? ! this.reverse : false;
this.sortKey = sortKey;
}
}
});
What is the best approach to achieve this? I tried but nothing seem to be working.
The first thing is : v-repeat is deprecated. So you should be using v-for instead.
<tr v-for="user in filteredPersons">
<td>{{ user.name }}</td>
<td>{{ user.age }}</td>
</tr>
filteredPersons is the name of a computed function that returns an array :
computed: {
filteredPersons: function () {
return this.users
.filter(this.filterByName)
.filter(this.filterByAge)
.sort(this.orderBy);
}
}
It filters and sorts the users array by combining two filter functions and one comparator function :
methods: {
filterByName : function(user) {
// no search, don't filter :
if (this.searchName.length === 0) {
return true;
}
return (user.name.toLowerCase().indexOf(this.searchName.toLowerCase()) > -1);
},
filterByAge : function (user) {
// no operator selected or no age typed, don't filter :
if (this.searchOperator.length === 0 || this.age.length === 0) {
return true;
}
if (this.searchOperator === '>') {
return (user.age > this.age);
} else if (this.searchOperator === '<') {
return (user.age < this.age);
}
},
orderBy : function (userA, userB) {
let condition = (userA[this.sortKey] > userB[this.sortKey]);
if (this.reverse) {
return !condition;
} else {
return condition;
}
}
},
Working snippet :
new Vue({
el: '#demo',
data: {
sortKey: 'name',
reverse: false,
searchName: '',
searchOperator: '',
searchAge: '',
columns: ['name', 'age'],
newUser: {},
search: "",
name: "",
age: "",
users: [
{ name: 'John', age: 50 },
{ name: 'Jane', age: 22 },
{ name: 'Paul', age: 34 },
{ name: 'Kate', age: 15 },
{ name: 'Amanda', age: 65 },
{ name: 'Steve', age: 38 },
{ name: 'Keith', age: 21 },
{ name: 'Don', age: 50 },
{ name: 'Susan', age: 21 }
]
},
methods: {
sortBy: function (sortKey) {
this.reverse = (this.sortKey == sortKey) ? !this.reverse : false;
this.sortKey = sortKey;
},
filterByName : function(user) {
// no search, don't filter :
if (this.searchName.length === 0) {
return true;
}
return (user.name.toLowerCase().indexOf(this.searchName.toLowerCase()) > -1);
},
filterByAge : function (user) {
// no operator selected or no age typed, don't filter :
if (this.searchOperator.length === 0 || this.age.length === 0) {
return true;
}
if (this.searchOperator === '>') {
return (user.age > this.age);
} else if (this.searchOperator === '<') {
return (user.age < this.age);
}
},
orderBy : function (userA, userB) {
let condition = (userA[this.sortKey] > userB[this.sortKey]);
if (this.reverse) {
return !condition;
} else {
return condition;
}
}
},
computed: {
filteredPersons: function () {
return this.users
.filter(this.filterByName)
.filter(this.filterByAge)
.sort(this.orderBy);
}
},
});
body {
margin: 2em 0;
}
a {
font-weight: normal;
color: blue;
}
a.active {
font-weight: bold;
color: black;
}
<html>
<head>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css"/>
<script src="https://unpkg.com/vue#2.5.16/dist/vue.js"></script>
<script src="index.js" defer></script>
</head>
<body>
<div id="demo" class="container">
<div class="row">
<div class="col-md-6">
<input v-model="searchName" class="form-control" placeholder="Username to search">
</div>
<div class="col-md-1">
<select class="form-control" v-model="searchOperator">
<option value=">">></option>
<option value="<"><</option>
</select>
</div>
<div class="col-md-5">
<input v-model="age" class="form-control" placeholder="Age" type="number">
</div>
</div>
<table class="table table-striped">
<thead>
<tr>
<th v-for="column in columns">
<!-- <a href="#" v-on="click: sortBy(column)" >-->
<!-- <a href="#"> -->
<a href="#" v-on:click="sortBy(column)" v-bind:class="{active: sortKey == column}">
<!--{{ column | capitalize }}-->
{{column}}
</a>
</th>
</tr>
</thead>
<tbody>
<tr v-for="user in filteredPersons">
<td>{{ user.name }}</td>
<td>{{ user.age }}</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
I am writing and application using AngularJs, HTML and PHP. In the application I have a dropdown list, which when an option is selected a table is populated with related data drawn from a database. I can add to or delete info from the table. That works perfectly. However, the challenge I have is that each time the table is edited, the web page refreshes/route is reloaded and the drop down list returns to its default value. The effect of this is the user having to manually reselect the dropdown list value to carryout each operation on the table.
How can I alter the code to keep/persist the last selected option value and have the said dropdown list option selected when the webpage/route reloads?
I have seen many help responses to setting initial default value for dropdown lists. However that is not what I am trying to achieve. I am trying to set the post reload value or value after the webpage reloads.
Below is a part of what I have tried. I am at my whits end trying to figure it out. Any help is appreciated.
EDIT
Problem Solved
HTML before changes
<select id="b_year" ng-options="b.year for b in budgets track by b.year" ng-model="b.selectedBudget" ng-change="getBudgetItems(b.selectedBudget.year)" required><option value="">-- Choose budget year --</option></select>
HTML with solution changes
<select id="b_year" ng-options="b.year for b in budgets" ng-model="b.selectedBudget" ng-init="b.selectedBudget" ng-selected="getBudgetItems(b.selectedBudget.year)" required><option value="">-- Choose budget year --</option></select>
Controller snippet before changes
$scope.reloadRoute();
$scope.items.push({'line_item': li.line_item, 'weight': li.weight, 'allocated': li.allocated});
localStorage['b_year'] = year; //Store the selected year in local storage for later use
budget_size = server.getBudgetSize();
for(var i = 0; i < budget_size; i++){
if($scope.budgets[i].year === localStorage['b_year']){
$scope.b.selectedBudget[i] = localStorage['b_year'];
}else{
}
}
Controller with solution changes
app.controller(.... {
....
(function(){
$http.get( //Get info on budgets
"http://localhost/TrGway/getbudgetinfo.php"
)
.success(function(data){
$scope.budgets = data.budgets;
for(var i = 0; i < data.budgets.length; i++){
if($scope.budgets[i].year === selectedYear){
$scope.b = {};
$scope.b.selectedBudget = $scope.budgets[i];
}
}
})
.error(function(data){
$scope.message = "Error!! Getting Budget Info Failed: " + data;
});
})();
....
$scope.$watch("b.selectedBudget.year", function(item) {
localStorage.setItem("selectedYear", item);
});
});
App.controller('budgetCtrl', ['$scope', '$http', '$location', '$window', '$route', 'BudgetService', function($scope, $http, $location, $window, $route, BudgetService){
(function(){
$http.get( //Get info on budgets
"http://localhost/TrGway/getbudgetinfo.php"
)
.success(function(data){
server.setBudgets(data.budgets);//Set the budget info in the BudgetService for
//later usage anywhere in the program;
$scope.budgets = data.budgets;
budget_size = server.getBudgetSize();
server.getBudgetInfo();
})
.error(function(data){
$scope.message = "Error!! Getting Budget Info Failed: " + data;
});
})();
(function(){ //Get Line Items Info to populate the table
$http.get(
"http://localhost/TrGway/lineItemInfo.php"
)
.success(function(data){
server.setLineItems(data.items);
$scope.items = data.items;
server.getLineItemsInfo();
})
.error(function(data){
$scope.message = "Error!! Getting Line Items Info Failed: "+data;
});
})();
$scope.addBudget = function(budget){
if( budget.year < new Date().getFullYear() ){
alert("Budgets cannot be created retroactively.\n\
\nPlease enter a budget year greater than "+ new Date().getFullYear() );
$scope.budget.year = "";
}else{
server.addBudget(budget);
server.getBudgetInfo();
$scope.budgets = server.getBudgets();
$scope.budget = {};//Clear the add budget form
}
};
$scope.getBudgetItems = function(year){
/*This is where info on the budget for the selected year is retrieved and passed to the table
This works great every time*/
if( year === undefined ){ //If no year is selected do nothing
$scope.budget_amount = 0;
$scope.budget_amount_used = 0;
$scope.budget_amount_remaining = 0;
}else{
Budgets = server.getBudgets();
budget_size = server.getBudgetSize();
for(var i = 0; i < budget_size; i++){
if(Budgets[i].year === year){
$scope.budget_amount = Budgets[i].starting_amount;
$scope.budget_amount_used = Budgets[i].amount_used;
$scope.budget_amount_remaining = Budgets[i].running_balance;
amount_remaining = Budgets[i].starting_amount - Budgets[i].amount_used;
percent_used += (Budgets[i].amount_used / Budgets[i].starting_amount * 100);
server.setSelectedBudget(Budgets[i]);
}else{
//$scope.budget_amount = 0;
}
}
server.setPercentUsed(percent_used);
server.setPercentRemaining( 100 - percent_used);
server.setAmountRemaining(amount_remaining);
}
};
/*======================= THIS IS WHERE THE CHALLENGE IS ====================*/
$scope.addLineItem = function(li, b){
/*This is where the List items are added to the table before page reload*/
if(($scope.budget_amount_used-0 + li.allocated-0) > $scope.budget_amount){
alert("Budgeted amount exceeded!!!\nPlease consider revising");
$location.path('editbudget');
$scope.li.weight = 0;
$scope.li.allocation = 0;
}else{
server.setSelectedBudgetYear(b.selectedBudget.year); //Save selected year to a variable
server.addLineItem(li, b); //Add the new Line items row to database
$scope.reloadRoute();
$scope.items.push({'line_item': li.line_item, 'weight': li.weight, 'allocated': li.allocated});
$scope.b = {};
//$scope.b.selectedBudget = localStorage.getItem("selected");
$scope.$watch("b.selectedBudget.year", function(item) {
localStorage.setItem("selected", item);
});
//$scope.b.selectedBudget.year = server.getSelectedBudgetYear(); //I tried this also
}
$scope.li.line_item = "";
$scope.li.weight = 0;
$scope.li.allocation = 0;
$scope.b.selectedBudget = localStorage.getItem("selected");
/* LEAVING THIS SO YOU CAN SEE WHAT ELSE I HAVE TRIED
$scope.b = {};
$scope.b.selectedBudget = localStorage.getItem("selectedBudget");
$scope.watch("b.selected", function(item) {
localStorage.setItem("selected", item);
});
$scope.b.selectedBudget = localStorage['b_year'];
*/
/* LEAVING THIS SO YOU CAN SEE WHAT ELSE I HAVE TRIED
budget_size = server.getBudgetSize();
for(var i = 0; i < budget_size; i++){
if($scope.budgets[i].year === server.getSelectedBudgetYear()){
$scope.b.selectedBudget.year = server.getSelectedBudgetYear();
}else{
}
}*/
};
$scope.deleteLineItem = function(row, b){
$http({
method: "post",
url: "http://localhost/TrGway/deleteLineItem.php",
crossDomain: true,
data:{
'seq': row.seq,
'year': row.year,
'allocated':row.allocated,
'starting_amount': b.selectedBudget.starting_amount,
'amount_used': b.selectedBudget.amount_used,
'running_balance': b.selectedBudget.running_balance
},
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
})
.success(function(response){
if(response.toString() === "success"){
var index = -1;
var itemsArr = eval( $scope.items );
for( var i = 0; i < itemsArr.length; i++ ) {
if( itemsArr[i].seq === row.seq ) {
index = i;
break;
}
}
if( index === -1 ) {
alert( "Something has gone wrong!!\nLine item was not deleted.\nPlease try again" );
}
server.reset();
server.reload();
$scope.budget_percent_used = server.getPercentUsed()-0;
$scope.budget_percent_remaining = server.getPercentRemaining()-0;
$scope.budget_amount_used = server.getAmountUsed()-0;
$scope.budget_amount_remaining = server.getAmountRemaining()-0;
server.setTotalWeighting($scope.totalweighting + row.weight)-0;
server.setPercentUsed(server.getTotalWeighting())-0;
server.setPercentRemaining(100 - server.getPercentUsed())-0;
$scope.items.splice( index, 1 ); //Remove the row with matching index/seq value
$scope.totalweighting = server.getTotalWeighting();
$scope.budget_percent_used = server.getPercentUsed()-0;
$scope.budget_percent_remaining = server.getPercentRemaining()-0;*/
$scope.reloadRoute();
}else{ //If deleting the line item failed
$location.path('editbudget');
$scope.budgetmessage = "Line Item Update Failed: "+response;
}
})
.error(function(response){ //If there was an error connecting to the server
$scope.budgetmessage = "Error!! Updating Line Item Failed: "+response;
});
row.isEditing = false;
};
$scope.updateLineItem = function(row){
$http({
method: "post",
url: "http://localhost/TrGway/updateLineItem.php",
crossDomain: true,
data:{
'seq': row.seq,
'line_item': row.line_item,
'weight': row.weight,
'allocated': row.allocated
},
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
})
.success(function(response){
if(response.toString() === "success"){
$location.path('editbudget');
}else{
$location.path('editbudget');
$scope.budgetmessage = "Line Item Update Failed: "+response;
}
})
.error(function(response){
$scope.budgetmessage = "Error!! Updating Line Item Failed: "+response;
});
row.isEditing = false;
};
$scope.cancelLineItemEditing = function(row){
row.isEditing = false;
};
$scope.updateBudget = function(b){
$http({
method: "post",
url: "http://localhost/TrGway/updatebudget.php",
crossDomain: true,
data:{
'year': b.selectedBudget.year,
'starting_amount': b.edit_starting_amount
},
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
})
.success(function(response){
if(response.toString() === "success"){
server.setStartingAmount(b.edit_starting_amount);
var line_items = server.getLineItems();
var start_amount = server.getStartingAmount();
var total_weight = 0;
var amount_used = 0;
var amount_remaining = 0;
var percent_used = 0;
var percent_remaining = 0;
for(var i = 0; i < line_items.length; i++){
//Update the line items on the page immediately
line_items[i].weight = ( line_items[i].allocated / start_amount) * 100;
total_weight += line_items[i].weight;
amount_used += line_items[i].allocated-0;
//Update the line items in the database immediately
$scope.updateLineItem(line_items[i]);
}
server.load();
percent_used = ( amount_used / start_amount) * 100;
amount_remaining = start_amount - amount_used;
percent_remaining = 100 - percent_used;
server.setLineItems(line_items);
server.setTotalWeighting(total_weight);
server.setAmountUsed(amount_used);
server.setPercentUsed(percent_used);
server.setAmountRemaining(amount_remaining);
server.setPercentRemaining(percent_remaining);
$scope.items = server.getLineItems();
$scope.total = 0;
$scope.total = server.getStartingAmount();
$scope.totalweighting = server.getTotalWeighting()-0;
$scope.budget_amount_used = server.getAmountUsed()-0;
$scope.budget_percent_used = server.getPercentUsed()-0;
$scope.budget_percent_remaining = server.getPercentRemaining()-0;
$scope.budget_amount_remaining = server.getAmountRemaining()-0;
$location.path('editbudget');
}else{
$location.path('editbudget');
$scope.budgetmessage = "Budget Update Failed: "+response;
}
})
.error(function(response){
$scope.budgetmessage = "Error!! Updating Budget Failed: "+response;
});
$scope.b = {};
};
$scope.reloadRoute = function(){
$route.reload();
server.reload();
//alert('Loaded!!');
};
}]);
<div class="breadcrumbs" id="breadcrumbs">
<ul class="breadcrumb">
<li>Home</li>
<li class="active">Add/Edit line Items</li>
<li style="float: right" ng-controller="authCtrl"><a ng-click="logout();">Logout</a></li>
<li style="float: right">Back</li>
</ul>
</div>
<div class="row" ng-cloak="" ng-controller="budgetCtrl">
<div class="col-md-5">
<section>
<form name="LineItemForm" class="form-horizontal" role="form">
<fieldset>
<table class="table table-striped">
<thead>
<tr>
<th style="width: 150px" colspan="1">Select Budget Year To Edit Line Items</th>
</tr>
</thead>
<tbody>
<tr>
<td colspan="1">
<div class="col-md-7">
<!-- THIS IS WHERE THE YEAR IS SELECTED -->
<select id="b_year" ng-options="b.year for b in budgets track by b.year" ng-model="b.selectedBudget" ng-change="getBudgetItems(b.selectedBudget.year);" ng-selected="" required>
<option value="">-- Choose budget year --</option>
</select>
</div>
</td>
</tr>
<tr>
<th style="width: 150px" colspan="1">{{b.budgetmessage}}</th>
</tr>
<tr>
<td>
<h3>Add Line Item Here</h3>
<div class="form-group">
<label class="col-md-3 control-label no-padding-right" for="line_item">Line Item</label>
<div class="col-md-7">
<input type="text" class="form-control" name="line_item" id="line_item" ng-model="li.line_item" ng-disabled="!b.selectedBudget" required/>
</div>
</div>
<div class="form-group">
<label class="col-md-3 control-label no-padding-right" for="weight">weighting %</label>
<div class="col-md-7">
<input type="number:2" class="form-control" name="weight" id="weight" ng-init="li.weight = 0" ng-model="li.weight" ng-disabled="!b.selectedBudget || !li.line_item || li.line_item.length<5" required/>
</div>
</div>
<div class="form-group">
<label class="col-md-3 control-label no-padding-right" for="allocated">Allocated $</label>
<div class="col-md-7">
<input type="number:2" class="form-control" name="allocated" id="allocated" placeholder="{{li.allocated = (budget_amount * li.weight) / 100 | currency}}" ng-init="li.allocated=0" ng-model="li.allocated" ng-disabled="!b.selectedBudget || !li.weight" required />
</div>
</div>
<div class="form-group" style="padding-right: 100px;">
<span class="lbl col-sm-5"> </span>
<div class="col-md-7 message" >
<span id="message" class="block input-icon input-icon-right">{{message}}</span>
</div>
</div>
<div class="form-group">
<div style="padding-right:100px">
<button type="submit" class="width-35 pull-right btn btn-sm btn-primary" ng-click="addLineItem(li, b)" data-ng-disabled="LineItemForm.$invalid || li.weight===0 || li.line_item.length < 5">
Add Line Item
</button>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</fieldset>
</form>
</section>
</div>
<div>
<section>
<div class="col-lg-14" style="float: right">
<form>
<fieldset>
<table class="table table-striped" >
<thead>
<tr>
<!--<th>Index</th>-->
<th>Line Items</th><th style="width: 150px">% Weighting</th><th style="width: 150px">Allocation $</th><th style="width: 120px">Tasks</th><!---->
</tr>
</thead>
<tbody>
<tr data-ng-repeat="row in items" ng-model="items" ng-if="row.year === b.selectedBudget.year"><!--<td>{{$index+1}}</td>-->
<td title="'Line Item'" ng-switch="row.isEditing" ng-class="line_item.$dirty ? 'bg-warning' : ''" ng-form="name" demo-tracked-table-cell>
<span ng-switch-default class="editable-text">{{row.line_item}}</span>
<div class="controls" ng-class="line_item.$invalid && line_item.$dirty ? 'has-error' : ''" ng-switch-when="true">
<input type="text" name="line_item" ng-model="row.line_item" class="editable-input form-control input-sm" required />
</div>
</td>
<td>{{ row.weight = row.allocated / budget_amount * 100 | number: 2 }}</td>
<td title="'Allocation'" ng-switch="row.isEditing" ng-class="allocated.$dirty ? 'bg-warning' : ''" ng-form="allocated" demo-tracked-table-cell>
<span ng-switch-default class="editable-text">{{ row.allocated | currency }}</span>
<div class="controls" ng-class="allocated.$invalid && allocated.$dirty ? 'has-error' : ''" ng-switch-when="true">
<input type="text" name="allocated" ng-model="row.allocated" class="editable-input form-control input-sm" required/>
</div>
</td>
<td>
<button class="btn btn-primary btn-sm" ng-click="updateLineItem(row)" ng-if="row.isEditing" ng-disabled="rowForm.$pristine || rowForm.$invalid"><span class="glyphicon glyphicon-ok"></span></button>
<button class="btn btn-default btn-sm" ng-click="cancelLineItemEditing(row)" ng-if="row.isEditing"><span class="glyphicon glyphicon-remove"></span></button>
<button class="btn btn-default btn-sm" ng-click="row.isEditing = true" ng-if="!row.isEditing"><span class="glyphicon glyphicon-pencil"></span></button>
<button class="btn btn-danger btn-sm" ng-click="deleteLineItem(row, b)" ng-if="!row.isEditing"><span class="glyphicon glyphicon-trash"></span></button>
</td>
</tr>
<tr>
<td>
<h5>
<span class="block input-icon input-icon-right">
<b>TOTAL (Budget Estimate)</b>
</span>
</h5>
</td>
<td>
<h5>
<span class="block input-icon input-icon-right">
<b>{{ totalweighting = budget_amount > 0 ? 100 : 0 | number:2 }}%</b><!---->
</span>
</h5>
</td>
<td>
<h5>
<span class="block input-icon input-icon-right">
<b>{{ budget_amount | currency }}</b>
</span>
</h5>
</td><td></td>
</tr>
<tr>
<td>
<h5>
<span class="block input-icon input-icon-right">
<b>BUDGET AMOUNT USED</b>
</span>
</h5>
</td>
<td>
<h5>
<span class="block input-icon input-icon-right">
<b>{{ budget_percent_used = budget_amount_used > 0 ? budget_amount_used * 100/budget_amount : 0 | number:2 }}%</b>
<!--Adding the -0 forces the compiler to treat the value as a number-->
</span>
</h5>
</td>
<td>
<h5>
<span class="block input-icon input-icon-right">
<!--{{ budget_amount_used + li.allocated | currency }}-->
<b>{{ budget_amount_used | currency }}</b>
</span>
</h5>
</td><td></td>
</tr>
<tr>
<td>
<h5>
<span class="block input-icon input-icon-right">
<b>BUDGETED AMOUNT REMAINING</b>
</span>
</h5>
</td>
<td>
<h5>
<span class="block input-icon input-icon-right">
<b>{{ budget_percent_remaining = 100 - budget_percent_used | number:2 }}%</b>
<!--Adding the -0 forces the compiler to treat the value as a number-->
</span>
</h5>
</td>
<td>
<h5>
<span class="block input-icon input-icon-right">
<!--{{ budget_amount_remaining - li.allocated | currency }}-->
<b>{{ budget_amount_remaining | currency }}</b>
</span>
</h5>
</td><td></td>
</tr>
</tbody>
</table>
</fieldset>
</form>
</div>
</section>
</div>
</div>
I'd do something like this:
app.controller(.... {
....
$scope.b = {};
$scope.b.selected = localStorage.getItem("selected");
$scope.watch("b.selected", function(item) {
localStorage.setItem("selected", item);
});
....
});
EDIT
So we suspect that the initial value gets overwritten somewhere. Here is some help how to debug it: You can define a property setter on $scope.b, that could alert or break to debugger. Use something like this:
$scope.b = {
set selectedBudget(v) {
debugger;
// or alert(1);
}
};
Solution
This is the solution I came up with to keep the drop down list selection after webpage refresh. I hope this will help someone. I have added comments to help explain what I did. I hope they are clear. However, I welcome any correction or clarification. I was able to come up with this solution , thanks to help from #Tamas Hegedus, who pointed me in the right direction.
app.controller(.... {
....
(function(){
$http.get( //Get info on budgets
"http://localhost/TrGway/getbudgetinfo.php"
)
.success(function(data){
$scope.budgets = data.budgets; //assign array of elements to the scope variable
for(var i = 0; i < data.budgets.length; i++){ //Loop through each array element
if($scope.budgets[i].year === selectedYear){ //Check if any of the 'year' value in the array match the value stored in localStorage
$scope.b = {}; //reset the selected scope variable
$scope.b.selectedBudget = $scope.budgets[i]; //Assign the array element with the matching year, as the selected scope element
}
}
})
.error(function(data){
$scope.message = "Error!! Getting Budget Info Failed: " + data;
});
})();
....
$scope.$watch("b.selectedBudget.year", function(item) { //Set watch on array element 'year' for changes/selection
localStorage.setItem("selectedYear", item); //When an element is selected from the drop down list, its value is stored in localStorage
});
});
<select id="b_year" ng-options="b.year for b in budgets" ng-model="b.selectedBudget" ng-selected="getBudgetItems(b.selectedBudget.year)" required><option value="">-- Choose budget year --</option></select>