kendo ui form update cancel button - kendo-ui

I am trying to a simple kendo ui form with 'Save' and 'Cancel' buttons. I am using the Kendo.Observable to bind the data to the form.
The functionality I am trying to achieve is, if the 'Save' button is clicked, the form data will be saved. Else, if 'Cancel' is clicked the form will come back to read-only mode with the previous data that was present. To do this, I am first saving the model data in a 'originalvalue' property on click of Update button. If 'Cancel' is clicked, the 'fields' model data is restored to the 'originalvalue'. But the issue is that the , 'originalvalue' does not contain the original value. It gets updated when the user is editing during 'Save'.
The question is - how do I retain the original model data so that it can be refreshed on cancel?
Please find below the code. Appreciate your help, thanks.
<script type="text/javascript">
var viewModel = kendo.observable ({
updated: false,
originalvalue: {},
update: function(e) {
var original = this.get("fields");
this.set("originalvalue", original);
this.set("updated", true);
},
save: function(e) {
e.preventDefault();
if (validator.validate()) {
// make an ajax call to save this data
this.set("updated", false);
}
},
cancel: function(e) {
var original = this.get("originalvalue");
validator.destroy();
this.set("fields", original);
this.set("updated", false);
},
fields: {}
});
viewModel.set("fields", formArray);
kendo.bind($("#outerForm"), viewModel);
// prepare the validator
var validator = $("#outerForm").kendoValidator().data("kendoValidator");

I had to make the exact thing on a form I am currently developing. I am using a DataSource object for the data so I had to use cancelChange().
The thing I did there:
1. I made a Datasource with a schema:
... schema: {
model: {id: "id"}}
...
2. I got the object I was editing with the mapped id:
clientDataSource.cancelChanges(clientDataSource.get(this.get("contactID")));
where the ContactID is created in a setData function where I have passed the ID:
this.set("contactID", contactID);
As I may have notices and understood, you have another problem here where you arent using a DataSource but rather data for fields?
The problem here is that your originalValue is inside the Observable object and it is referenced to the variable original and thus it has observable properties. You should have the variable originalValue defined outside the observable object:
var originalValue;
var viewModel = kendo.observable ({ ...
And you should send the formArray also to that variable so you will have the defaults load before even the observable object is loaded such as:
originalValue = formArray;
viewModel.set("fields", formArray);
So when you need to cancel it you should have:
cancel: function(e) {
var original = originalValue;
validator.destroy();
this.set("fields", original);
this.set("updated", false);
},
I havent tested it but it should provide you some guidance on how to solve that problem.

Related

Kendo Grid: Keep custom popup open after save new record

I've got a custom popup editor template for my Kendo Grid which contains tabs. One of the tabs is to have a second Kendo Grid of records relating to the record being edited.
I'd like to be able to create a new record and immediately start adding the related records, without having to re-open the newly created record. Obviously, I have to first save the record in order for its ID to be generated.
I've managed to prevent the popup editor closing when new records are saved, but I think the popup window is no longer bound to the model at this point.
Is there a way I can rebind the window to the model so I can carry on editing and adding the related records?
Thanks
Edit: Here's the technique for keeping the editor open:
The grid's edit and save events:
edit: function(e){
var editWindow = this.editable.element.data("kendoWindow");
editWindow.bind("close", onWindowEditClose);
},
save: function(e){
if (e.model.isNew()) {
preventCloseOnSave = true;
} else {
preventCloseOnSave = false;
}
}
The onWindowEditClose:
var onWindowEditClose = function(e) {'
if (preventCloseOnSave) {
e.preventDefault();
preventCloseOnSave = false;
}
};
I ended up using a slightly clunky workaround, but other than a minor UI 'flash' it works okay.
The Grid has a rowTemplate, so I've added the record's ID field to the TR tags so I can find them by ID. I then have a function that runs on the complete event when a new record is created which finds the new row and immediately opens it:
var ds = new kendo.data.DataSource({
// ...
transport: {
create: {
url: '/url/to/create',
dataType: 'json',
type: 'post',
complete: recordCreated
}
});
function recordCreated(e) {
"use strict";
var id = e.responseJSON.data[0].id,
grid = $('#grid').data("kendoGrid"),
row = grid.tbody.find("tr[data-id='" + id + "']");
grid.editRow(row);
}
On a conceptual level, you could intercept the POST action that saves the record to the database and get the saved data on return. Note that your POST action must return the saved object (as is expected). You can hook into this event by using the requestEnd method of the Kendo UI Datasource object that supports your grid and bind the saved record to your edit window (as long as you have a reference to it).
var ds = new kendo.data.DataSource({
// ...
requestEnd: function (e) {
kendo.bind(editWindow, e.response); // bind the returned data to your edit window
}
});
The understanding of the kendo ui structure (which can be tedious at times) is important to getting anything done with it. The closing of the popup that allows inserting is done in the dataBinding event. Therefore, that is the place we need to prevent it from:
$("#yourgrid").kendoGrid({
dataSource: yourDataSource,
columns: [
{ field: "YouColumn", title: "YourTitle", ... },
...
]
.
.
.
editable: "popup",
dataBinding: function(e){
//this is the key to keeping the popup open
e.preventDefault();
},
save: function (e) {
//whatever you need to do here
}
.
.
.
});
Hope this helps someone.
//Houdini

Using durandal with jaydata and kendoui

I created a new VS2012 project using the hottowel template, which in return uses durandal, knockout and breeze.
I want to use jaydata instead of breeze and for the ui layer I want to use the excellent asKendoDataSource() functionality to power a kendoui grid.
I followed all the instructions to make kendoui work well with durandal. This is fine.
I have a model in which I get the jaydata entities and run asKendoDataSource() on it. My view is an MVVM declared kendoui grid with its source property set to the viewmodel's property that contains a reference to the asKendoDataSource().
In the knockout world the viewModel property would be an empty entities = ko.observableArray() which would then would be initialized by using entities(values) when the data service returns.
I need to mimic this such that I have a viewModel property that is an empty kendo datasource which then is initialized by the asKendoDataSource() conversion when the data comes back from jaydata. This way the mvvm kendo grid is bound initially to the empty datasource and then receives its items when the asKendoDataSource() is called.
This is all because the model - viewModel binding in durandal is asynchronous and there needs to be a placeholder property in the viewModel from the very beginning, which then, after the viewModel's activate() method promise is resolved, gets updated with the bound data and in return powers the DOM that the viewModel is bound to.
I can't figure out how to mimic the knockoutjs practice of an empty observable array which is bound to the dom and then (the same exact reference) gets initialized and populates the dom. There seems to be no way to create an empty kendo datasource which then is re-initialized by the asKendoDataSource() method. Reassigning the viewModel property to the new data source doesn't work because the kendo grid is bound to the original reference.
This is my airport view:
<section>
<h2 class="page-title" data-bind="text: title"></h2>
<div id="airportGrid" data-kendo-role="grid" data-kendo-sortable="true" data-kendo-pageable="true" data-kendo-page-size="25" data-kendo-editable="true" data-kendo-columns='["Id", "Abbrev", "Name"]' data-kendo-bind="source: airports"></div>
</section>
This is my datacontext:
define([
'durandal/system',
'services/logger'],
function (system, logger) {
var getAirports = function (airportsObservable) {
$data.Entity.extend("Airport", {
Id: { type: "int", key: true, computed: true },
Abbrev: { type: String, required: true, maxLength: 200 },
Name: { type: String, required: true, maxLength: 512 }
});
$data.EntityContext.extend("JumpSeatDatabase", {
Airports: { type: $data.EntitySet, elementType: Airport }
});
var airportDB = new AirportDatabase('http://localhost:2663/odata');
var deferred = Q.defer();
airportDB.onReady(function () {
deferred.resolve(airportDB.Airports);
});
return deferred.promise;
}
var datacontext = {
getAirports: getAirports
};
return datacontext;
function log(msg, data, showToast) {
logger.log(msg, data, system.getModuleId(datacontext), showToast);
}
function logError(msg, error) {
logger.logError(msg, error, system.getModuleId(datacontext), true);
}
//#endregion
});
This is my airport viewmodel:
define(['services/datacontext', 'durandal/plugins/router'],
function (datacontext, router) {
var airports = null;// = kendo.data.ObservableArray([]);
var activate = function () {
var airportRes = datacontext.getAirports();
return airportRes.then(function (airp) {
vm.airports = airp.asKendoDataSource();
});
};
var deactivate = function() {
//airports([]);
};
var viewAttached = function (view) {
//using this type of reach in to the viewModel is considered bad practice in durandal
//$('#airportGrid').kendoGrid({
// dataSource: airports.asKendoDataSource({ pageSize: 10 }),
// filterable: true,
// sortable: true,
// pageable: true,
// height: 500,
// columns: ['Id', 'Abbrev', 'Name']
//});
//kendo.init($("#container"));
kendo.bind(view, vm);//this should eventually go away the recommended ViewModelBinder code in main.js is not firing for me
};
var vm = {
activate: activate,
deactivate: deactivate,
airports: airports,
title: 'Airports',
viewAttached: viewAttached
};
return vm;
});
One last issue that I am seeing:
It seems to me that an MVVM declared kendoui grid, bound to the view model through data-kendo-bind={source: airports)" where airports is a property of the viewmodel that was created through entities.asKendoDataSource() does not work. Somehow the grid does not show the data. Is there something extra that needs to be done?
Thanks
My best guess is that this is a timing issue, where the Grid is binding to vm.airports while it is still null, then vm.airports = airp.asKendoDataSource(); is called after it is already bound? Perhaps try something like:
return airportRes.then(function (airp) {
vm.airports.data(airp.asKendoDataSource().data());
});

Kendo UI grid - batch update not executed

I'm implementing a simple (at least ,that was the goal) Kendo UI grid that displays two columns: one holding a checkbox, bound to a boolean, and one holding a display name for the item. The checkbox column has a simple template, and the change() event of the checkbox is handled so that the model in the datasource gets updated. I have verified this, and it works.
The data source has been configured for batch, and defines a transport for read and update. Both call a function that perform the ajax call. As I said before, the read function is handled as expected. However, the update function defined on the transport is not. The sync() on the datasource is triggered with a simple button whose click event is hooked to a function that calls datasource.sync() (or grid.saveChanges()).
transport: {
read: function(options) {
return loadStuff(options);
},
update: function (options) {
return updateStuff(options);
}
}
When debugging in the Kendo UI code, it looks like the models attribute on the ModelSet is always empty, and therefore the sync() decides that there's nothing to sync. Anyone got a clue what is happening here?
UPDATE:
Looks like something may be wrong when handling the checkbox check / uncheck. Apparently I should use something like
$('#divGrid').on('click', '.chkbx', function() {
var checked = $(this).is(':checked');
var grid = $('#divGrid').data().kendoGrid;
var dataItem = grid.dataItem($(this).closest('tr'));
dataItem.set("Selected", checked);
});
Unfortunately, it looks like the set() method is not defined on the data item. When debugging, it only contains the data, and no Model object having the set() method.
UPDATE 2:
Tried wrapping the data returned from the ajax call in a model defined with Model.define(). That seems to solve the issue of the model not being dirty, as the _modified property on the model returns true. However, the models array in the ModelSet remains empty. Is this a bug in Kendo UI, or am I going the wrong way?
You don't actually need to bind to click event on the checkboxes.
I´ve posted an example on using it in JSFiddle where you can see it running. This example displays in a grid two columns: first text (tick) and second boolean rendered as a checkbox (selected); the update is batch (so, it's pretty close to what you have).
Questions to keep in mind are:
For displaying the checkbox while not in edit mode, you should define a template, something like this. You might realize that the checkbox is in disabled state by default since you want to edit it as other fields (selecting the cell first). This also guarantees that the model is correctly updated:
{
field : "selected",
title : "Selected",
template: "<input type='checkbox' name='selected' #= selected ? 'checked' : '' # disabled/>"
}
Define in the model that this field is boolean:
schema : {
id : "id",
model: {
fields: {
symbol : { type: "string" },
selected: { type: "boolean" }
}
}
},
Define the transport.update function, something like:
transport: {
read : function (operation) {
// Your function for reading
},
update: function (operation) {
// Display modified data in an alert
alert("update" + JSON.stringify(operation.data.models, null, 4));
// Invoke updating function
// that should ends with an operation.success(the_new_data)
// In this example just say ok
operation.success(operation.data.models)
}
}
EDIT: If you want to be able to modify the checkbox state without having to enter in edit mode first, you should:
Remove the disabled from the template:
{
field : "selected",
title : "Selected",
template : "<input type='checkbox' name='selected' #= selected ? 'checked' : '' #/>"
},
Then bind the click event on checkboxes to the following handler function:
$("#stocks_tbl").on("click", "input:checkbox", function(ev) {
var dataItem = grid.dataItem($(this).closest('tr'));
dataItem.set("selected", this.checked);
});
Where #stocks_tbl is the id of the div that contains the grid. You might see it running here.
NOTE: It's important the on with the three parameters for making it live

how to load a partial view on button click in mvc3

I have a DDL, on its change I load a partial view _GetX. Right now I am using it like this
#Html.DropDownListFor(model => model.XId, Model.XList, "Select", new { GetUrl = Url.Action("GetX", "Route") })
I need to load this partial view on clicking a button. How do I do this?
Assuming your GetX controller action already returns the partial view:
$(function() {
$('#someButton').click(function() {
// get the DDL. It would be better to add an id to it
var ddl = $('#XId');
// get the url from the ddl
var url = ddl.attr('GetUrl');
// get the selected value of the ddl to send it to the action as well
var selectedValue = ddl.val();
// send an AJAX request to the controller action passing the currently
// selected value of the DDL and store the results into
// some content placeholder
$('#somePlaceholder').load(url, { value: selectedValue });
return false;
});
});

backbone.js: understanding browser event handling and view removing

I'm fiddling with a view and related model that look like that:
App.Views.Addresses = App.Views.Addresses || {};
App.Views.Addresses.Address = Backbone.View.extend({
events: {
"click button#foo" : "clear"
},
initialize: function(model){
this.address = model.model;
this.address.view = this;
_.extend(this, Backbone.Events);
this.render();
},
render: function(){
... rendering stuff
},
clear: function(){
this.address.clear();
}
});
and
var Address = Backbone.Model.extend({
url: function() {
... url stuff
},
clear: function(){
this.destroy();
this.view.remove();
}
});
I'm facing two problems here. The first one:
I have a button with id="foo" in my source and would like the view to catch the 'click' event of this very button and fire the 'clear' event. Problem: This does not work.
Anyway calling 'clear' on my model by hand cleanly removes the data on the server but does not remove the view itself. Thats the second problem. Hopefully someone more experienced can enlighten me.
Thx in advance
Felix
First problem:
Your button must be inside the element rendered by the view.
backbone scope events to inner elements only
You must render your view within this.el element
backbone use that element for delegation
Second problem:
Use events to destroy your view
You should not store the view in the model. This is kind of a "no no" in MVC. Your model already emits a "remove" event when deleted. Your view should listen to it and behave accordingly.
You must remove your view element from the DOM yourself
This is not handled by backbone.
Other general comments:
Views already are extending Backbone.Events
Use this.model instead of this.address

Resources