entityAspect.setDeleted doesn't fire the subscribed propertyChanged event - breeze

I am running into problem where i subscribe to propertyChanged event, the subscribed event does fire for entity Modification, but never fires for when setting entity to Deleted.
what might i be doing wrong.
The objective of what i am doing is that, whenever user modifies the row, i want to provide
button at row level to cancel the changes. similarly when user deletes a row, i want to provide a button to unDelete a row. The modification part works as expected, but for Delete it is not working.
I was Expecting that item.entityAspect.setDeleted(), would fire the propertyChanged Event
so that i can update the vlaue of observable IsDeleted,which in turn would update the visibility of the button.
ViewModel
/// <reference path="jquery-1.8.3.js" />
/// <reference path="../linq-vsdoc.js" />
/// <reference path="../linq.min.js" />
/// <reference path="../breeze.intellisense.js" />
/// <reference path="../breeze.debug.js" />
$(document).ready(function () {
//extend country type
var Country = function () {
console.log("Country initialized");
var self = this;
self.Country_ID = ko.observable(0); // default FirstName
self.Country_Code = ko.observable(""); // default LastName
self.Country_Name = ko.observable("");
self.entityState = ko.observable("");
self.hasValidationErrors = ko.observable(false);
self.IsDeleted = ko.observable(false);
self.IsModified = ko.observable(false);
self.templateName = ko.observable("AlwayEditable");
var onChange = function () {
var hasError = self.entityAspect.getValidationErrors().length > 0;
if (hasError)
self.hasValidationErrors(true);
else
self.hasValidationErrors(false);
};
//dummy property to wireup event
//should not be used for any other purpose
self.hasError = ko.computed(
{
read: function () {
self.entityAspect // ... and when errors collection changes
.validationErrorsChanged.subscribe(onChange);
},
// required because entityAspect property will not be available till Query
// return some data
deferEvaluation: true
});
//dummy property to wireupEvent and updated self.entityStateChanged property
self.entityStateChanged = ko.computed({
read: function () {
self.entityAspect.propertyChanged.subscribe(function (changeArgs) {
if (changeArgs.entity.entityAspect.entityState.name == "Deleted") {
self.IsDeleted(false);
}
else if (changeArgs.entity.entityAspect.entityState.name == "Modified")
self.IsModified(true);
}); //subscribe
},
deferEvaluation: true,
// self.entityStateChanged(false)
});
self.fullName = ko.computed(
function () {
return self.Country_Code() + " --- " + self.Country_Name();
});
};
manager.metadataStore.registerEntityTypeCtor("Country", Country);
var countryViewModel = function (manager) {
var self = this;
window.viewModel = self;
self.list = ko.observableArray([]);
self.pageSize = ko.observable(2);
self.pageIndex = ko.observable(0);
self.selectedItem = ko.observable();
self.hasChanges = ko.observable(false);
self.totalRows = ko.observable(0);
self.totalServerRows = ko.observable(0);
self.RowsModified = ko.observable(false);
self.RowsAdded = ko.observable(false);
self.RowsDeleted = ko.observable(false);
self.templateToUse = function (dataItem, context) {
var item = dataItem;
if (!_itemTemplate) {
_itemTemplate = ko.computed(function (item) {
//var x = this;
if (this.entityAspect == "undefined")
return this.templateName("AlwayEditable");
if (this.entityAspect.entityState.name == "Deleted") {
this.templateName("readOnlyTmpl");
return this.templateName();
}
else {
this.templateName("AlwayEditable");
return this.templateName();
}
}, item);
}
if (item.entityAspect.entityState.name == "Deleted") {
item.templateName("readOnlyTmpl");
return item.templateName();
}
else {
item.templateName("AlwayEditable");
return item.templateName();
}
// return _itemTemplate();
}
var _itemTemplate;
self.hasError = ko.computed(
{
read: function () {
self.entityAspect // ... and when errors collection changes
.validationErrorsChanged.subscribe(onChange);
},
// required because entityAspect property will not be available till Query
// return some data
deferEvaluation: true
});
self.acceptChanges = function (item) {
// self.selectedItem().entityAspect.acceptChanges();
self.selectedItem(null);
}
manager.hasChanges.subscribe(function (newvalue) {
self.hasChanges(newvalue.hasChanges);
});
self.edit = function (item, element) {
highlightRow(element.currentTarget, item);
self.selectedItem(item);
};
self.discardChanges = function () {
manager.rejectChanges();
manager.clear();
self.pageIndex(0);
self.loadData();
};
self.cancel = function (item, element) {
item.entityAspect.rejectChanges();
self.selectedItem(null);
};
self.add = function () {
var countryType = manager.metadataStore.getEntityType("Country"); // [1]
var newCountry = countryType.createEntity(); // [2]
//if not using this line, the table is not updated to show this newly added item
self.list.push(newCountry);
manager.addEntity(newCountry); // [3]
self.selectedItem(newCountry);
};
self.remove = function (item) {
item.entityAspect.rejectChanges();
item.entityAspect.setDeleted(); //was expecting that propertychaged subscribe event will fire, but it does not
item.templateName("readOnlyTmpl"); //if i don't do this the template is not changed/updated
item.IsDeleted(true); //have to use this
};
self.UndoDelete = function (item) {
item.entityAspect.rejectChanges();
item.templateName("AlwayEditable");
item.IsDeleted(false);
};
self.save = function () {
if (manager.hasChanges()) {
alertTimerId = setTimeout(function () {
//this works as well
$.blockUI({ message: '<img src="Images/360.gif" /> </p><h1>Please Saving Changes</h1>', css: { width: '275px' } });
}, 700);
manager.saveChanges()
.then(saveSucceeded(alertTimerId))
.fail(saveFailed);
} else {
$.pnotify({
title: 'Save Changes',
text: "Nothing to save"
});
// alert("Nothing to save");
};
};
manager.hasChanges.subscribe(function (newvalue) {
self.hasChanges(newvalue.hasChanges);
});
manager.entityChanged.subscribe(function (changeArg) {
self.RowsDeleted(manager.getEntities(null, [breeze.EntityState.Deleted]).length);
self.RowsModified(manager.getEntities(null, [breeze.EntityState.Modified]).length);
self.RowsAdded(manager.getEntities(null, [breeze.EntityState.Added]).length);
});
//we want maxPageIndex to be recalculated as soon as totalRows or pageSize changes
self.maxPageIndex = ko.dependentObservable(function () {
return Math.ceil(self.totalRows() / self.pageSize()) - 1;
//return Math.ceil(self.list().length / self.pageSize()) - 1;
});
self.previousPage = function () {
if (self.pageIndex() > 1) {
self.pageIndex(self.pageIndex() - 1);
//self.loadData();
getData();
}
};
self.nextPage = function () {
if (self.pageIndex() < self.maxPageIndex()) {
self.pageIndex(self.pageIndex() + 1);
// self.loadData();
getData();
}
};
self.allPages = ko.dependentObservable(function () {
var pages = [];
for (i = 0; i <= self.maxPageIndex() ; i++) {
pages.push({ pageNumber: (i + 1) });
}
return pages;
});
self.moveToPage = function (index) {
self.pageIndex(index);
//self.loadData();
getData();
};
};
// self.loadData
var vm = new countryViewModel(manager);
//ko.validation.group(vm);
ko.applyBindings(vm);
// ko.applyBindingsWithValidation(vm);
vm.loadData();
try {
} catch (e) {
displayModalMessage("Page Error :- Reload the Page", e.message);
}
}); //end document.ready
View
<p><a class="btn btn-primary" data-bind="click: $root.add" href="#" title="Add New Country"><i class="icon-plus"></i> Add Country</a></p>
<span> Search Country Code :</span><input id="txtSearch" type="text" /><input id="BtnSearch" type="button" value="Search" data-bind="click: $root.loadData" />
<!--<table class="table table-striped table-bordered " style="width: 700px">-->
<!--<table id="myTable" class="ui-widget" style="width: 800px">-->
<table id="myTable" class="table table-striped table-bordered " style="width: 1200px">
<caption> <div>
<span class="label label-info">Number of Rows Added </span> <span class="badge badge-info" data-bind="text: RowsAdded"></span> ,
<span class="label label-success">Number of Rows Modified</span> <span class="badge badge-success" data-bind="text: RowsModified"></span> ,
<span class="label label-important">Number of Rows Deleted</span> <span class="badge badge-important" data-bind="text: RowsDeleted"></span>
<p/>
</div></caption>
<thead class="ui-widget-header">
<tr>
<th>Code</th>
<th>Name</th>
<th>Full Name</th>
<th />
</tr>
</thead>
<!--<tbody data-bind=" title:ko.computed(function() { debugger; }), template:{name:templateToUse, foreach: list, afterRender: HighlightRows }" class="ui-widget-content"></tbody>-->
<tbody data-bind=" title:ko.computed(function() { debugger; }), template:{name:templateToUse, foreach: list}" ></tbody>
</table>
<div class="pagination">
<ul><li data-bind="css: { disabled: pageIndex() === 0 }">Previous</li></ul>
<ul data-bind="foreach: allPages">
<li data-bind="css: { active: $data.pageNumber === ($root.pageIndex() + 1) }"></li>
</ul>
<ul><li data-bind="css: { disabled: pageIndex() === maxPageIndex() }">Next</li></ul>
</div>
<!--<input id="Button1" type="button" value="Save" data-bind="attr: { disabled: !hasChanges()}, click:saveChanges" />-->
<a class="btn btn-success btn-primary" data-bind="click: $root.save, css: { disabled: !$root.hasChanges()}" href="#" title="Save Changes"> Save Changes</a>
<!-- <input id="Button3" type="button" value="Create New" data-bind="click:AddNewCountry" />
<input id="Button4" type="button" value="Discard and reload data" data-bind="click:discardreload, attr: { disabled: !hasChanges()}" /> -->
<a class="btn btn-danger btn-primary" data-bind="click: $root.discardChanges, css: { disabled: !$root.hasChanges()}" href="#" title="Discard Changes"><i class="icon-refresh"></i> Discard & Reload</a>
<script id="readOnlyTmpl" type="text/html">
<tr >
<td>
<span class="label " data-bind="text: Country_Code "></span>
<div data-bind="if: hasValidationErrors">
<span class="label label-important" data-bind="text: $data.entityAspect.getValidationErrors('Country_Code')[0].errorMessage ">Important</span>
</div>
</td>
<td>
<span class="label " data-bind="text: Country_Name "></span>
<p data-bind="validationMessage: Country_Name"></p>
<span data-bind='visible: ko.computed(function() { debugger; }), text: Country_Name.validationMessage'> </span>
</td>
<td> <span class="label " data-bind="text: fullName "></span>
</td>
<td >
<a class="btn btn-danger" data-bind="click: $root.cancel, visible: $data.IsModified" href="#" title="cancel/undo changes">Undo Changes<i class="icon-trash"></i></a>
<a class="btn btn-danger" data-bind="click: $root.remove, visible: !$data.IsDeleted() " href="#" title="Delete this Row">Delete<i class="icon-remove"></i></a>
<a class="btn btn-danger" data-bind="click: $root.UndoDelete, visible: $data.IsDeleted() " href="#" title="Undo Delete">Un Delete<i class="icon-remove"></i></a>
</td>
</tr>
</script>
<script id="AlwayEditable" type="text/html">
<tr >
<td><input type="text" placeholder="Country Code" data-bind="value: Country_Code , uniqueName: true, css: { error: hasValidationErrors }, valueUpdate: 'afterkeydown'"/>
<!-- <div data-bind="if: $data.entityAspect.getValidationErrors().length>0">
<pre data-bind="text: $data.entityAspect.getValidationErrors('Country_Code')[0].errorMessage "></pre>
</div>-->
<div data-bind="if: hasValidationErrors">
<span class="label label-important" data-bind="text: $data.entityAspect.getValidationErrors('Country_Code')[0].errorMessage ">Important</span>
</div>
</td>
<td><input type="text" placeholder="Country Name" data-bind="value: Country_Name, uniqueName: true, valueUpdate: 'afterkeydown'"/>
<p data-bind="validationMessage: Country_Name"></p>
<span data-bind='visible: ko.computed(function() { debugger; }), text: Country_Name.validationMessage'> </span>
</td>
<td>
<span data-bind=' text: fullName'> </span>
</td>
<td >
<a class="btn btn-danger" data-bind="click: $root.cancel, visible: $data.IsModified" href="#" title="cancel/undo changes">Undo Changes<i class="icon-trash"></i></a>
<a class="btn btn-danger" data-bind="click: $root.remove, visible: !$data.IsDeleted() " href="#" title="Delete this Row">Delete<i class="icon-remove"></i></a>
<a class="btn btn-danger" data-bind="click: $root.UndoDelete, visible: $data.IsDeleted() " href="#" title="Undo Delete">Un Delete<i class="icon-remove"></i></a>
</td>
</tr>
</script>

Analysis
The propertyChanged event is raised when ... a property changes. But that's not what you want to watch. You want to monitor the entityAspect.entityState
When you set a property to a new value (for example person.FirstName("Naunihal")), you get both a propertyChanged event and a change to the entity's EntityState.
When you delete the entity, the entity's EntityState changes ... to "Deleted". But deleting doesn't change a property of the entity. Breeze does not consider the EntityState itself to be a property of the entity. Therefore, there is no propertyChanged notification.
Solution
Update Jan 12, 2013
I think more people will discover this solution if I rephrase the question that you asked so people understand that you want to listen for changes to EntityState.
So I moved my answer to a new SO question: "How can I detect a change to an entity's EntityState?". Hope you don't mind following that link.

Related

jquery goes on the first page instead mine

I have 2 pages (data-role="page") in my HTML page, in the list page if you go to one detail, save, and try to go into another detail again, it goes to list instead of the detail page.
I tried to follow the code using chrome but the javascript is correct
To change page i use: $.mobile.changePage("#PageAddCustomer");
but it seems working for just 1/2 second, then it goes to the list (who is the first page in the code)
Chrome does not return any javascript error,
just here in stackoverflow preview it returns :"error loading page", with no clues on it
what is wrong ?
I am thinking to use 2 different HTML files, probably it could be an idea
var lat = 0;
var lng = 0;
var map;
var url = "https:////www.suale.it"; //"http://localhost:65386/";//
var urlSito = url+"//GestionePersonale";// "http://localhost:65386/";//
var urlCartellaIMG =url + "//IMGAnnunci";// url+"//public//IMGAnnunci";//
//tiene in memoria
var Categorie;
var Tipologie;
var TipoAnnunci;
var Razze;
$(document).ready(function () {
$("div[data-role='panel']").panel().enhanceWithin();
VaiCustomers();
});
//target the entire page, and listen for touch events
$('html, body').on('touchstart touchmove', function(e){
//prevent native touch activity like scrolling
e.preventDefault();
});
function caricaCustomer(j) {
var $select = $('#cbbInsAdsCustomer');
$select.find('option').remove();
$select.append("<option ></option>");
$.each(j, function (key, value) {
$select.append('<option value=' + value.CodTipologia + '>' + value.DescTipologia + '</option>');
});
}
function GoPage(page) {
//$.mobile.changePage(page);
$(':mobile-pagecontainer').pagecontainer('change', page, {
transition: 'flip',
changeHash: false,
reverse: true,
showLoadMsg: true
});
}
function DoNothing() { }
function CaricaDettagliClienteDetail(J) {
// J = JSON.stringify(J);
J = JSON.parse(J);
$("#txtDetailCliCognome").text(J.Cognome);
$("#txtDetailCliNominativo").text(J.Nominativo);
$("#txtDetailCliIndirizzo").text(J.Indirizzo);
$("#txtDetailCliEmail").text(J.Email);
$("#txtDetailCliCitta").text(J.Citta);
$("#txtDetailCliCellulare").text(J.Cellulare);
$("#txtDetailCliTelefono").text(J.Telefono);
}
function CaricaDatiDetailIMG(data) {
var codAnn = $("#hidCodRapportino").val();
var ico;
var img;
$("#divDetailIMG").empty('');
//img = img.replace('\\', '//').replace('ICO', '');
var x, txt = "";//aggiungo IMG
for (x in data) {
var ico = urlCartellaIMG + "//" + codAnn + "//" + data[x];
var img = ico.replace('ICO', '');
txt += "<img onclick='OpenPopIMG(`" + img + "`)' src='" + ico + "'>";
}
$("#divDetailIMG").html(txt);
}
function GoHEad() {
$.mobile.changePage("#page1");
}
function getToNextPage() {
$.mobile.changePage("#page2");
}
function ScaricaDatiUser() {
var codAnag = $("#hidCodAnagrafica").val();
if (codAnag == '')
{
Messaggio('you must login to see your ads');
return;
}
s = document.createElement("script");
//var Category = $('#cbbSearchCategory option:selected').val();
s.src = urlSito + "/scriptWS.aspx?func=CaricaDatiListaUser&OnErr=Messaggio&modo=ListaUser&codAnag=" + codAnag
//alert(s.src);
document.body.appendChild(s);
}
//#region Customer
function VaiCustomers()
{
if (ControlloLoggato()==false){return;};
GoPage('#PageListCustomer');
CaricaCustomers();
}
function CaricaCustomers()
{
s = document.createElement("script");
var RCodDitta = $('#hidCodDitta').val();
s.src = urlSito + "/scriptWS.aspx?func=CustomersList&OnErr=Messaggio&modo=GetCustomers&CodDitta=" + RCodDitta;
//alert(s.src);
document.body.appendChild(s);
}
function CustomersList(data) {
$('#DTCustomersList').DataTable({
destroy: true,
data: data,
"columns": [
{ "data": "Cognome" },
{ "data": "Nominativo" },
{
"data": "CodAnagrafica",
"mData": function showIMGLista(data, type, dataToSet) {
var J = JSON.stringify(data);
var CodAnagrafica = data.CodAnagrafica;
return "<img src='IMG\\Detail.jpg' onclick='OpenDetailCustomer(" + CodAnagrafica + ");'>";
}
},
]
});
}
function OpenDetailCustomer(CodAnagrafica)//dal principale annuncio carico tutto
{
s = document.createElement("script");
s.src = urlSito + "/scriptWS.aspx?func=CaricaCurstomerDetail&OnErr=Messaggio&modo=GetDetailAnag&CodAnagrafica=" + CodAnagrafica;
document.body.appendChild(s);
//$.mobile.changePage("#PageAddCustomer");
$(':mobile-pagecontainer').pagecontainer('change', '#PageAddCustomer', {
transition: 'flip',
changeHash: false,
reverse: true,
showLoadMsg: true
});
}
function CaricaCurstomerDetail(J) {
// J = JSON.stringify(J);
//J = JSON.parse(J);
$("#txtInsCliCognome").val(J.Cognome);
$("#txtInsCliNominativo").val(J.Nominativo);
$("#txtInsCliIndirizzo").val(J.Indirizzo);
$("#txtInsCliEmail").val(J.Email);
$("#txtInsCliCitta").val(J.Citta);
$("#txtInsCliTelefono").val(J.Cellulare);
$("#txtInsCliCellulare").val(J.Telefono);
$("#hidCodCustomer").val(J.CodAnagrafica);
}
function ConfirmDeleteCustomer()
{
bootbox.confirm("Are you sure?", function (result)
{
if (result) {
DeleteCustomer();
}
});
}
function DeleteCustomer()
{
var CodAnagrafica= $("#hidCodCustomer").val();
s = document.createElement("script");
s.src = urlSito + "/scriptWS.aspx?func=CaricaCustomers&OnErr=Messaggio&modo=DeleteCustomer&CodAnagrafica=" + CodAnagrafica;
document.body.appendChild(s);
$.mobile.changePage("#PageListCustomer");
}
function SaveCustomer()
{
if (ControlloSaveCustomer() == true)
{
var Cognome= $("#txtInsCliCognome").val();
var Nominativo= $("#txtInsCliNominativo").val();
var Indirizzo= $("#txtInsCliIndirizzo").val();
var Email= $("#txtInsCliEmail").val();
var Citta= $("#txtInsCliCitta").val();
var Cellulare= $("#txtInsCliTelefono").val();
var Telefono= $("#txtInsCliCellulare").val();
var CodAnagrafica= $("#hidCodCustomer").val();
var RCodDitta = $('#hidCodDitta').val();
s = document.createElement("script");
s.src = urlSito + "/scriptWS.aspx?func=DoNothing&OnErr=Messaggio&modo=SaveAnagrafica&CodAnagrafica=" + CodAnagrafica + "&Cognome=" + Cognome + "&Nominativo=" + Nominativo
+ "&Indirizzo=" + Indirizzo + "&Email=" + Email + "&Citta=" + Citta + "&Cellulare=" + Cellulare + "&Telefono=" + Telefono + "&RCodDitta=" + RCodDitta+ "&Cliente=1";
document.body.appendChild(s);
VaiCustomers();
GoPage('#PageListCustomer');
}
// $.mobile.changePage("#PageListCustomer");
}
function ControlloSaveCustomer() {
$("#txtInsCliCognome").removeClass('inputObbligatorio');
$("#txtInsCliCellulare").removeClass('inputObbligatorio')
$("#txtInsCliNominativo").removeClass('inputObbligatorio')
if ($("#txtInsCliCognome").val() == '') { $("#txtInsCliCognome").addClass('inputObbligatorio'); return false; }
if ($("#txtInsCliCellulare").val() == '') { $("#txtInsCliCellulare").addClass('inputObbligatorio'); return false; }
if ($("#txtInsCliNominativo").val() == '') { $("#txtInsCliNominativo").addClass('inputObbligatorio'); return false; }
return true;
}
//#endregion
function CaricaDatiListaUser(data) {
caricaUserDatatable(data);
}
function ControlloLoggato() {
var codAnag = $("#hidCodAnagrafica").val();
if (codAnag == '') {
Messaggio('you must login to manage ads');
return false;
}
}
.img-container { position: relative; }
.img-container .top {
position: absolute;
top: 0;
left: 0;
z-index: 1;
}
.lblDesc {
font-weight: bold !important;
}
.inputObbligatorio {
border: 2px solid #ff0000 !important;
}
.auto-style1 {
height: 25px;
}
<link rel="stylesheet" href="https://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.css" />
<script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>
<script src="https://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.19/css/jquery.dataTables.min.css" />
<script type="text/javascript" language="javascript" src="https://cdn.datatables.net/1.10.19/js/jquery.dataTables.min.js"></script>
<input type="hidden" id="hidCodAnagrafica" value="1"/>
<input type="hidden" id="hidCodDitta" value="1"/>
<div data-role="panel" id="menuPanel" data-mini="true" data-rel="popup" style=" z-index: 1;" class="ui-btn ui-corner-all ui-shadow ui-btn-inline ui-icon-bars ui-btn-icon-left ui-btn-b" data-transition="pop">
<h4>Menu</h4>
<ul data-role="listview">
<!-- <li>List Admin</li>
<li>Your Reports</li>-->
<!-- <li>New Report</li>-->
<li>Customers</li>
<!-- <li>Jobs</li>
<li>Employees</li>-->
<!-- <li>Register</li>
<li>Login</li>
<li><a href="#" onclick='Website2APK.rateUs();'>Rate us</a></li>-->
<!--<img src="img/head2.jpg" />-->
</ul>
</div>
<!--panel-->
<div data-role="page" id="PageListCustomer" style="background-color: #E5E7E9;">
<h2>Customers</h2>
Menu
<table id="DTCustomersList" class="display" style="width: 100%">
<thead>
<tr>
<th>Surname</th>
<th>Name</th>
<th></th>
</tr>
</thead>
</table>
<input class="btn btn-primary" type="submit" onclick="GoPage('#PageAddCustomer');" value="Add" />
</div> <!--- PageListCustomer --->
<div data-role="page" id="PageAddCustomer" style="background-color: #E5E7E9;">
<h2>Customer</h2>
Menu
<input type="hidden" id="hidCodCustomer" />
<form id="frmAddCustomer">
<table style='border-collapse:collapse;' >
<tr>
<td>
<label class="lblDesc">Surname</label>
</td>
<td>
<input type="text" id="txtInsCliCognome" />
</td>
</tr>
<tr>
<td>
<label class="lblDesc">Name</label></td>
<td>
<input type="text" id="txtInsCliNominativo" />
</td>
</tr>
<tr>
<td>
<label class="lblDesc">Address</label></td>
<td>
<input type="text" id="txtInsCliIndirizzo" />
</td>
</tr>
<tr>
<td>
<label class="lblDesc">Email</label></td>
<td>
<input type="text" id="txtInsCliEmail" />
</td>
</tr>
<tr>
<td>
<label class="lblDesc">City</label></td>
<td>
<input type="text" id="txtInsCliCitta" />
</td>
</tr>
<tr>
<td class="auto-style1">
<label class="lblDesc">Mobile Ph.</label></td>
<td class="auto-style1">
<input type="text" id="txtInsCliTelefono" />
</td>
</tr>
<tr>
<td>
<label class="lblDesc">Phone</label></td>
<td>
<input type="text" id="txtInsCliCellulare" />
</td>
</tr>
</table>
<input class="btn btn-primary" type="submit" onclick="SaveCustomer();" value="Save" />
<input class="btn btn-primary" type="submit" onclick=" VaiCustomers();" value="List" />
</form>
</div> <!--- PageAddCustomer --->

Refresh Jquery datatable after deleting a row

I have a Datatable that display data from a table (role) and i use an ajax call to delete a row :
function OnDeleteClick(el)
{
//var url = ($(this).attr('href'));
//alert(url);
//var employeeId = event.getAttribute(id); //e.target.id
//var url = ($(this).attr('id'));
var id = $(el).attr('id');
var roleid = getURLParameter(id, 'id');
var username = getURLParameter(id, 'name');
var flag = confirm('You are about to delete Role ' + username + ' permanently.Are you sure you want to delete this record?');
if (flag) {
$.ajax({
url: '/Role/DeleteAJAX',
type: 'POST',
data: { roleId: roleid },
dataType: 'json',
//success: function (result) { alert(result); $("#" + Name).parent().parent().remove(); },
success: function (result) {
alert(result);
},
error: function () { alert('Error!'); }
});
}
return false;
}
My question is how to refresh the jquery datatable once the delete has succeed, so the deleted row will be also deleted from the datatable?
Below is the code that i use to build my JQuery datatable :
<div class="row" >
<fieldset class="col-md-10 col-md-offset-1" >
<legend>Liste des rôles </legend>
<div class="table-responsive" style="overflow-x: hidden;">
<table width="98%" class="table table-bordered table-hover" id="dataTables-example">
<thead>
<tr>
<th>Designation Role</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
#item.Name
</td>
<td width="110">
<a class="btn btn-custom btn-xs" href="" onclick="return getbyID('#item.Id',false)" title="consulter">
<i class="fa fa-folder-open-o"></i>
</a>
<a class="btn btn-custom btn-xs" href="" onclick="return getbyID('#item.Id',true)" title="Editer">
<i class="fa fa-edit"></i>
</a>
<a class="btn btn-custom btn-xs" onclick="OnDeleteClick(this);" id="#?id=#item.Id&name=#item.Name" title=" supprimer">
<i class="fa fa-trash-o"></i>
</a>
</td>
</tr>
}
</tbody>
</table>
</div>
<div class="b-right">
<button type="button" class="btn btn-primary" onclick="location.href='#Url.Action("Create", "Role")'">
<i class="fa fa-plus"></i> Ajouter un role
</button>
</div>
</fieldset>
</div>
Thanks for your help.
If you are using DataTables.net and you built your table like this with an AJAX data source:
var myTable = $("#myTable").DataTable({ ajax: "path/to/my/data" });
You can refresh the data in the table like this:
$.ajax({
<your deleting code>
success: function() {
myTable.ajax.reload();
})
});
If you've built your table with Razor using the view model from MVC, you will need to either 1) reload the page in the success callback:
$.ajax({
<your deleting code>
success: function() {
window.location.reload();
})
});
or 2) remove the row from the DOM, knowing it's already deleted from the database, to keep the DB and the UI in sync:
$.ajax({
<your deleting code>
success: function() {
myTable.row($(el).parents("tr")).remove().draw();
})
});

Prevent submitting of a form in asp.net mvc

I'm using MVC Http.BeginForm to create a search form.
Now I want to add create button, which opens Popup, but when I press it HttpPost event in controller fires up. How can I prevent HttpPost event to fire up when popup is pressed?
#using (Html.BeginForm())
{
<table border="0">
<tr>
<td style="padding: 5px;">A:</td>
<td style="padding: 5px;">#Html.TextBox("slicplate", "", new { style="width:120px; height:25px;" })</td>
<td style="padding: 5px;">B:</td>
<td style="padding: 5px;">#Html.TextBox("smodelis", "", new { style="width:120px; height:25px;" })</td>
<td style="padding: 5px;">C:</td>
<td style="padding: 5px;">#Html.TextBox("suzsakovas", "", new { style = "width:120px; height:25px;" })</td>
<td style="padding: 5px;">D:</td>
<td style="padding: 5px;">#Html.TextBox("svairuotojas", "", new { style = "width:120px; height:25px;" })</td>
<td style="padding: 5px;" rowspan="3" valign="top"><input type="submit" value="Search" style="height:59px;" /></td>
<td style="padding: 5px;" rowspan="3">
<button class="btn btn-primary" style="height:50px; width:100px;" ng-click="open('new_tp')">Popup button</button>
</td>
</tr>
</table>
}
Open function:
var ModalDemoCtrl = function ($scope, $modal, $log) {
$scope.items = ['item1', 'item2', 'item3'];
$scope.open = function (size, id) {
var modalInstance
if (size == "new_tp") {
modalInstance = $modal.open({
templateUrl: '/Transport/Create',
controller: ModalInstanceCtrl,
size: size,
resolve: {
items: function () {
return $scope.items;
}
}
});
}
modalInstance.result.then(function (selectedItem) {
$scope.selected = selectedItem;
}, function () {
$log.info('Modal dismissed at: ' + new Date());
});
};
};
And ModalDemoCtrl function:
var ModalInstanceCtrl = function ($scope, $modalInstance, items) {
$scope.items = items;
$scope.selected = {
item: $scope.items[0]
};
$scope.ok = function () {
$('.modal-dialog').close;
$modalInstance.close($scope.selected.item);
};
$scope.delete = function (id) {
$('.modal-dialog').close;
$modalInstance.close($scope.selected.item);
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
};
Change code from
<button class="btn btn-primary" style="height:50px; width:100px;" ng-click="open('new_tp')">Popup button</button>
To:
<button class="btn btn-primary" type="button" style="height:50px; width:100px;" ng-click="open('new_tp')">Popup button</button>

Automatically update viewModel after save to db

What I want to achieve is once the data got saved into database, when it goes back to client, it will automatically update the observable array. But somehow I couldn't make it happen.
This is my Server side code:
[HttpGet]
public JsonResult GetTasks()
{
var tasks = context.ToDoTasks.ToList();
return Json(tasks.Select(c => new TaskViewModel(c)).ToList(), JsonRequestBehavior.AllowGet);
}
[HttpPost]
public JsonResult AddTask(string text, string date)
{
var nTask = new ToDoTask()
{
Text = text,
Date = DateTime.ParseExact(date, "MM/dd/yyyy", System.Globalization.CultureInfo.InvariantCulture),
IsDone = false,
Order = 1,
};
context.ToDoTasks.Add(nTask);
context.SaveChanges();
return Json(new TaskViewModel(nTask), JsonRequestBehavior.AllowGet);
}
This is my cshtml file code:
<form>
<div class="controls controls-row" style="margin-top:40px;">
<input class="span7" type="text" placeholder="Task to do" style="margin-right:4px;" id="oText">
<div id="task-date" class="input-append date">
<input data-format="MM/dd/yyyy" type="text" placeholder="MM/dd/yyyy" name="taskDate" id="oDate" />
<span class="add-on">
<i data-time-icon="icon-time" data-date-icon="icon-calendar">
</i>
</span>
</div>
<button class="btn" type="submit" style="margin-top:-10px;" data-bind="click: save">+</button>
</div>
<div class="controls">
<label class="checkbox">
<input type="checkbox"> Mark all as complete
</label>
</div>
<div id="task-section" style="margin-top:20px;">
<ul data-bind="foreach: Tasks">
<!-- ko if: IsDone -->
<li>
<span><input type="checkbox" style="margin:-5px 5px 0px 0px;" data-bind="checked: IsDone" /></span>
<del><span data-bind="text: Text"></span></del>
<del><span class="task-date" data-bind="text: Date"></span></del>
</li>
<!-- /ko -->
<!-- ko ifnot: IsDone -->
<li>
<span><input type="checkbox" style="margin:-5px 5px 0px 0px;" data-bind="checked: IsDone" /></span>
<span data-bind="text: Text"></span>
<span class="task-date" data-bind="text: Date"></span>
</li>
<!-- /ko -->
</ul>
</div>
<div class="clearfix" style="margin-top:30px;">
<span class="pull-left" style="font-weight:bold;"><span data-bind="text: oItemLeft"></span> item left</span>
<span class="pull-right badge" style="cursor:pointer;" data-bind="click: remove">Clear # completed item</span>
</div>
</form>
And finally my JS:
var ViewModel = function (data) {
var self = this;
self.Tasks = ko.mapping.fromJS(data, {}, self.Tasks);
self.oItemLeft = ko.computed(function () {
var i = 0;
data.forEach(function (entry) {
if (!entry.IsDone) i++;
});
return i;
});
self.save = function () {
$.ajax({
url: "Home/AddTask",
type: "POST",
data: { text: $('#oText').val(), date: $('#oDate').val() },
success: function (response) {
ko.mapping.fromJS(response, ViewModel);
}
});
};
self.remove = function () {
alert('delete');
}
}
$(function () {
$.getJSON("/Home/GetTasks/", null, function (data) {
ko.applyBindings(new ViewModel(data));
});
// for datepicker
$('#task-date').datetimepicker({
language: 'pt-BR',
pickTime: false
});
});
self.save = function () {
$.ajax({
url: "Home/AddTask",
type: "POST",
data: { text: $('#oText').val(), date: $('#oDate').val() },
success: function (response) {
var task = ko.mapping.fromJS(response);
self.Tasks.push(task);
}
});
};
Also for oItemLeft you should be referring to self.Tasks instead of data:
self.oItemLeft = ko.computed(function () {
var i = 0;
self.Tasks().forEach(function (entry) {
if (!entry.IsDone) i++;
});
return i;
});

Can not save data 2nd time knockout mvc

I am new in knockout and asp.net mvc both.
I am trying to Insert update delete data in database with knockout. My knockout model is
function City(data) {
this.CityId = ko.observable(data.CityId);
this.CityName = ko.observable(data.CityName);
}
function CityViewModel() {
var self = this;
self.Citys = ko.observableArray([]);
self.CityId = ko.observable();
self.CityName = ko.observable();
self.selectedCity = ko.observable();
// self.City = ko.observable();
selectCity = function (item) {
self.selectedCity(item);
}
//load
loadCitys = function () {
$.getJSON("/Admin/GetCitys", {}, function (result) {
var mappedCitys = ko.utils.arrayMap(result.Data, function (item) {
return new City(item);
});
self.Citys([]);
self.Citys.push.apply(self.Citys, mappedCitys);
});
}
//edit
EditCity = function (item) {
//what need to do here
// is it possible to fill the hidden fild and the text box ??
}
//save
SaveCity = function (item) {
City = new City(item);
$.ajax({
type: "POST",
url: "/Admin/SaveCity",
data: ko.toJSON({ City: City }),
contentType: "application/json",
success: function (result) {
if (result.Edit) {
City.CityId = result.Success;
City.CityName = item.CityName;
self.Citys.push(City);
toastr.success('City Information Save Successfully', 'Success');
}
else if (result.Edit == false) {
toastr.success('City Information Update Successfully', 'Success');
}
else {
toastr.error('There is an error please try again later', 'Errror');
}
}
});
}
//delete
DeleteCity = function (City) {
$.ajax("/Admin/DeleteCity", {
data: ko.toJSON({ CityId: City.CityId }),
type: "POST", contentType: "application/json",
success: function (result) {
if (result.Success) {
self.Citys.remove(City);
toastr.success('City Remove Successfully', 'Success');
}
else {
alert("Error..");
}
}
});
}
}
(function () {
ko.applyBindings(new CityViewModel, document.getElementById("Citys"));
loadCitys();
});
And my Html codes are
<table class="table table-striped">
<thead>
<tr>
<th>City Id</th>
<th>City Name</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody data-bind="foreach: $root.Citys">
<tr data-bind="click: selectCity">
<td><span data-bind="text:CityId"></span></td>
<td><span data-bind="text:CityName"></span></td>
<td><button data-bind="click: EditCity" class="btn btn-primary">Edit</button></td>
<td><button data-bind="click: DeleteCity" class="btn btn-danger">Delete</button></td>
</tr>
</tbody>
</table>
<fieldset>
<legend>Add new / Edit City</legend>
<label>City name</label>
<input type="hidden" data-bind="value: CityId" />
<input type="text" data-bind="value: CityName" placeholder="Type city name…">
<button type="submit" data-bind="click: SaveCity" class="btn">Submit</button>
</fieldset>
With this codes I can get data form database display them successfully in my view file,
I delete the data from database, and I also can Insert data to database but here is a problem I can save data only 1st time when I change the textbox value (without page refresh) and try to save city information then it say (in Firebug on my javascript code):
TypeError: City is not a constructor
City = new City(item);
My question is what have I done wrong in this codes, and I am trying to fill the textbox and the hidden field when edit button click, how can I do this?
Thanks in advance.
There are a number of faults with your javascript, including:
The methods on your viewmodel, such as SaveCity, DeleteCity, EditCity are all being defined without the 'this/self' prefixes, therefore they are being added to the global namespace.
In your SaveCity method, your are assigning a new instance of the City class to a variable called 'City', therefore destroying the City class. It will work the first time, but any other attempts to create an instance of a City will yield an exception.
Anyway, this should be a working version of your script and HTML without the ajax stuff. You will need to adapt that yourself. I have also created a working JsFiddle here..
function City(data) {
this.CityId = ko.observable(data.CityId);
this.CityName = ko.observable(data.CityName);
}
function CityViewModel() {
var self = this;
self.Citys = ko.observableArray([]);
self.SelectedCity = ko.observable();
self.EditingCity = ko.observable(new City({CityId: null, CityName: ''}));
self.EditCity = function(city){
self.EditingCity(new City(ko.toJSON(city)));
};
//load
self.loadCitys = function () {
self.Citys().push(new City({CityId: '1245', CityName: 'Los Angeles'}));
self.Citys().push(new City({CityId: '45678', CityName: 'San Diego'}));
};
//save
self.SaveCity = function () {
var city = self.EditingCity();
if(city.CityId()){
var cityIndex;
for(var i = 0; i < self.Citys().length; i++) {
if(self.Citys()[i].CityId() === city.CityId()) {
cityIndex = i;
break;
}
}
self.Citys[cityIndex] = city;
}
else{
city.CityId(Math.floor((Math.random()*1000000)+1));
self.Citys.push(city);
}
self.EditingCity(new City({CityId: null, CityName: ''}));
}
//delete
self.DeleteCity = function (city) {
self.Citys.remove(city);
};
}
var viewModel = new CityViewModel();
viewModel.loadCitys();
ko.applyBindings(viewModel, document.getElementById("Citys"));
HTML
<table class="table table-striped">
<thead>
<tr>
<th>City Id</th>
<th>City Name</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody data-bind="foreach: Citys">
<tr data-bind="click: $root.SelectedCity">
<td><span data-bind="text:CityId"></span></td>
<td><span data-bind="text:CityName"></span></td>
<td><button data-bind="click: $root.EditCity" class="btn btn-primary">Edit</button></td>
<td><button data-bind="click: $root.DeleteCity" class="btn btn-danger">Delete</button></td>
</tr>
</tbody>
</table>
<fieldset data-bind='with:EditingCity'>
<legend>Add new / Edit City</legend>
<label>City name</label>
<input type="hidden" data-bind="value: CityId" />
<input type="text" data-bind="value: CityName" placeholder="Type city name" />
<button type="submit" data-bind="click: $root.SaveCity" class="btn">Submit</button>
</fieldset>

Resources