I have this select field:
<select data-bind="foreach: $root.feeGroups, value: $root.selectedFee">
<optgroup data-bind="attr: {label: label}, foreach: fees">
<option data-bind="text: description, option: $data"></option>
</optgroup>
</select>
The feesGroup property:
self.feeGroups([
{ label: "NEW", fees: self.fees().filter(f => f.status === "New") },
{ label: "OLD", fees: self.fees().filter(f => f.status === "Old") }
]);
And the binding handler:
ko.bindingHandlers.option = {
update: function(element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
console.log(ko.toJSON(value));
ko.selectExtensions.writeValue(element, value);
}
};
My issue is with the "optionsCaption", as I am using a foreach method to generate the inner options it doesn't automatically work like it would if I was able to use the "Options" binding. But I do need to have a "Please Select..." default option.
Is there a way to do it?
You can move the foreach binding to a virtual element and add an extra option in your view that represents the null value:
<select data-bind="value: selectedFee">
<option data-bind="text: 'Select an option', option: null"></option>
<!-- ko foreach: feeGroups -->
...
<!-- /ko -->
</select>
Keep in mind that the selectedFee observable will contain null when the placeholder is active.
// From: https://stackoverflow.com/a/11190148/3297291
ko.bindingHandlers.option = {
update: function(element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
ko.selectExtensions.writeValue(element, value);
}
};
const fees = [
{ type: "old", description: "test1" },
{ type: "old", description: "test2" },
{ type: "new", description: "test3" },
{ type: "new", description: "test4" }
];
const feeGroups = [
{ label: "new", fees: fees.filter(f => f.type === "new") },
{ label: "old", fees: fees.filter(f => f.type === "old") }
];
const selectedFee = ko.observable(null);
selectedFee.subscribe(console.log.bind(console, "new selection:"));
ko.applyBindings({ feeGroups, selectedFee });
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<select data-bind="value: selectedFee">
<option data-bind="text: 'Select an option', option: null"></option>
<!-- ko foreach: feeGroups -->
<optgroup data-bind="attr: {label: label}, foreach: fees">
<option data-bind="text: description, option: $data"></option>
</optgroup>
<!-- /ko -->
</select>
Related
I have a Vue component with 5 input elements. As a exercise to learn VueX I wanted to manage the user input in a Vuex store. Let's assume each input represents a line in a poem. My state, mutation and actions look like that
state: {
poem: {
line1: '',
line2: '',
line3: '',
line4: '',
line5: '',
}
},
mutations: {
setPoem(state, line) {
state.poem = {...state.poem, ...line}
},
resetPoem(state) {
state.poem = {
line1: '',
line2: '',
line3: '',
line4: '',
line5: '',
}
}
},
actions: {
setPoem({commit}, line) {
commit('setPoem', line)
},
resetPoem({commit}) {
commit('resetPoem')
},
},
Looking the documentation I found that I could use v-model as usual but with a two-way computed property: https://next.vuex.vuejs.org/guide/forms.html#two-way-computed-property
But it seems not very DRY to create a computed property for each input element like to:
computed: {
line1: {
get() {
return this.$store.state.poem.line1;
},
set(value) {
this.$store.dispatch('setPoem', {line1: value})
}
},
line2: {
get() {
return this.$store.state.poem.line2;
},
set(value) {
this.$store.dispatch('setPoem', {line2: value})
}
},
line3: {
get() {
return this.$store.state.poem.line3;
},
set(value) {
this.$store.dispatch('setPoem', {line3: value})
}
},
line4: {
get() {
return this.$store.state.poem.line4;
},
set(value) {
this.$store.dispatch('setPoem', {line4: value})
}
},
line5: {
get() {
return this.$store.state.poem.line5;
},
set(value) {
this.$store.dispatch('setPoem', {line5: value})
}
}
},
My template looks like this:
<form class="form-group" v-on:submit.prevent="addDocument">
<input v-model="line1" type="text" />
<p class="error">{{errorMsg1}}</p>
<input v-model="line2" type="text" />
<p class="error">{{errorMsg2}}</p>
<input v-model="line3" type="text" />
<p class="error">{{errorMsg3}}</p>
<input v-model="line4" type="text" />
<p class="error">{{errorMsg4}}</p>
<input v-model="line5" type="text" />
<p class="error">{{errorMsg5}}</p>
<button type="submit">Send Poem</button>
</form>
How can I refactor this? Is there a best practice to manage state of multiple forms?
You can use vuex-map-fields
<script>
import { mapFields } from 'vuex-map-fields';
export default {
computed: {
...mapFields([
'poem.line1',
'poem.line2',
'poem.line3',
// ...
]),
},
};
</script>
and in your store, you can import the getField and updateField to fetch and mutate data
...
getters: {
getField,
},
mutations: {
updateField,
}
I am trying to clear a form after I submit it, in this case creating a simple user. I am resetting the state with vuex (see below). But the form stays with data.
this is how the form looks like
<form #submit.prevent="onSubmit" v-if="!loading">
<div class="form-group">
<input placeholder="Name" v-model="user.name" type="text" name="name" class="form-control">
<span class="invalid-feedback" v-if="errors.name">{{ errors.name }}</span>
</div>
<div class="form-group">
<input v-bind:class="{ harError: errors.email }" placeholder="Email" v-model="user.email" type="email" name="email" class="form-control" id="validationCustom03">
<span class="invalid-feedback" v-if="errors.email">{{ errors.email }}</span>
</div>
...
the onSubmit method
/**
* on submitting the form update or crete a user
*/
onSubmit() {
let action = this.id ? 'UPDATE_USER' : 'CREATE_USER';
this.inProgress = true;
this.$store
.dispatch(action)
.then(() => {
console.log('reset or not?');
this.inProgress = false;
// navigate to user
this.$router.push('users');
})
.catch( ({ response }) => {
this.inProgress = false;
this.errors = response.data.errors;
console.log('you have an error on creating an user')
});
},
Resetting the
RESET_STATE({state}) {
console.log('reset state');
for (let f in state) {
Vue.set(state, f, initialState[f]);
}
},
the state like this
const initialState = {
users: [],
user: {
name: '',
email: '',
password: '',
type: '',
bio: '',
photo: '',
active: '1',
},
loading: false,
};
export const store = new Vuex.Store({
namespaced: true,
state: { ...initialState },
...
The input types stais with data
Ok At least I figured out myself, Insead of a const I used a function to set the initialtState like so
function initialState () {
return {
users: [],
user: {
name: '',
email: '',
password: '',
type: '',
bio: '',
photo: '',
active: '1',
},
loading: false,
}
}
export const store = new Vuex.Store({
namespaced: true,
state: { ...initialState() },
then in the mutations i assigned the intitialState to the state
mutations: {
/**
*
* #param state
* #constructor
*/
RESET_STATE: (state) => {
Object.assign(state, initialState())
},
in my user component I dispached it like so
...mapActions(['RESET_STATE']),
...
this.$store.dispatch("RESET_STATE");
My form can not update data which it got from server to form's record.
Below is my w2form
$().w2form({
name: 'editSv',
method: 'GET',
style: 'border: 0px; background-color: transparent;',
recid: w2ui['grid'].records[w2ui['grid'].getSelection(true)]['id'],
url: {
get: '/api/Test/GetService/' + w2ui['grid'].records[w2ui['grid'].getSelection(true)]['id'],
save: '/api/Test/PostService'
},
formURL: '/api/Test/GetService/' + w2ui['grid'].records[w2ui['grid'].getSelection(true)]['id'],
fields: [
{ name: 'id', type: 'number', required: true },
{ name: 'servicename', type: 'text', required: true },
{ name: 'price', type: 'number', required: true },
{ name: 'unit', type: 'text' }
],
record: {
id: 0,
servicename: '',
price: 0,
unit: ''
}, onSubmit: function (formName, formObj) {
formObj.postData = formObj.postData.record;
},
onLoad: function (event) {
console.log(event.xhr);
},
actions: {
"save": function () {
var obj = this;
this.submit({}, function (data) {
if (data.status == 'success') {
w2alert(data.status);
w2ui['grid'].reload();
} else {
w2alert(data.message);
return;
}
obj.clear();
});
},
"reset": function () { this.clear(); },
}
});
This is data got from onLoad() event
{readyState: 4, responseText: "{"id":5,"servicename":"4","price":4.0000,"unit":"4"}", status: 200, statusText: "OK"}
And the message from chrome's console:
ERROR: Cannot associate field "id" with html control. Make sure html control exists with the same name.
ERROR: Cannot associate field "servicename" with html control. Make sure html control exists with the same name.
ERROR: Cannot associate field "price" with html control. Make sure html control exists with the same name.
ERROR: Cannot associate field "unit" with html control. Make sure html control exists with the same name.
I have tried to add formHTML to w2form but it makes no sense. Has anyone solved this problem?
Have you defined any HTML page for your w2ui form in this case you can use kickstart or you can define formHTML in your form:
$().w2form({
name: 'editSv',
style:'border: 0px',
focus:-1,method: 'GET',
style: 'border: 0px; background-color: transparent;',
recid: w2ui['grid'].records[w2ui['grid'].getSelection(true)]['id'],
url: {
get: '/api/Test/GetService/' + w2ui['grid'].records[w2ui['grid'].getSelection(true)]['id'],
save: '/api/Test/PostService'
},
formURL: '/api/Test/GetService/' + w2ui['grid'].records[w2ui['grid'].getSelection(true)]['id'],
formHTML:
'<div class="w2ui-page page-0">'+
' <div class="w2ui-field">'+
' <label> id </label>'+
' <div>'+
' <input name="id" type="text" />'+
' </div>'+
' </div>'+ ' <div class="w2ui-field">'+
' <label> id </label>'+
' <div>'+
' <input name="id" type="text" />'+
' </div>'+
' </div>'+ ' <div class="w2ui-field">'+
' <label> price </label>'+
' <div>'+
' <input name="price" type="text" />'+
' </div>'+
' </div>'+ ' <div class="w2ui-field">'+
' <label> unit </label>'+
' <div>'+
' <input name="unit" type="text" />'+
' </div>'+
' </div>'+
'<div class="w2ui-buttons">'+
' <button class="btn" name="Cancel">Cancel</button>'+
' <button class="btn" name="Save">Save</button>'+
'</div>',
fields: [
{ name: 'id', type: 'number', required: true },
{ name: 'servicename', type: 'text', required: true },
{ name: 'price', type: 'number', required: true },
{ name: 'unit', type: 'text' }
],
record: {
id: 0,
servicename: '',
price: 0,
unit: ''
}, onSubmit: function (formName, formObj) {
formObj.postData = formObj.postData.record;
},
onLoad: function (event) {
console.log(event.xhr);
},
actions: {
"save": function () {
var obj = this;
this.submit({}, function (data) {
if (data.status == 'success') {
w2alert(data.status);
w2ui['grid'].reload();
} else {
w2alert(data.message);
return;
}
obj.clear();
});
},
"reset": function () { this.clear(); },
}
});
Pls help basically I want to filter my datatable using form.
each input has a value that would affect the query with submitted form.
here is my JQUERY.
$("#submit").click(function (e) {
$('#table').dataTable
({
"sAjaxSource": "index.php/report/get_report",
"sServerMethod": "POST",
'fnServerData': function (url, data, callback) {
// Add new data
dataString = $("#myform").serialize();
$.ajax({
'url': "index.php/report/get_report",
'data': dataString,
'type': 'POST',
'success': callback,
'dataType': 'json',
'cache': true
});
},
'bServerSide' : true,
"aaSorting": [[ 3, "desc" ]],
"bPaginate": true,
"bSortClasses": false,
"bAutoWidth": true,
"bInfo": true,
"iDisplayLength" : 3,
"bScrollCollapse": true,
"oLanguage": {
"sSearch": "Search:"
},
"bDestroy": true
});
});
and this is my HTML FORM
<form name="myform">
<label>Employee:</label>
<input type="text" name="employeeid" id="employeeid" title="Type Employee" />
<label>Training Type: </label>
<select name="trainingtype" id="trainingtype" >
<option value="" selected="selected">All</option>
<option value="1">Externally Facilitated Training</option>
<option value="3">Internally Facilitated Training</option>
<option value="2">Webcast/E-Learning</option>
</select>
<label>Datestart</label>
<input type="text" class="field size3" name="datestart" id="datepicker_s" />
<label>Dateend </label>
<input type="text" class="field size3" name="dateend" id="datepicker_e" />
<input type="hidden" id="txtsearchid" name="txtsearchid">
<input type="button" class="button" value="Submit" id="submit" />
when I submit my form I get nothing.
Im I doing the right way?
pls help.
GOT IT
"fnServerData": function ( sSource, aoData, fnCallback ) {
//REQUIRED: Add a Post variable with the object value
aoData.push(
{ "name": "txtsearchid", "value": $( "#txtsearchid" ).val() },
{ "name": "datestart", "value": $( "#datepicker_s" ).val() },
{ "name": "dateend", "value": $( "#datepicker_e" ).val() },
{ "name": "trainingtype", "value": $( "#trainingtype" ).val() }
);
$.ajax( {
dataType: 'json',
type: "POST",
url: sSource,
data: aoData ,
success: fnCallback
} );
},
this must be the solution for my problem. I didnt used the serialized instead of push
Use Firebug add ons from Mozilla to check the error
I have a dropdownlist in my page this is the code:
<div id="test">
Role: <span data-bind="text: role"></span>
</br>
Roles:<select id="roles" data-bind="source: roles, value: role" data-text-field="roleName" data-value-field="roleId" ></select>
<ul id="view" data-template="Access-template" data-role="listview" data-bind="source:Accesses"></ul>
<script id="Access-template" type="text/x-kendo-template">
<li>
<input type="checkbox" data-bind="checked: selected" />
<label data-bind="text: AccessName" />
</li>
</script>
</div>
and I want that when I change the dropdownlist value it changes my role collection. This is my code:
var Accesses = [{
AccessName: 'Create',
selected: false
}, {
AccessName: 'Delete',
selected: false
}, {
AccessName: 'Update',
selected: true
}];
var Roles = [{
roleName: "Admin",
roleId: 1,
accessItem: Accesses
}, {
roleName: "User",
roleId: 2,
accessItem: Accesses
}];
var viewModel = kendo.observable({
roles: Roles,
accssesItem: Roles.accessItem
});
kendo.bind($("#test"), viewModel);
Figuring out what DropDown item is selected is a little... unintuitive, but here is what you want:
<script id="Access-template" type="text/x-kendo-template">
<li>
<input type="checkbox" data-bind="checked: selected" />
<label data-bind="text: AccessName" />
</li>
</script>
<select
data-role="dropdownlist"
data-bind="source: roles, events: { select: roleSelected }"
data-text-field="roleName"
data-value-field="roleId"></select>
<ul data-template="Access-template"
data-role="listview"
data-bind="source: accessItem"></ul>
...and...
$(document).ready(function () {
var roles = [{
roleName: "Admin",
roleId: 1,
accessItem: [{
AccessName: 'Create',
selected: true
}, {
AccessName: 'Delete',
selected: true
}, {
AccessName: 'Update',
selected: true
}]
}, {
roleName: "User",
roleId: 2,
accessItem: [{
AccessName: 'Create',
selected: false
}, {
AccessName: 'Delete',
selected: false
}, {
AccessName: 'Update',
selected: true
}]
}];
var viewModel = kendo.observable({
roles: roles,
accessItem: roles[0].accessItem,
roleSelected: function (e) {
this.set("accessItem", this.roles[e.item.index()].accessItem);
}
});
kendo.bind("body", viewModel);
});
Working jsFiddle here: http://jsfiddle.net/rally25rs/JHNm6/