Bind events within Kendo grid - mvvm

I need to bind some events on the grid row and details view. I'm using an observable view model with some events registered and trying to bind them to the DOM using row template and details templates. So far no progress.
$("#grid").kendoGrid({
sortable:true,
rowTemplate:'<tr class="k-master-row">
<td class="k-hierarchy-cell"><a class="k-icon k-plus" href=""></a></td>
<td><a data-bind:"click:highlight">click in row ${id}</a></td><td>${bool}</td>
</tr>',
detailTemplate:'<a data-bind:"click:highlight">click In details ${id}</a>',
columns: [{field:'id',sortable:false}, {field:'bool'}],
dataBound: function(e) {
var grid=$("#grid").data('kendoGrid');
grid.expandRow("tr.k-master-row");
}
});
var model=( {
highlight:function(){
console.log(this.id);
},
items:[{id: 1123, bool: true}, {id: 223, bool: false}]
});
kendo.bind($("#grid"),kendo.observable(model));
Here is the jsFiddle http://jsfiddle.net/amGmr/9/ . Is there any possibility to bind events withing the grid using MVVM ?

You should specify the events you wish to bind to via the data-bind attribute and the events binding:
<div data-role="grid"
data-bind="source: dataSource, events:{dataBound: dataBound, detailInit: detailInit}"
></div>
<script>
var viewModel = kendo.observable({
dataBound: function(e) {
var grid = e.sender; // `this` is the viewModel instance
},
detailInit: function(e) {
var grid = e.sender; // `this` is the viewModel instance
},
dataSource: [
{ name: "John Doe" },
{ name: "Jane Doe" }
]
});
</script>

Related

Angular 4 Create Dynamic formArray inside array using reactive forms

Here, we are creating dynamically form array's inside array.
Below is the sample structure of expected result given below.
{
"optionsRadios": null,
"Package_Title": null,
"HotelData": [
{
"Htitle": "",
"HDescription": "",
"hotelStar": "",
"RoomData": [
{
"Hotel_Room_Type": ""
},
{
"Hotel_Room_Type": ""
}
]
}
]
}
I want to create HotelData Dynamically, within that HotelData array i want to create RoomData array fields also dynamically.
I created HotelData fields by the following codes:
export class AddPackageComponent implements OnInit {
ngOnInit() {
this.invoiceForm = this._formBuild.group({
Package_Title: [],
HotelData: this._formBuild.array([this.addRows()])
})
}
addRows() {
return this._formBuild.group({
Htitle: [''],
HDescription: [''],
hotelStar: ['']
});
}
addHotel() {
const control: FormArray = this.invoiceForm.get(`HotelData`) as FormArray;
control.push(this.addRows());
}
}
You are on the right track, we just need to add some more code...
addRows need the form array RoomData, and here we also initially push an empty form group of room. If you don't want that, modify it.
addRows() {
let group = this._formBuild.group({
...
RoomData: this._formBuild.array([])
});
// push formgroup to array initially
this.addRoom(group.controls.RoomData)
return group;
}
addRoom looks like this:
addRoom(hotel:any) {
let group = this._formBuild.group({
Hotel_Room_Type: ['']
})
hotel.push(group)
}
addRoom is also the method we are calling from template when we want to add a new room to a hotel. Remember to pass the current hotel as parameter from template.
As for adding a new hotel, your addHotel stays the way you have it now.
Then over to your template, the relevant part should look something like this:
<div formArrayName="HotelData">
<div *ngFor="let hotel of invoiceForm.get('HotelData').controls; let i = index" [formGroupName]="i" >
<!-- form controls here -->
<button (click)="addRoom(hotel.get('RoomData'))">Add Room</button>
<div formArrayName="RoomData">
<div *ngFor="let room of hotel.get('RoomData').controls; let j = index" [formGroupName]="j">
<!-- form controls here -->
</div>
</div>
</div>
</div>
Finally, here's a Demo: http://plnkr.co/edit/7tcLcnzALew3oKGenjwK?p=preview

What is sane way in vuejs + vuex form handling?

I have a large forms to submit in single page.
<container>
<formA>
<formB>
<formC>
<submitButton>
<container>
it looks apparently like this. and I have a store which save every form data. then when user click submit button, I gather all form data using vuex store.
The problem is I need to update the form data in store everytime.
so I'll be like this in vue component
watch: {
userInput (val) {
this.updateState(val)
}
update state when input changes by watching form data(binded with v-model).
or like this which is documented in vuex doc.
userInput: {
get () {
return this.$store.state.userInput
},
set (val) {
this.updateState(val)
}
}
well.. I don't think these are good idea. Is there any better way to form handling with vuex?
I made a little tool which makes form handling wit Vuex a lot easier: vuex-map-fields
Example
Store
import Vue from 'vue';
import Vuex from 'vuex';
// Import the `getField` getter and the `updateField`
// mutation function from the `vuex-map-fields` module.
import { getField, updateField } from 'vuex-map-fields';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
fieldA: '',
fieldB: '',
},
getters: {
// Add the `getField` getter to the
// `getters` of your Vuex store instance.
getField,
},
mutations: {
// Add the `updateField` mutation to the
// `mutations` of your Vuex store instance.
updateField,
},
});
Component
<template>
<div id="app">
<input v-model="fieldA">
<input v-model="fieldB">
</div>
</template>
<script>
import { mapFields } from 'vuex-map-fields';
export default {
computed: {
// The `mapFields` function takes an array of
// field names and generates corresponding
// computed properties with getter and setter
// functions for accessing the Vuex store.
...mapFields([
'fieldA',
'fieldB',
]),
},
};
</script>
You can read more about vuex-map-fields on my blog: How to Handle Multi-row Forms with Vue, Vuex and vuex-map-fields
I would use deep watchers for this and have all fields in a object, you could use multiple approaches for saving the data, iterating over Object.keys to store each field with it's variable name in the form object, or storing the entire form, whatever you might need.
You could also use v-model.lazy="form.myfield" to indicate that you only want the binding to update once the user has left the field.
Form component
<template>
<div>
<!-- You can optionally use v-model.lazy="form.field1" to only update once user has exited the field or pressed enter -->
<input v-model="form.field1" />
<input v-model.lazy="form.field2" />
</div>
</template>
<script>
export default {
props: ['value'],
data: function () {
return {
internalForm: {
field1: null,
field2: null
}
}
},
watch: {
internalForm: {
handler: function (newValue) {
// Emit new form object to parent component so we can use v-model there
this.$emit('input', this.form)
// Or save form data
this.handleFormSave(this.form)
},
// Tell vue to do a deep watch of entire form object to watch child items in the object
deep: true
}
}
}
</script>
Parent component
<template>
<form-component v-model="forms.form1" />
<submit-button #click="saveAllFormData" />
</template>
<script>
export default {
data: function () {
return {
forms: {
form1: null // This will be updated when 'input' is emitted
}
}
},
watch: {
forms: {
handler: function (newValue) {
if (allFormsValid && readyToSave)
saveAllFormData(newValue);
},
deep: true
}
}
}
</script>
I had headache regarding this probem to.
Vuex doc describes that we need to update store for every field.
It's a loot of typing whatfor?
We make one solution that works.
It based on cloning store object to local one.
//We are passing (vuexstore) 'item' object from parent component:
//<common-item v-bind:item="item" ....
props: ['item'],
// create localItem - this is reactive object for vuex form
data: () => {
return {
localItem: null
}
},
// make clone on created event
created: function() {
this.localItem = this._clone(this.item)
},
// watch vuexstore 'item' for changes
watch: {
item: function(val) {
this.localItem = this._clone(this.item)
}
},
// map mutations and update store on event
methods: {
...mapMutations([
'editItem'
]),
updateItemHandler: function() {
this.editItem({ item: this._clone(this.localItem) })
},
_clone: function(o){
return JSON.parse(JSON.stringify(o))
}
},
Inside form use:
<input v-model="localItem.text" #keyup="updateItemHandler" type="text" class="form-control"></input>
I think this is only lack of vuex. There should be much shorter and built in solution.

Kendo UI DragAndDrop TreeView item to a ListView

I have a requirement to enable drag and drop from a kendo-ui tree view to a templated list view.
I've tried the following:
1.Enabling dragAndDrop on the treeview and configuring the listview as a kendoDropTarget
2.Disabling dragAndDrop on the treeview and instead configuring that control as kendoDraggable to the listview configured as a kendoDropTarget
<div>
<div id="treeview">
</div></div>
<div id="favorites-window" style="height:185px;width:1170px">
<div class="report-reader" style="height:185px;width:1170px;overflow:auto">
<div id="listView"></div>
</div>
</div>
$("#favorites-window").kendoWindow({
width: "1180",
height: "185",
resizable: false,
draggable: false,
actions: ["Custom"],
title: "Favorites"
});
$("#listView").kendoListView({
selectable: "single",
navigatable: false
}).kendoDropTarget({
drop: function (e) {
console.log(e);
var item = getObjects(nucleusTreeJsonData, 'text', e.draggable.hint.text());
$("#listView").data("kendoListView").dataSource.add(item);
}
});
var inlineDefault = new kendo.data.HierarchicalDataSource({
data: [
{ text: "Furniture", items: [
{ text: "Tables & Chairs" },
{ text: "Sofas" },
{ text: "Occasional Furniture" }
] },
{ text: "Decor", items: [
{ text: "Bed Linen" },
{ text: "Curtains & Blinds" },
{ text: "Carpets" }
] }
]
});
$("#treeview").kendoTreeView({
dragAndDrop: true,
dataSource: inlineDefault,
dataTextField: "text"
});
//.kendoDraggable({
// container: $("#tree-pane"),
// hint: function () {
// return $("#treeview").clone();
// },
// dragstart: draggableOnDragStart
//});
$("#treeview").data("kendoTreeView").bind("dragstart", function (e) {
if ($(e.sourceNode).parentsUntil(".k-treeview", ".k-item").length == 0) {
e.preventDefault();
}
});
/*$("#treeview").data("kendoTreeView").bind("drop", function (e) {
e.preventDefault();
var copy = this.dataItem(e.sourceNode).toJSON();
if (e.dropPosition == "over") {
//var item = getObjects(nucleusTreeJsonData, 'text', e.sourceNode.textContent);
$("#listView").data("kendoListView").add(copy);
}
});*/
$('ul.k-group.k-treeview-lines div').children().css('font-weight', 'bold').find('div').css('font-weight', 'normal');
I'm not having much luck with it. Please take a look at my fiddle. Any suggestions would be greatly appreciated
http://jsfiddle.net/OhenewaDotNet/JQBZN/16/
I know this is an old question but I had it, too, so I went ahead and figured it out using this fiddle.
http://jsfiddle.net/JQBZN/74/
This is really really basic and is probably architected awfully but I think it at least demonstrates the key point(s):
$("#treeview").kendoTreeView({
dragAndDrop: true,
dataSource: inlineDefault,
dataTextField: "text",
drag: function (e) {
/* Manually set the status class. */
if (!$("#treeview").data('kendoTreeView').dataItem(e.sourceNode).hasChildren && $.contains($('#favorites-window')[0], e.dropTarget)) {
e.setStatusClass('k-add');
} else {
e.setStatusClass('k-denied');
}
},
drop: function (e) {
if (e.valid) {
/* Do your adding here or do it in a drop function elsewhere if you want the receiver to dictate. */
}
e.preventDefault();
}
});
If the KendoUI tool set isn't doing what you want it to do, you may find it easier to do what you want to do with jQuery UI. They're both implementing the same jQuery core library.
If you go with jQuery UI, it's simply a matter of binding 'draggable' to the element you want to drag, and 'droppable' to your targets. From there, you can wire up handlers to do pretty much anything you want.
I've set up a simple jsFiddle that demonstrates how this would work:
http://jsfiddle.net/e2fZk/23/
The jQuery code is really simple:
$(".draggable").draggable();
$(".container").droppable({
drop: function (event, ui) {
var $target = $(this);
var $source = ui.draggable;
var newUrl = $source.find("input").val();
alert("dropped on " + $target.attr("id") + ", setting URL to " + newUrl);
$target.find("#imageDiv").html("<img id='myImage' />")
.find("#myImage").attr("src", newUrl);
}
});
The API documentation is here:
Draggable
Droppable

KendoUI: binding view model to datasource change

I have an observable object which defines a pointer to a datasource binded to a grid, and a custom field which should return an aggregate value I declared in the datasource.
I would like to bind the second field ("totAmount") to a custom HTML element.
I do not understand how to update its value: when I call the "read()" method of the datasource shouldn't the binded value also be updated on the interface? Does it work only with "primitive" model fields?
=== JAVASCRIPT ===
var vm = kendo.observable({
gridDatasource: new kendo.data.DataSource({ ... }),
totAmount: function() {
var ds = this.get("gridDatasource");
var value = (ds.aggregates()) ? ds.aggregates().totAmount : 0;
return value;
}
});
=== HTML ===
<span data-bind="text: totAmount"></span>
My previous answer was not totally correct: it binds the model update on grid change (on each row selection). It is better to bind it to the "change" event of the datasource:
=== JAVASCRIPT ===
var vm = kendo.observable({
gridDatasource: new kendo.data.DataSource({ ... }),
totAmount: 0
});
vm.gridDatasource.bind("change", function(e) {
vm.set("totAmount", this.aggregates().totAmount);
});
=== HTML ===
<span data-bind="text: totAmount"></span>
So far I have found a solution similar to my previous post (bind HTML elements to grid selected row/dataItem), setting the value in the "change" event of the grid binded to the datasource:
=== JAVASCRIPT ===
var vm = kendo.observable({
gridDatasource: new kendo.data.DataSource({ ... }),
totAmount: 0
});
$("#grid").kendoGrid({
change: function(e) {
vm.set("totAmount", this.dataSource.aggregates().totAmount);
}
});
=== HTML ===
<span data-bind="text: totAmount"></span>

kendo bind HTML elements to grid selected row/dataItem

I have the following situation (using KendoUI):
I have a grid binded to a datasource.
When I select a row in the grid I invoke its "change" event to get the selected dataItem e show its values through other HTML elements.
Something like the following:
$("grid-element").kendoGrid({
change: setElements
});
function setElements() {
var grid = $("#grid-element").data("kendoGrid");
var selectedItem = grid.dataItem(grid.select());
$("#span-field1").text(selectedItem.field1);
$("#span-field2").text(selectedItem.field2);
$("#span-field3").text(selectedItem.field3);
}
My question is: is it possibile to achieve the same through MVVM or a better KendoUI model binding solution?
So far I have found the following solution:
=== JAVASCRIPT ===
var vm = kendo.observable({
gridSelectedItem: null,
_field1: function() {
return this.get("gridSelectedItem.field1");
},
_field2: function() {
return this.get("gridSelectedItem.field2");
}
});
$("#grid-element").kendoGrid({
change: function(e) {
var selectedItem = this.dataItem(this.select());
vm.set("gridSelectedItem", selectedItem);
}
});
=== HTML ===
<span data-bind="text: _field1"></span>
<span data-bind="text: _field2"></span>
Is there a better way?
Indeed there you are on the right track,
Here is what I can suggest you to try:
=== JAVASCRIPT ===
var vm = kendo.observable({
gridSelectedItem: null
});
$("#grid-element").kendoGrid({
change: function(e) {
var selectedItem = this.dataItem(this.select());
vm.set("gridSelectedItem", selectedItem);
}
});
=== HTML ===
<span data-bind="text: gridSelectedItem.field1"></span>
<span data-bind="text: gridSelectedItem.field2"></span>
It should be slightly more compact.