I have a listview that I bind manually:
$("#List").kendoListView({
template: kendo.template($("#ItemTemplate").html()),
autoBind: false,
dataSource: new kendo.data.DataSource({
data: []
})
});
Initially I bind the list and later I load it with some data:
$("#ActButton").on("click", function(e) {
.
.
var list = $("#List").data("kendoListView");
list.dataSource.data(data);
list.refresh();
In my template being bound I have a control that I want to initialize as a numeric textbox. I was hoping to use data attributes, but it doesn't recognize:
<input type="text" name="NewAmount" data-role="numerictextbox"
data-format="##.####" data-decimals="2" data-spinners="false"
min="0" max="#= Amount #" value="" />
How can I initialize the numeric textbox within a listview?
Note: I'm not using MVVM so I was using kendo.bind("body") to wire up the UI initially, but not using data-bind for the additional wire up of data.
I'm previously work on similar problem, i have a dojo example where i have a list view (kind of) and custom template with show/hide textbox and numeric textbox input. Click on the Product Name or Amount will make the textbox or numericTextbox visible, changing the textbox or numericTextbox value will also update the label value as it refering to the same thing on the dataSource.
First of all i'm usually work with kendo observable as vm like
var vm = kendo.observable({
dataSource: new kendo.data.DataSource({
Id: "id",
data: []
}),
act: function(){
var data = [
{id:1, productName: "Item A", amount: 1, isEditName: false, isEditAmount: false },
{id:2, productName: "Item B", amount: 2, isEditName: false, isEditAmount: false },
];
var list = $("#list").data("kendoListView");
list.dataSource.data(data);
list.refresh();
},
toggleProductName : function(e){
var editable = vm.dataSource.get($(e.currentTarget).attr("data-id")).isEditName;
vm.dataSource.get($(e.currentTarget).attr("data-id")).set("isEditName",!editable);
},
toggleAmount: function(e){
var editable = vm.dataSource.get($(e.currentTarget).attr("data-id")).isEditAmount;
vm.dataSource.get($(e.currentTarget).attr("data-id")).set("isEditAmount",!editable);
},
});
Create the listView and bind the page to vm
$("#list").kendoListView({
template: kendo.template($("#ItemTemplate").html()),
autoBind: false,
dataBound: function(e){ kendo.bind($("#list"),vm); },
dataSource: vm.dataSource
});
kendo.bind($("#example"),vm);
Then here goes the html :
<div id="example">
<div id="list"></div>
<button id="act" data-bind="click:act">Act now</button>
</div>
My item Template :
<script type="text/x-kendo-template" id="ItemTemplate">
<tr>
<td role="gridcell" style="width:200px">
<input type="text" name="NewAmount" data-bind="visible: dataSource.get(#=id#).isEditName, value: dataSource.get(#=id#).productName " style="width:100px"/>
<label data-id="#=id#" data-bind="visible: dataSource.get(#=id#).isEditName, click: toggleProductName"> close </label>
<label data-id="#=id#" data-bind="invisible: dataSource.get(#=id#).isEditName, click: toggleProductName, text: dataSource.get(#=id#).productName"> #= productName #</label>
</td>
<td role="gridcell" style="width:200px">
<input type="text" name="NewAmount" data-role="numerictextbox"
data-format="##.####" data-decimals="2" data-spinners="false"
min="0" max="100" style="width:100px" data-bind="visible: dataSource.get(#=id#).isEditAmount, value: dataSource.get(#=id#).amount" />
<label data-id="#=id#" data-bind="visible: dataSource.get(#=id#).isEditAmount, click: toggleAmount"> close </label>
<label data-id="#=id#" data-bind="invisible:dataSource.get(#=id#).isEditAmount, click: toggleAmount, text: dataSource.get(#=id#).amount"></label>
</td>
</tr>
</script>
The main thing here is that binding the template with the vm again on databound (so it can access the vm properties as well as updating the value from the input)
I bind the row to their respective record on dataSource
This is just a workaround for me, i hope it could help you as well
Related
In my code I create a lot of elements dynamicly on serverside, store the html of these elements in a javascript object,remove them and dynamicly/conditionally add them to different parts of the page.
For one particular element I want a data binding, such that I can refer to that binding in a v-if directive. However, if I add the v-bind on the server side, it gets lost after I copy the html.
Since I do only add the elements in my javascript code, I can not register the v-bind in my template. Neither can I provide the content in a component, since it is not static but relys on the input from the server.
How do I register the binding?
Sample Code:
Dynamicly generated form elements (server side):
<div id="archive" style="display: none;">
<div><input type="text" name="purpose" v-bind:value="purpose" id="id_purpose"></div> <!-- v-bind has no effect -->
<div><input type="text" name="purpose__iexact" id="id_purpose__iexact"></div>
<div><input type="text" name="purpose__contains" id="id_purpose__contains"></div>
<div><input type="text" name="purpose__icontains" id="id_purpose__icontains"></div>
<div><input type="text" name="purpose__in" id="id_purpose__in"></div>
...
</div>
Code to copy the html:
var input = {};
var archive = document.getElementById('archive');
for(var i = 0; i < archive.children.length; i++) {
var div = archive.children[i];
input[div.firstChild.name] = div.innerHTML
}
archive.parentNode.removeChild(archive);
Template code to display a certain input field dynamicly (client side):
<div class="inline" v-html="input[SOME CONDITIONAL COMPUTATIONS]"></div>
the correct way to rendering vue scene is:
<template>
<div>
<input type="button" value="Add new item" #click="addItem">
<hr>
<div v-for="(item,index) in data" :key="index">
<span v-html="item.html"></span>
<h3>Model data {{item.model}}</h3>
<input type="text" v-model="item.model">
<input type="button" value="Click me" #click="item.action(index)">
<input v-if="item.show" type="button" value="Remove me" #click="removeItem(index)">
</br>
</div>
</div>
</template>
<script>
export default {
data() {
return {
item: {
model: "",
show:true,
html: "<b>mydata html</b>",
action: function(index) {
console.log(`Clicked ${index} element.`);
}
},
data: [
{
model: "",
show:false,
html: "<b>mydata html</b>",
action: function(index) {
alert(`Clicked ${index} element.`);
console.log(`Clicked ${index} element.`);
}
},
{
model: "",
show:true,
html: "<b>mydata html</b>",
action: function(index) {
alert(`Clicked ${index} element.`);
console.log(`Clicked ${index} element.`);
}
}
]
};
},
methods: {
addItem() {
let item = Object.assign({}, this.item); // other way dublicating Observer
this.data.push(item);
},
removeItem(index){
this.data.splice(index,1)
}
}
};
</script>
You can add a show boolean prop to item object and v-if="" atribut to div to hide it.
I hope this example will help you.
w2ui is ignoring the value on the input tag.
How do I get it to use the value?
It reads the selects just fine.
jsfiddle.net
<div id="form" style="width: 750px;">
<div class="w2ui-page page-0">
<div class="w2ui-field">
<label>First Name:</label>
<div>
<input name="first_name" type="text" value="John" />
</div>
</div>
</div>
<div class="w2ui-buttons">
<button class="w2ui-btn" name="reset">Reset</button>
<button class="w2ui-btn" name="save">Save</button>
</div>
</div>
$(function() {
$('#form').w2form({
name: 'form',
url: 'server/post',
fields: [
{ field: 'first_name', type: 'text', required: true }
],
actions: {
reset: function() {
this.clear();
},
save: function() {
this.save();
}
}
});
});
If I have to write JavaScript. How would I access the fields?
You can access the value of the input with form.record.
In your case w2ui.form.record.first_name (where form is the name of your w2form).
In your save event you can access the record with this.record, e.g.:
save: function() {
console.log(this.record);
console.log(this.record.first_name);
this.save();
}
additionally, As documentation of w2ui , you can set the value of input field
w2ui.Your_Form_Name.record['You_InputField_Name'] = The_New_value;
and then call form refresh to update both html and object, but this has issue where it clear the previous drop list select , so do set the new value with code as the following to avoid use refresh and keep pre-select in drop list
$('#InputFiled_Name').val(The_New_Value);
w2ui.Your_Form_name.record['Your_InputField_Name'] = The_New_Value;
I want to render some forms and bind them to Vue.
forms are listed on json Like this.
formslist:[
"<input type='text' name='item1' v-model='item1'>",
"<input type='text' name='item2' v-model='item2'>",
]
I could render them but couldn't bind.
Template is this.
<div v-for="form in formslist">
<div v-html="form"></div>
</div>
Later, I understood that html added by "v-html" can't use data bindings.
But then I don't have no ideas how to do this.
Would anyone teach me good solution?
Sorry for lack of words.
I wrote code like this but "item1" and "item2" are not binded.
js:
new Vue({
el: '#app',
data() {
return {
formData: {},
formslist:
// this list is got by ajax as neccessary
{
"item1": "<input type='text' name='item1' v-model='formData.item1'>",
"item2": "<input type='text' name='item2' v-model='formData.item2'>",
}
};
},
methods: {
submit() {
alert('formdata: ' + JSON.stringify(this.formData));
// submit data to backend
// e.g. axios.post(...).then(...).catch(...);
}
}
})
html:
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<div id="app">
<form name="myForm" #submit.prevent="submit">
<div class="form-group">
<label>Firstname</label>
<input class="form-control" v-model="formData.firstName" type="text">
</div>
<div class="form-group">
<label>Lastname</label>
<input class="form-control" v-model="formData.lastName" type="text">
</div>
<div class="form-group">
<label>Description</label>
<textarea class="form-control" v-model="formData.description"></textarea>
</div>
<div class="form-group" v-for="(form, name) in formslist">
<label>{{name}}</label>
<div v-html="form"></div>
</div>
<button type="submit">
Submit
</button>
</form>
</div>
Vue.js is model/data oriented - have a look at the docs to learn more about it.
So create a model where you're storing your form data and then use that model to submit the data or do anything you like with the data.
Please have a look at a basic example below or at this fiddle.
Update 01.07.2017
OK, now I understand what you're trying to do.
Please find my updated code below or at the fiddle (same link).
You could also use a form generator like vue-form-generator. It's doing the same as my demo - just fewer coding required.
How does it work?
The form will be rendered with a template that is getting the data from a model that you can get from the server.
The type of the input can't be bound with :type because that's not supported with v-model - there was an error message when I tried that. So it's a bit more to write but it should be clear how this works. Add similar template tags for other types.
To further improve the model, you could also store the top-level of your form, so you can customize that display class as well. e.g. something like:
{
formName: 'myForm',
className: 'form-horizontal',
items: [...] // same array as before
}
If you need to group some inputs or display them differently you can add more properties to the model and more logic to the template.
const formRenderModel = [{
type: 'text',
modelName: 'firstName',
className: 'form-control',
labelText: 'First name',
required: true
}, {
type: 'text',
modelName: 'lastName',
className: 'form-control',
labelText: 'Last name',
required: true
}, {
type: 'text',
modelName: 'description',
className: 'form-control',
labelText: 'Description',
required: true,
pattern: '.{10,}', // minimum 10 characters
validationMessage: 'Minimum 10 characters required'
}];
new Vue({
el: '#app',
data() {
return {
formData: {}
};
},
created() {
// later axios.get('/form-render).then(...).catch(...);
// now just a constant
this.formRenderModel = formRenderModel;
},
methods: {
submit() {
console.log('formdata', this.formData);
// submit data to backend
// e.g. axios.post(...).then(...).catch(...);
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<div id="app">
<form name="myForm" #submit.prevent="submit">
<div v-for="inputModel in formRenderModel">
<label>{{inputModel.labelText}}</label>
<template v-if="inputModel.type == 'text'">
<input type="text" v-model="formData[inputModel.modelName]"
:class="inputModel.className"
:pattern="inputModel.pattern"
:title="inputModel.validationMessage"
:required="inputModel.required" />
</template>
<!-- add more templates for radios, checkboxes, .. -->
</div>
<button type="submit">
Submit
</button>
</form>
</div>
OK, here goes.
Problem 1:
I want to, based on the radio button checked, display a tr-element or not. Threre will be 3 buttons, displaying 'unlocked achievements', 'locked', and 'all' (both locked + unlocked).
The code below shows how it looks when I try to call three different functions, each setting the tr:s visibility to true/false depending on radio button checked. (D.R.Y, I know, but right now I'm just looking for the functionality).
Problem 2:
Making the for-loop run. itemsListForFilter is declared globally, outside the filter function. itemsListForFilter is a copy of an object arrayMap which is initiallized and filled elsewhere in the code. The array contains items with - amongst other things - the boolean "radioCheck", with the default value "true", which I want to check.
When I access itemsListForFilter in the function where the copying takes place it's filled with items but...
When I try to access itemsListForFilter in the filter function it displays as having the value of null. So the copy is "lost" somewhere :)
View / HTML:
<div class="widget-header-container">
<h3 class="widget-header">Achievements</h3>
<div class="wrapper">
<input type="radio" name="appliedFilter" value="all" data-bind="checked: filterAll"/><label for="all">Show all</label>
<input type="radio" name="appliedFilter" value="unlocked" data-bind="checked: filterUnlocked"/><label for="unlocked">Unlocked</label>
<input type="radio" name="appliedFilter" value="locked" data-bind="checked: filterLocked"/><label for="locked">Locked</label>
</div>
<div><div class="widget-header-line-game1"></div><div class="widget-header-line-game2"></div><div class="widget-header-line-shadow"></div></div>
</div>
<div>
<div class="rounded-box" style="padding:15px;padding-top: 0;background-color:#fff;overflow:hidden;">
<table id="game-achievements" class="table table-condensed" style="margin-top:10px;">
<tbody data-bind="foreach: viewGame.achievements()">
<tr data-bind="visible: radioCheck" style="display: none">
Viewmodel / JS:
filterUnlocked: function(){
return filter('unlocked');
},
filterLocked: function(){
return filter('locked');
},
filterAll: function(){
return filter('all');
},
filter: function(x){
for (var item in itemsListForFilter){
if (x === 'locked'){
item.radioCheck = '!achieved';
}
if (x === 'unlocked') {
item.radioCheck = 'achieved';
}
else {item.radioCheck = true;}
}
Observe that the viewmodel is an object and not a function:
var gamesViewModel = {
self: this,
settings: null,
gameId: null,
authorized: false,
(...)
Right now the functions named filterUnlocked, etc (except filter) displays as "unused properties" in the JS file. What should i do to call them from the HTML? Or is there a better way to accomplish what I'm looking for?
Thank you.
You can get the effect you want with less complexity.
First, the radio group. The checked binding in Knockout kind of replaces (or acts like) the name attribute for a radio group: all the radio buttons should share one. The bound variable will be updated to the value of the selected radio button, which will cause others bound to the same variable to de-select. Now you have one variable to look at instead of three.
Instead of doing filtering by hiding rows, it is more usual to have a computed filter the data, and the table just displays it. The computed can get the data from "outside"; it doesn't have to be an observable or part of the viewmodel (although if it's not an observable, you'll need to tell the computed when to update, if the source changes).
Here's a little working example:
var achievements = [{
name: 'The locked one',
locked: true
}, {
name: 'The unlocked one',
locked: false
}];
var vm = {
appliedFilter: ko.observable('all'),
viewGame: {}
};
vm.viewGame.achievements = ko.computed(function() {
var filter = vm.appliedFilter();
if (filter === 'all') {
return achievements;
}
return ko.utils.arrayFilter(achievements, function(item) {
return item.locked === (filter === 'locked');
});
});
ko.applyBindings(vm);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div class="widget-header-container">
<h3 class="widget-header">Achievements</h3>
<div class="wrapper">
<input type="radio" value="all" data-bind="checked: appliedFilter" />
<label for="all">Show all</label>
<input type="radio" value="unlocked" data-bind="checked: appliedFilter" />
<label for="unlocked">Unlocked</label>
<input type="radio" value="locked" data-bind="checked: appliedFilter" />
<label for="locked">Locked</label>
</div>
</div>
<div>
<div class="rounded-box" style="padding:15px;padding-top: 0;background-color:#fff;overflow:hidden;">
<table id="game-achievements" class="table table-condensed" style="margin-top:10px;">
<tbody data-bind="foreach: viewGame.achievements()">
<tr><td data-bind="text: name"></td></tr>
</tbody>
</table>
</div>
</div>
I have created a table structure like this:
<table><tr data-bind="css: {success: status}">
<td>
<input type="checkbox" onclick="this.disabled = 'disabled';" data-bind="checked: status, disable: status, click: $root.UpdateStatus" />
</td>
<td>
<span style="width: 80%" data-bind="text: goals" />
</td>
<td>
<input type="text" style="width: 80%" data-bind="value: notes , event: { blur: $root.UpdateNote}" />
</td>
</tr></table>
In this table, one checkbox is there in every row. My problem is i want to change the row color when checkbox is checked. I have done css binding in tr, but its working if i reload the page again.
This is jsfiddle link, but its not working.
I cleaned up your fiddle and made it work:
http://jsfiddle.net/vyshniakov/gkyGN/3/
EDIT
For mapping data from server use arrayMap function from ko.utils:
$.ajax({
url: 'ajax/test.html',
success: function(data) { // in data should come tblGoals.
var mappedData = ko.utils.arrayMap(data, function(item) {
// Change property names if necessary
return new Goal(data.GoalId, data.Goals, data.Notes, data.Status);
});
var viewModel = new ViewModel(mappedData);
ko.applyBindings(viewModel);
}
});