I have a View in which i have criteria a Supplier TextBox a LastMonth dropdown and a Months Textbox.
#using JouleBrokerDB.ViewModels;
#model AssignPayReportToDepositViewModel
#{
ViewBag.Title = "View";
}
<link href="#Url.Content("~/Content/kendo/kendo.common-bootstrap.min.css")" rel="stylesheet" />
<link href="#Url.Content("~/Content/kendo/kendo.bootstrap.min.css")" rel="stylesheet" />
<link href="#Url.Content("~/Content/kendo/kendo.dataviz.min.css")" rel="stylesheet" />
<link href="#Url.Content("~/Content/kendo/kendo.dataviz.bootstrap.min.css")" rel="stylesheet" />
<style>
.treediv {
display: inline-block;
vertical-align: top;
width: 440px;
/*height:400px;*/
min-height: 400px;
text-align: left;
margin: 0 2em;
border-radius: 25px;
border: 2px solid #8AC007;
padding: 15px;
overflow: auto;
}
</style>
<div class="row">
<div class="col-md-9 col-md-offset-1">
#using (Html.BeginForm("Show", "AssignPayReportToDeposit", FormMethod.Post, new { id = "AssignToPayReportForm", #class = "form-horizontal" }))
{
<fieldset>
<!-- Form Name -->
<legend>Assign Pay Report to Deposit</legend>
<div class="form-group">
<!-- Supplier -->
<div class="col-sm-4">
#Html.Label("", "Supplier:", new { #class = "control-label", #for = "textinput" })
<div id="suppliers">
#Html.DropDownListFor(x => x.SuppliersList, new SelectList(Model.SuppliersList, "SupplierID", "Name"), new { id = "ddSupplier", #class = "form-control" })
</div>
</div>
<!-- Last Month -->
<div class="col-sm-4">
#Html.Label("", "Last Month:", new { #class = "control-label", #for = "textinput" })
#Html.DropDownListFor(x => x.LastMonthsList, new SelectList(Model.LastMonthsList), new { #id = "ddLastMonth", #class = "form-control" })
</div>
<!-- Months-->
<div class="col-sm-4">
#Html.Label("", "Months:", new { #class = "control-label", #for = "textinput" })
#Html.TextBox("txtMonths", null, new { type = "number", step = 1, min = 1, max = 12, #class = "form-control", required = "required" })
</div>
</div>
</fieldset>
<div class="treediv">
#Html.Label("", "UnAssigned PayReport:", new { #class = "control-label", #for = "textinput" })
<div id="TreeView_UPR" style="padding:5px"></div>
</div>
<div class="treediv">
#Html.Label("", "Deposits:", new { #class = "control-label", #for = "textinput" })
<h4></h4>
<div id="TreeView_AD" style="padding:5px"></div>
</div>
}
</div>
</div>
<script src="#Url.Content("~/Scripts/kendo/kendo.all.min.js")"></script>
<script src="#Url.Content("~/Scripts/Views/AssignPayReportToDeposit/Show.js")"></script>
Here on this text box i have attached changed event though jQuery. The requirement is that whenever the criteria changes the treeview div will be filled with data will be refreshed.
AssignPayReportsToDeposit.AttachEvents = function () {
$("#ddSupplier").change(AssignPayReportsToDeposit.OnSupplierChange);
$("#ddLastMonth").change(AssignPayReportsToDeposit.OnLastMonthChange);
$("#txtMonths").change(AssignPayReportsToDeposit.OnMonthsChange);
}
these changed event handler will handle the refreshing the treeview. The whole thing is handled through ajax calls.
Now i know that using Ajax.ActionLink and UpdateTargetId parameter with Replace option i can return the treeview in partial view so the manual handling can be removed. but that will require me put the anchor button which user have to click. Requirement is that the refresh of treeview should be done on any criteria change.
Is there any way i am able to achieve this using Ajax.ActionLink (or any another razor syntax that will take load off from the manual handling ) ? On change event of the controls i would like to call a controller using ajax.actionlink which will return a partialview and update the div.
Edit: I am handling this through jQuery right now. so i will post the complete code for more understanding.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using JouleBrokerDB;
using JouleBrokerDB.ViewModels;
using JouleBroker.Filters;
namespace JouleBroker.Controllers
{
[RoutePrefix("AssignPayReportToDeposit")]
[Route("{action=Show}")]
public class AssignPayReportToDepositController : Controller
{
// GET: AssignPayReportToDeposit
//[Route("Show",Name = "APTDShow")]
//[ValidateLogin]
public ActionResult Show()
{
List<SupplierViewModel> suppliers = DBCommon.GetAllSuppliers(false);
SuppliersList_LastMonthsList_ViewModel model = new SuppliersList_LastMonthsList_ViewModel()
{
SuppliersList = suppliers,
LastMonthsList = new List<string>()
};
return View(model);
}
[HttpPost]
[Route("GetUnAssignedPayReports")]
public JsonResult GetUnAssignedPayReports(int SupplierID,
string MonthPaid,
int Months)
{
var payreports = AssignPayReportsToDepositData.GetUnAssignedPayReports(SupplierID,
MonthPaid,
Months);
return Json(payreports);
}
[HttpPost]
[Route("GetAssignedPayReports")]
public JsonResult GetAssignedPayReports(int SupplierID,
string MonthPaid,
int Months)
{
var payreports = AssignPayReportsToDepositData.GetAssignedPayReports(SupplierID,
MonthPaid,
Months);
return Json(payreports);
}
[HttpPost]
[Route("AssignDepositIdToPayReport")]
public bool AssignDepositIdToPayReport(int PayReportID, int DepositID)
{
return AssignPayReportsToDepositData.AssignDepositIdToPayReport(PayReportID, DepositID);
}
}
}
JavaScript File (the code is a bit lengthy so you don't need to look at all of them you can see the methods which are calling the action methods. GetUnAssignedPayReports and GetAssignedPayReports which returns the data which is used to fill the tree view.) I just want this portion to moved to partial view and passing model to partial view generate treeview there and replace the div each time on change event with rendering partial view again. Hope i am clear enough. so change the above methods to return partial instead of json result that what i am trying to achive
function AssignPayReportsToDeposit() { }
AssignPayReportsToDeposit.SelectedSupplierID = 0;
AssignPayReportsToDeposit.SelectedLastMonth = null;
AssignPayReportsToDeposit.SelectedMonths = 0;
AssignPayReportsToDeposit.LastMonthsList = null;
AssignPayReportsToDeposit.UnAssignedPayReportsList = null;
AssignPayReportsToDeposit.AssignedPayReportsList = null;
AssignPayReportsToDeposit.LastTextChangedNode = null;
//--------- Document Ready Function -------- //
$(document).ready(function () {
//AttachEvents
AssignPayReportsToDeposit.AttachEvents();
});
AssignPayReportsToDeposit.AttachEvents = function () {
$("#ddSupplier").change(AssignPayReportsToDeposit.OnSupplierChange);
$("#ddLastMonth").change(AssignPayReportsToDeposit.OnLastMonthChange);
$("#txtMonths").change(AssignPayReportsToDeposit.OnMonthsChange);
}
//Handles Supplier ChangeEvents
AssignPayReportsToDeposit.OnSupplierChange = function () {
//Get Changed Supplier ID
AssignPayReportsToDeposit.SelectedSupplierID = $('#ddSupplier').val();
//Get Last Month List
AssignPayReportsToDeposit.LastMonthsList = CommonAction.GetLastPayReportMonthsBySupplierID(AssignPayReportsToDeposit.SelectedSupplierID);
//Fill Last Month List
AssignPayReportsToDeposit.FillLastMonths();
//Refresh TreeView_UPR
AssignPayReportsToDeposit.RefreshTreeViewUPR();
//Refresh TreeView_AD
AssignPayReportsToDeposit.RefreshTreeViewAD();
}
//Handles Last Month Change Event
AssignPayReportsToDeposit.OnLastMonthChange = function () {
AssignPayReportsToDeposit.SelectedLastMonth = $('#ddLastMonth').val();
//Refresh TreeView_UPR
AssignPayReportsToDeposit.RefreshTreeViewUPR();
//Refresh TreeView_AD
AssignPayReportsToDeposit.RefreshTreeViewAD();
}
//Handles Month Change Event
AssignPayReportsToDeposit.OnMonthsChange = function () {
AssignPayReportsToDeposit.SelectedMonths = $('#txtMonths').val();
//Refresh TreeView_UPR
AssignPayReportsToDeposit.RefreshTreeViewUPR();
//Refresh TreeView_AD
AssignPayReportsToDeposit.RefreshTreeViewAD();
}
//Fills Last Month Dropdown with options
AssignPayReportsToDeposit.FillLastMonths = function () {
var ddLastMonth = $("#ddLastMonth");
if (ddLastMonth != undefined) {
ddLastMonth.empty();
if (AssignPayReportsToDeposit.LastMonthsList != undefined) {
$.each(AssignPayReportsToDeposit.LastMonthsList, function () {
Common.AddOptionToSelect(ddLastMonth, this.Text, this.Text);
});
ddLastMonth.val(AssignPayReportsToDeposit.LastMonthsList[0].Text);
AssignPayReportsToDeposit.SelectedLastMonth = ddLastMonth.val();
}
}
}
AssignPayReportsToDeposit.ValidateControls = function () {
var success = true;
if (AssignPayReportsToDeposit.SelectedSupplierID == undefined ||
AssignPayReportsToDeposit.SelectedSupplierID == 0) {
// bootbox.alert('Please select a Supplier');
success = false;
}
else if (AssignPayReportsToDeposit.SelectedLastMonth == undefined ||
AssignPayReportsToDeposit.SelectedLastMonth == '') {
// bootbox.alert('Please select Last Month');
success = false;
}
else if (AssignPayReportsToDeposit.SelectedMonths == undefined ||
AssignPayReportsToDeposit.SelectedMonths == 0) {
// bootbox.alert('Please Enter Months');
success = false;
}
return success;
}
//Assigns DepositIdToPayReport
AssignPayReportsToDeposit.AssignDepositIdToPayReport = function (PayReportID, DepositID) {
var success = false;
if (PayReportID != undefined && DepositID != undefined) {
var jsonData = JSON.stringify({ PayReportID: PayReportID, DepositID: DepositID });
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: 'AssignPayReportToDeposit/AssignDepositIdToPayReport',
data: jsonData,
async: false,
success: function (result) {
success = result;
},
error: Common.AjaxErrorHandler
});
}
return success;
}
//--------- Tree View UPR Functions -------- //
//Gets UnAssigned Pay Reports
AssignPayReportsToDeposit.GetUnAssignedPayReports = function () {
var payReports;
if (AssignPayReportsToDeposit.ValidateControls()) {
var jsonData = JSON.stringify(
{
SupplierID: AssignPayReportsToDeposit.SelectedSupplierID,
MonthPaid: AssignPayReportsToDeposit.SelectedLastMonth,
Months: AssignPayReportsToDeposit.SelectedMonths
});
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: "AssignPayReportToDeposit/GetUnAssignedPayReports",
data: jsonData,
async: false,
success: function (data) {
if (data != undefined && data != "")
payReports = data;
},
error: Common.AjaxErrorHandler
});
}
return payReports;
}
AssignPayReportsToDeposit.BindTreeViewUPR = function () {
var treeview = $("#TreeView_UPR");
var inline = new kendo.data.HierarchicalDataSource({
data: AssignPayReportsToDeposit.UnAssignedPayReportsList,
schema: {
model: {
id: "PayReportID"
}
}
});
treeview.kendoTreeView({
dragAndDrop: true,
dataSource: inline,
dataBound: function (e) {
if (!this.dataSource.data().length) {
this.element.append("<p class='no-items'>No items yet.</p>");
} else {
this.element.find(".no-items").remove();
}
},
dataTextField: ["DisplayValue"],
drop: AssignPayReportsToDeposit.OnTreeViewUPRDrop
});
}
AssignPayReportsToDeposit.OnTreeViewUPRDrop = function (e) {
var isTargetTreeViewAD = false;
var sourceDataItem = this.dataItem(e.sourceNode);
var targetDataItem = this.dataItem(e.destinationNode);
if (targetDataItem == undefined) {
targetDataItem = $("#TreeView_AD").data("kendoTreeView").dataItem(e.destinationNode);
isTargetTreeViewAD = true;
}
if (sourceDataItem == undefined ||
targetDataItem == undefined) {
//Source and target both must exists
e.preventDefault();
return;
}
if (sourceDataItem.IsDeposit == true) {
//Deposits cannot be drag and Drop
e.preventDefault();
return;
}
if (isTargetTreeViewAD) {
if (e.dropPosition == "over" &&
sourceDataItem.IsPayReport == true &&
sourceDataItem.IsAssignedPayReport == false &&
targetDataItem.IsDeposit == true) {
//Source must UnAssigned Payreport Target Must be Deposit and Drop position must over
//Implement logic to assign deposit id to the Pay Report
var PayReportID = sourceDataItem.PayReportID;
var DepositID = targetDataItem.DepositID;
if (AssignPayReportsToDeposit.AssignDepositIdToPayReport(PayReportID, DepositID)) {
sourceDataItem.set("DepositID", DepositID);
sourceDataItem.set("IsAssignedPayReport", true);
}
else {
//Didnt update the record don't do the drop
e.preventDefault();
return;
}
}
else {
e.preventDefault();
return;
}
}
else {
if ((e.dropPosition == "before" || e.dropPosition == "after") &&
sourceDataItem.IsPayReport == true &&
targetDataItem.IsPayReport == true &&
targetDataItem.IsAssignedPayReport == false) {
//Only allow sorting in this condition otherwise cancel drop event
//Means only allow sorting of unassigned payreports within the tree
}
else {
e.preventDefault();
return;
}
}
}
AssignPayReportsToDeposit.RefreshTreeViewUPR = function () {
//Destroy and empty tree
var treeview = $("#TreeView_UPR").data("kendoTreeView");
if (treeview != undefined) { treeview.destroy(); }
treeview = $("#TreeView_UPR");
treeview.empty();
AssignPayReportsToDeposit.UnAssignedPayReportsList = AssignPayReportsToDeposit.GetUnAssignedPayReports();
AssignPayReportsToDeposit.BindTreeViewUPR();
}
//--------- TreeView_AD Functions -------- //
//Gets Assigned Pay Reports
AssignPayReportsToDeposit.GetAssignedPayReports = function () {
var payReports;
if (AssignPayReportsToDeposit.ValidateControls()) {
var jsonData = JSON.stringify(
{
SupplierID: AssignPayReportsToDeposit.SelectedSupplierID,
MonthPaid: AssignPayReportsToDeposit.SelectedLastMonth,
Months: AssignPayReportsToDeposit.SelectedMonths
});
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: "AssignPayReportToDeposit/GetAssignedPayReports",
data: jsonData,
async: false,
success: function (data) {
if (data != undefined && data != "")
payReports = data;
},
error: Common.AjaxErrorHandler
});
}
return payReports;
}
AssignPayReportsToDeposit.BindTreeViewAD = function () {
var treeview = $("#TreeView_AD");
var inline = new kendo.data.HierarchicalDataSource({
data: AssignPayReportsToDeposit.AssignedPayReportsList,
schema: {
model: {
id: "DepositID",
hasChildren: "HasAnyAssignedPayReports",
children: "AssignedPayReports"
}
}
});
treeview.kendoTreeView({
dragAndDrop: true,
dataSource: inline,
dataBound: function (e) {
if (!this.dataSource.data().length) {
this.element.append("<p class='no-items'>No items yet.</p>");
} else {
this.element.find(".no-items").remove();
}
},
dataTextField: ["DisplayValue", "DisplayValue"],
drop: AssignPayReportsToDeposit.OnTreeViewADDrop,
select: AssignPayReportsToDeposit.OnTreeViewADSelect
});
}
AssignPayReportsToDeposit.OnTreeViewADSelect = function (e) {
var dataItem = this.dataItem(e.node);
var treeview = this;
if (AssignPayReportsToDeposit.LastTextChangedNode != undefined) {
//Restore last node's Text
var previousDataItem = this.dataItem(AssignPayReportsToDeposit.LastTextChangedNode);
if (previousDataItem != undefined) {
var date = AssignPayReportsToDeposit.FormatDepositMonthToDisplay(previousDataItem.DepositDate);
var displaytext = "[" + date + "]" + "-[" + previousDataItem.BankName + "]-" + "[" + previousDataItem.Amount + "]";
this.text(AssignPayReportsToDeposit.LastTextChangedNode, displaytext);
}
AssignPayReportsToDeposit.LastTextChangedNode = undefined;
}
if (dataItem.IsDeposit) {
if (dataItem.hasChildren > 0) {
dataItem.set("expanded", true);
//Append sum to selected node's diplay value
var childs = dataItem.children.data();
var sum = 0;
$.each(childs, function () { sum += this.Amount });
var date = AssignPayReportsToDeposit.FormatDepositMonthToDisplay(dataItem.DepositDate);
var displaytext = "[" + date + "]" + "-[" + dataItem.BankName + "]-" + "[" + dataItem.Amount + "(" + sum + ")" + "]";
this.text(e.node, displaytext)
AssignPayReportsToDeposit.LastTextChangedNode = e.node;
}
}
}
AssignPayReportsToDeposit.FormatDepositMonthToDisplay = function (jsondate) {
var depositedate = "";
if (jsondate != undefined && jsondate != "") {
var date = Common.ParseDate(jsondate);
var month = ("0" + (date.getMonth() + 1)).slice(-2);
depositedate = date.getFullYear() + "-" + (month);
}
return depositedate;
}
AssignPayReportsToDeposit.OnTreeViewADDrop = function (e) {
var isTargetTreeViewURP = false;
var DroptoNoItemZone = false;
var sourceDataItem = this.dataItem(e.sourceNode);
var targetDataItem = this.dataItem(e.destinationNode);
var treeview_UPR = $("#TreeView_UPR").data("kendoTreeView");
if (targetDataItem == undefined) {
targetDataItem = treeview_UPR.dataItem(e.destinationNode);
if (treeview_UPR.element.find(".no-items").length > 0) DroptoNoItemZone = true;
isTargetTreeViewURP = true;
}
if ((sourceDataItem == undefined ||
targetDataItem == undefined) && DroptoNoItemZone == false) {
e.preventDefault();
return;
}
if (sourceDataItem.IsDeposit == true) {
//Deposits can not be moved within the tree view
e.preventDefault();
return;
}
if (isTargetTreeViewURP) {
if (((e.dropPosition == "before" || e.dropPosition == "after") &&
sourceDataItem.IsPayReport == true &&
sourceDataItem.IsAssignedPayReport == true &&
targetDataItem.IsPayReport == true) || (e.dropPosition == "over" && DroptoNoItemZone)) {
//Implement logic to unassing deposit id to PayReport
var PayReportID = sourceDataItem.PayReportID;
var DepositID = 0;
if (AssignPayReportsToDeposit.AssignDepositIdToPayReport(PayReportID, DepositID)) {
sourceDataItem.set("DepositID", DepositID);
sourceDataItem.set("IsAssignedPayReport", false);
}
else {
//Didnt update the record don't do the drop
e.preventDefault();
return;
}
}
else {
e.preventDefault();
return;
}
}
else {
if (e.dropPosition == "over" &&
sourceDataItem.IsPayReport == true &&
targetDataItem.IsDeposit == true) {
//Implement Logic to change deposit ID for assigned payreport
var PayReportID = sourceDataItem.PayReportID;
var DepositID = targetDataItem.DepositID;
if (AssignPayReportsToDeposit.AssignDepositIdToPayReport(PayReportID, DepositID)) {
sourceDataItem.set("DepositID", DepositID);
sourceDataItem.set("IsAssignedPayReport", true);
}
else {
//Didnt update the record don't do the drop
e.preventDefault();
return;
}
}
else {
e.preventDefault();
return;
}
}
}
AssignPayReportsToDeposit.RefreshTreeViewAD = function () {
//Destroy and empty tree
var treeview = $("#TreeView_AD").data("kendoTreeView");
if (treeview != undefined) { treeview.destroy(); }
treeview = $("#TreeView_AD");
treeview.empty();
AssignPayReportsToDeposit.LastTextChangedNode = undefined;
AssignPayReportsToDeposit.AssignedPayReportsList = AssignPayReportsToDeposit.GetAssignedPayReports();
AssignPayReportsToDeposit.BindTreeViewAD();
}
Unfortunately not out the box.
The Ajax extension methods are really just HTML helpers that work with other jQuery libraries. The helpers create the relavant HTML markup (such as adding custom addtributes data-*="") and the client scripts use this to determine their behaviour.
You could create your own MVC HTML helper and script library to handle change events for you however I would recommend looking at a front end framework such as Angular instead. This library would handle all the events declaratively so you don't need to waste time writing event handlers.
Related
I am using MVC to create my Master Detail form, i have tried this to add my detail record in the to show my detail record in the form so that when user click Add button detail data itself shows in a table.
JQUERY is not working
This is my View:
#section script{
//date picker
$(function () {
$('#orderDate').datepicker({
datepicker: 'mm-dd-yy'
});
});
$(document).ready(function()
{
var orderItems = [];
//Add Button click function
$('#add').click(function () {
//Chk Validation
var isValidItem = true;
if ($('#itemName').val().trim() == '') {
isValidItem = false;
$('#itemName').siblings('span.error').css('visibility', 'vissible')
}
else {
$('#itemName').siblings('span.error').css('visibility', 'hidden')
}
if (!($('#quantity').val().trim() !== '' && !isNaN($('#dvch_nar').val().trim()))) {
isValidItem = false;
$('#quantity').siblings('span.error').css('visibility', 'vissible')
}
else {
$('#quantity').siblings('span.error').css('visibility', 'hidden')
}
if (!($('#itemName').val().trim() !== '' && !isNaN($('#dvch_cr_amt').val().trim()))) {
isValidItem = false;
$('#itemName').siblings('span.error').css('visibility', 'vissible')
}
else {
$('#itemName').siblings('span.error').css('visibility', 'hidden')
}
if (!($('#rate').val().trim() !== '' && !isNaN($('#dvch_cr_amt').val().trim()))) {
isValidItem = false;
$('#rate').siblings('span.error').css('visibility', 'vissible')
}
else {
$('#rate').siblings('span.error').css('visibility', 'hidden')
}
//add item to list if valid
if (isValidItem) {
orderItems.push(
{
ItemName: $('#itemName').val().trim(),
Quantity:parseInt$('#quantity').val().trim(),
Rate: parseInt$('#rate').val().trim(),
Total: parseInt($('#quantity').val().trim())* parseFloat($('#rate').val().trim())
});
//clear fields
$('#itemName').val('').focus();
$('#quantity').val('');
$('#rate').val('');
}
//populate order item
GeneratedItemsTable();
}
);
//save button click function
$('#submit').click(function () {
//validation order
var isAllValid = true;
if(orderItems.length=0)
{
$('#orderItems').html('<span style="color:red;">Please add another item</span>')
isAllValid = false;
}
if ($('#orderNo').val().trim() == '')
{
$('#orderNo').siblings('span.error').css('visibility', 'visible')
isAllValid = false;
}
else {
$('#orderNo').siblings('span.error').css('visibility', 'hidden')
}
if ($('#orderDate').val().trim() == '') {
$('#orderDate').siblings('span.error').css('visibility', 'visible')
isAllValid = false;
}
else {
$('#orderDate').siblings('span.error').css('visibility', 'hidden')
}
//if ($('')
//save if valid
if (isAllValid){
var data={
Date: $('#orderNo').val().trim(),
Remarks: ('#orderDate').val().trim(),
Description:$('description').val().trim(),
orderDetails:orderItems
}
}
$(this).val("Please Wait...");
$.ajax(
{
url: "/Home/SaveOrder",
type:"post",
data:JSON.stringify(data),
dataType:"application/json",
success:function(d){
//check is successfully save to database
if(d.status==true)
{
//will send status from server side
alert('successfully done.');
//clear form
orderItems=[];
$('#orderNo').val('');
$('#orderDate').val('');
$('#orderItems').empty();
}
else{
alert('Failed');
}
},
error :function(){
alert('Error:Please Try again.');
}
}
);
});
//function for show added item
function GeneratedItemsTable()
{
if(orderItems.length>0)
{
var $table = $('<table/>');
$table.append('<thead><tr><th>Item</th><th>Quantity</th><th>Rate</th><th>Total</th></tr></thead>')
var $tboday = $('<tbody/>');
$.each(orderItems,function(i,val)
{
var $row=$('<tr/>');
$row.append($('<tr/>').html(val.ItemName))
$row.append($('<tr/>').html(val.Quantity))
$row.append($('<tr/>').html(val.Rate))
$row.append($('<tr/>').html(val.Total))
$tboday.append($row);
});
$table.append($tboday);
$('#orderItems').html($table);
}
}
}
);
</script>
}
thanks for quick response
Try this
<script src="//code.jquery.com/ui/1.11.4/jquery-ui.js"></script>
<script>
//Date Picker
$(function () {
$('#orderDate').datepicker({
dateFormat : 'mm-dd-yy'
});
});
$(document).ready(function () {
var orderItems = [];
//Add button click function
$('#add').click(function () {
//Check validation of order item
var isValidItem = true;
if ($('#itemName').val().trim() == '') {
isValidItem = false;
$('#itemName').siblings('span.error').css('visibility', 'visible');
}
else {
$('#itemName').siblings('span.error').css('visibility', 'hidden');
}
if (!($('#quantity').val().trim() != '' && !isNaN($('#quantity').val().trim()))) {
isValidItem = false;
$('#quantity').siblings('span.error').css('visibility', 'visible');
}
else {
$('#quantity').siblings('span.error').css('visibility', 'hidden');
}
if (!($('#rate').val().trim() != '' && !isNaN($('#rate').val().trim()))) {
isValidItem = false;
$('#rate').siblings('span.error').css('visibility', 'visible');
}
else {
$('#rate').siblings('span.error').css('visibility', 'hidden');
}
//Add item to list if valid
if (isValidItem) {
orderItems.push({
ItemName: $('#itemName').val().trim(),
Quantity: parseInt($('#quantity').val().trim()),
Rate: parseFloat($('#rate').val().trim()),
TotalAmount: parseInt($('#quantity').val().trim()) * parseFloat($('#rate').val().trim())
});
//Clear fields
$('#itemName').val('').focus();
$('#quantity,#rate').val('');
}
//populate order items
GeneratedItemsTable();
});
//Save button click function
$('#submit').click(function () {
//validation of order
var isAllValid = true;
if (orderItems.length == 0) {
$('#orderItems').html('<span style="color:red;">Please add order items</span>');
isAllValid = false;
}
if ($('#orderNo').val().trim() == '') {
$('#orderNo').siblings('span.error').css('visibility', 'visible');
isAllValid = false;
}
else {
$('#orderNo').siblings('span.error').css('visibility', 'hidden');
}
if ($('#orderDate').val().trim() == '') {
$('#orderDate').siblings('span.error').css('visibility', 'visible');
isAllValid = false;
}
else {
$('#orderDate').siblings('span.error').css('visibility', 'hidden');
}
//Save if valid
if (isAllValid) {
var data = {
OrderNo: $('#orderNo').val().trim(),
OrderDate: $('#orderDate').val().trim(),
//Sorry forgot to add Description Field
Description : $('#description').val().trim(),
OrderDetails : orderItems
}
$(this).val('Please wait...');
$.ajax({
url: '/Home/SaveOrder',
type: "POST",
data: JSON.stringify(data),
dataType: "JSON",
contentType: "application/json",
success: function (d) {
//check is successfully save to database
if (d.status == true) {
//will send status from server side
alert('Successfully done.');
//clear form
orderItems = [];
$('#orderNo').val('');
$('#orderDate').val('');
$('#orderItems').empty();
}
else {
alert('Failed');
}
$('#submit').val('Save');
},
error: function () {
alert('Error. Please try again.');
$('#submit').val('Save');
}
});
}
});
//function for show added items in table
function GeneratedItemsTable() {
if (orderItems.length > 0)
{
var $table = $('<table/>');
$table.append('<thead><tr><th>Item</th><th>Quantity</th><th>Rate</th><th>Total</th><th></th></tr></thead>');
var $tbody = $('<tbody/>');
$.each(orderItems, function (i, val) {
var $row = $('<tr/>');
$row.append($('<td/>').html(val.ItemName));
$row.append($('<td/>').html(val.Quantity));
$row.append($('<td/>').html(val.Rate));
$row.append($('<td/>').html(val.TotalAmount));
var $remove = $('Remove');
$remove.click(function (e) {
e.preventDefault();
orderItems.splice(i, 1);
GeneratedItemsTable();
});
$row.append($('<td/>').html($remove));
$tbody.append($row);
});
console.log("current", orderItems);
$table.append($tbody);
$('#orderItems').html($table);
}
else {
$('#orderItems').html('');
}
}
});
</script>
I managed to make this code to submit a form, it works fine, but I can not implement a validator does not need it, but it shows no message just does not send the form is empty.
I searched but could not quite do it. Can anyone help me?
<script type="text/javascript">
jQuery(document).ready(function(){
jQuery('#newsletter').submit(function(){
var dados = jQuery( this ).serialize();
jQuery.ajax({
type: "POST",
url: "newsletter_cadastrar.asp?f=1",
data: dados,
success: function( data )
{
$('#resultado-form-newsletter').html(data);
}
});
return false;
});
});
</script>
<input id="nomenewslbase" name="nomenewslbase" type="text">
<input id="emailnewslbase" name="emailnewslbase" type="email">
Thank you for your attention.
If you are looking for applying jquery validation rules then you can do it like:
<script type="text/javascript">
function ValidationRules() {
}
ValidationRules.prototype.initializeSaveValidators = function() {
jQuery.validator.addMethod("csProjectName",
function(value, element) {
if ($.trim($('#txtProjectName').val()) == "") {
if ($.trim($(element).val()) != '')
return true;
else
return false;
} else
return true;
}, "Project Name is required");
jQuery.validator.addMethod("csDescription",
function (value, element) {
if ($.trim($('#txtDescription').val()) == "") {
if ($.trim($(element).val()) != '')
return true;
else
return false;
}
else
return true;
}, "Description is required");
};
ValidationRules.prototype.enableSaveValidators = function () {
jQuery.validator.classRuleSettings.csProjectName = { csProjectName: true };
jQuery.validator.classRuleSettings.csDescription = { csDescription: true };
};
ValidationRules.prototype.disableSaveValidators = function () {
jQuery.validator.classRuleSettings.csProjectName = { csProjectName: false };
jQuery.validator.classRuleSettings.csDescription = { csDescription: false };
};
jQuery(document).ready(function () {
var validationRules = new ValidationRules();
validationRules.initializeSaveValidators();
validationRules.enableSaveValidators();
$("#btnsubmit").click(function () {
applyjQueryValidation();
return false;
});
});
function applyjQueryValidation() {
var isValid = $('#newsletter').valid();
//here you can manually check for some extra validations
if (isValid==true) {
alert("valid");
//here is you ajax call
}else{
alert("invalid");
}
}
</script>
Also, you need to include jquery.validate.js
Here is the JSfiddle working example.
Has anyone written a utility that will convert Breeze metadata (captured from entity framework data attributes) into knockout validation extensions (using knockout.validation)?
I have made an function that reads the metadata from an entity and adds validation rules.
app.domain.indicador = (function () {
"use strict";
var constructor = function () {...}
var initializer = function indicadorInitializer(entity) {
var entityType = entity.entityType;
if (entityType) {
console.log(entityType);
for (var i = 0; i < entityType.dataProperties.length; i++) {
var property = entityType.dataProperties[i];
console.log(property);
var propertyName = property.name;
var propertyObject = entity[propertyName];
if (!property.isNullable) {
propertyObject.extend({ required: true });
}
if (property.maxLength) {
propertyObject.extend({ maxLength: property.maxLength });
}
}
for (var i = 0; i < entityType.foreignKeyProperties.length; i++) {
var property = entityType.foreignKeyProperties[i];
console.log(property);
var propertyName = property.name;
var propertyObject = entity[propertyName];
if (!property.isNullable) {
propertyObject.extend({ required: true });
}
if (property.maxLength) {
propertyObject.extend({ maxLength: property.maxLength });
}
//Bussines rule
propertyObject.extend({ notEqual: 0 });
}
}
};
return {
constructor: constructor,
initializer: initializer
};
})();
I use the function as initializer:
store.registerEntityTypeCtor("Indicador", domain.indicador.constructor, domain.indicador.initializer);
It's just a start but for the time is useful for me.
Update:
I changed the way I add validation. I share it here in case it is useful to someone:
Helper object:
app.validatorHelper = (function (breeze) {
var foreignKeyInvalidValue = 0;
function addDataTypeRules(dataType, property) {
switch (dataType) {
case breeze.DataType.DateTime:
//TODO: implement my function to validate dates. This validator is too permissive
property.extend({ date: true });
break;
case breeze.DataType.Int64:
case breeze.DataType.Int32:
case breeze.DataType.Int16:
//it's needed to accept negative numbers because of the autogenerated keys
property.extend({ signedDigit: true });
break;
case breeze.DataType.Decimal:
case breeze.DataType.Double:
case breeze.DataType.Single:
property.extend({ number: true });
break;
}
};
function addValidationRules(entity) {
var entityType = entity.entityType;
if (entityType) {
for (var i = 0; i < entityType.dataProperties.length; i++) {
var property = entityType.dataProperties[i];
//console.log(property);
var propertyName = property.name;
var propertyObject = entity[propertyName];
addDataTypeRules(property.dataType, propertyObject);
if (!property.isNullable) {
propertyObject.extend({ required: true });
}
if (property.maxLength) {
propertyObject.extend({ maxLength: property.maxLength });
}
}
for (var i = 0; i < entityType.foreignKeyProperties.length; i++) {
var property = entityType.foreignKeyProperties[i];
//console.log(property);
var propertyName = property.name;
var propertyObject = entity[propertyName];
addDataTypeRules(property.dataType, propertyObject);
if (!property.isNullable) {
propertyObject.extend({ required: true });
//Bussiness Rule: 0 is not allowed for required foreign keys
propertyObject.extend({ notEqual: foreignKeyInvalidValue });
}
if (property.maxLength) {
propertyObject.extend({ maxLength: property.maxLength });
}
}
}
};
return {
addValidationRules: addValidationRules
};
})(breeze);
The custom validator:
(function (ko) {
ko.validation.rules['signedDigit'] = {
validator: function (value, validate) {
if (!validate) return true;
return ko.validation.utils.isEmptyVal(value) || (validate && /^-?\d+$/.test(value));
},
message: 'Please enter a digit'
};
ko.validation.registerExtenders();
})(ko);
Using the helper at the initializer:
app.domain.valorIndicador = (function (vHelper) {
"use strict";
var constructor = function () {
};
var initializer = function indicadorInitializer(entity) {
vHelper.addValidationRules(entity);
};
return {
constructor: constructor,
initializer: initializer
};
})(app.validatorHelper);
And setting the initializer:
store.registerEntityTypeCtor("ValorIndicador", domain.valorIndicador.constructor, domain.valorIndicador.initializer);
A simple way to bind validation errors from breezejs using knockout.
We can subscribe to validationErrorsChanged event from the entityAspect:
function subscribeValidation() {
return self.entity().entityAspect.validationErrorsChanged.subscribe(function (validationChangeArgs) {
validationChangeArgs.added.forEach(function (item) { addError(item); });
validationChangeArgs.removed.forEach(function (item) { self.validationErrors.remove(item); });
});
}
this.hasError = function (propertyName) {
var array = self.validationErrors();
var match = array.filter(function (item) {
return item.propertyName == propertyName;
});
if (match.length > 0) {
return true;
} else return false;
};
function addError(item) {
self.validationErrors.remove(function (i) {
return i.propertyName == item.propertyName;
});
self.validationErrors.push(item);
}
Finally we can bind to the messages on the UI (I'm using Twitter boostrap css classes)
<div class="control-group" data-bind="css: { 'error': hasError('Nome') }">
<label class="control-label">Nome</label>
<div class="controls">
<input type="text" class="input-xxlarge" data-bind="value: model().Nome">
<span class="help-inline" data-bind="text: getErrorMessage('Nome')"></span>
</div>
</div>
See the full gist here
I've searched this before as I started using breeze with knockout and then I had the exact same question about how to validate stuff, and how to show validation inline.
Considering that breeze already has validation built in, I decided to write a custom Knockout Binding to show the validation result every time the observable value changes and it was quite easy afterall:
Here's the custom binding:
ko.bindingHandlers.breezeValidate = {
init: function (element, valueAccessor, allBindingsAccessor, context) {
var isOk = context.entityAspect.validateProperty(valueAccessor());
var errors = context.entityAspect.getValidationErrors(valueAccessor());
var message = "";
if (errors.length > 0)
message = errors[0].errorMessage;
$(element).html(message);
},
//update the control when the view model changes
update: function (element, valueAccessor, allBindingsAccessor, context) {
debugger;
this.init(element, valueAccessor, allBindingsAccessor, context)
}
};
And the usage is like this:
<span data-bind="text: Name"></span>
<span data-bind="breezeValidate: 'Name'"></span>
This works because of this line:
var isOk = context.entityAspect.validateProperty(valueAccessor());
When breeze is requested to validate the property it ends up calling the observable and it gets registered by knockout, so every time it is changed, this binding will be invoked again and the error message will be updated accordingly.
I'm just showing the first validation message, but of course you can iterate thru all of them and even add a different styling to the element.
Hope this helps!!
Not sure why people would want to use ko.validation - it just replicates the processing breeze's client side is doing anyway. And given the breeze developers hints that validation will get even more power soon, why bother.
So I started with Thiago Oliveira's great work. But I wanted to have the bare minimum of markup. By assuming the use of bootstrap classes & defaulting the validation property name from the previous element I could simplify most markup additions to:
<span class="help-inline" data-bind="breezeValidation: null"></span>
Win!
My ko.bindingHandler:
//Highlight field in red & show first validation message
//
//Outputs first validation message for 'propertyName' or if null: previous controls value binding
//Needs ancestor with 'control-group' class to set class 'error' for Bootstrap error display
//
//Example:
//<td class="control-group">
// <input class="input-block-level text-right" data-bind="value: id" />
// <span class="help-inline" data-bind="breezeValidation: null"></span>
//</td>
//
//Does not and cannot validate keys that already exist in cache. knockout write calls breeze which throws uncaught error
ko.bindingHandlers.breezeValidation = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
// This will be called when the binding is first applied to an element
// Set up any initial state, event handlers, etc. here
var $msgElement = $(element);
var entity = viewModel;
var propName = valueAccessor();
if (propName === null) {
// $element.prev().data("bind") = "value: itemType"
var prevBinds = $msgElement.prev().data("bind");
if (!prevBinds) {
$msgElement.text("Could not find prev elements binding value.");
return;
}
var bindPhrases = prevBinds.split(/,/);
for (var i = 0, j = bindPhrases.length; i < j; i++) {
var bindPhrase = bindPhrases[i];
if (utility.stringStartsWith(bindPhrase, 'value: ')) {
propName = bindPhrase.substr(7);
break;
}
}
}
if (!propName) {
$msgElement.text("Could not find this or prev elements binding value.");
return;
}
//var $groupElement = $msgElement.parent();
var $groupElement = $msgElement.closest(".control-group");
if (!$groupElement.hasClass("control-group")) {
$msgElement.text("Could not find parent with 'control-group' class.");
return;
}
onValidationChange(); //fire immediately (especially for added)
//... and anytime validationErrors are changed fire onValidationChnange
entity.entityAspect.validationErrorsChanged.subscribe(onValidationChange);
element.onchange = function () {
//Should never have updates pushed from validation msgElement
$msgElement.text("readonly error");
};
function onValidationChange() {
var errors = entity.entityAspect.getValidationErrors(propName);
var message = "";
if (errors.length > 0) {
message = errors[0].errorMessage;
}
if (message) {
$groupElement.addClass('error');
}
else {
$groupElement.removeClass('error');
}
$msgElement.text(message);
}
}
//Not interested in changes to valueAccessor - it is only the fieldName.
//update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
};
Example view simple implicit property usage:
<div class="control-group">
<label class="control-label" for="editStatusNote">Status note:</label>
<div class="controls">
<input id="editStatusNote" type="text" data-bind="value: statusNote" />
<span class="help-inline" data-bind="breezeValidation: null"></span>
</div>
</div>
Example view explicit property usage:
<div class="control-group">
<label class="control-label" for="editAmount">Amount:</label>
<div class="controls">
<div class="input-prepend">
<span class="add-on">$</span>
<input id="editAmount" class="input-small" type="text" data-bind="value: amount" />
</div>
<span class="help-inline" data-bind="breezeValidation: 'amount'"></span>
</div>
</div>
I updated breezeValidation to Bootstrap 3 and improved with multipath property support.
ko.bindingHandlers.breezeValidation = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
// This will be called when the binding is first applied to an element
// Set up any initial state, event handlers, etc. here
var $msgElement = $(element);
var entity = viewModel;
var propName = valueAccessor();
if (propName === null) {
// $element.prev().data("bind") = "value: itemType"
var prevBinds = $msgElement.prev().data("bind");
if (!prevBinds) {
$msgElement.text("Could not find prev elements binding value.");
return;
}
var bindPhrases = prevBinds.split(/,/);
for (var i = 0, j = bindPhrases.length; i < j; i++) {
var bindPhrase = bindPhrases[i];
if (bindPhrase.substr(0, 7) == 'value: ') {
propName = bindPhrase.substr(7);
entity = ko.utils.unwrapObservable(entity);
var propPath = propName.replace(/[()]/g, "").split('.'), i = 0;
var tempProp = entity[propPath[i]], links = propPath.length;
i++;
while (ko.utils.unwrapObservable(tempProp) && i < links) {
entity = ko.utils.unwrapObservable(tempProp);
tempProp = entity[propName = propPath[i]];
i++;
}
break;
}
}
}
if (!propName) {
$msgElement.text("Could not find this or prev elements binding value.");
return;
}
//var $groupElement = $msgElement.parent();
var $groupElement = $msgElement.closest(".form-group");
if (!$groupElement.hasClass("form-group")) {
$msgElement.text("Could not find parent with 'form-group' class.");
return;
}
onValidationChange(); //fire immediately (especially for added)
//... and anytime validationErrors are changed fire onValidationChnange
entity.entityAspect.validationErrorsChanged.subscribe(onValidationChange);
element.onchange = function () {
//Should never have updates pushed from validation msgElement
$msgElement.text("readonly error");
};
function onValidationChange() {
var errors = entity.entityAspect.getValidationErrors(propName);
var message = "";
if (errors.length > 0) {
message = errors[0].errorMessage;
}
if (message) {
$groupElement.addClass('has-error');
}
else {
$groupElement.removeClass('has-error');
}
$msgElement.text(message);
}
}
//Not interested in changes to valueAccessor - it is only the fieldName.
//update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
};
Knockout validator can use breeze validation as a whole:
function addKoValidationRules(entity) {
if (entity.koValidationRulesAdded) {
return;
}
entity.entityType.dataProperties.forEach(function (property) {
entity[property.name].extend({
validation: {
validator: function () {
// manual validation ensures subscription to observables which current field depends on
// entity is added to context for retrieving other properties in custom validators
entity.entityAspect.validateProperty(property.name, { entity: entity });
var errors = entity.entityAspect.getValidationErrors(property.name);
if (!errors.length) {
return true;
}
this.message = errors[0].errorMessage;
return false;
},
message: ''
}
});
});
entity.koValidationRulesAdded = true;
}
I have a slickgrid using the dataview. From what I can tell, the grid's add new row event isn't called until after the first new row field's editor is complete. The field I was editing is a custom editor that uses a input box with autocomplete and save the selected item "value" to the grid source data. The problem is the new "item" source isn't created until the grid add new row event is fired. I know there is a way around this and just want to know what is the best way to solve for this.
Thanks.
//Add new row event
grid.onAddNewRow.subscribe(function (e, args) {
var item = args.item;
addnewid = addnewid - 1;
item.inventorytransferdetailid = addnewid;
$.extend(item, args.item);
dataView.addItem(item);
});
// Custom editor
function Suggest2(args) {
var $input;
var defaultValue;
var scope = this;
this.init = function () {
$input = $("<INPUT type=text class='editor-text' />")
$input.width = args.column.width;
$input.appendTo(args.container);
$input.focus();
$input.bind("keydown.nav", function (e) {
if (e.keyCode === $.ui.keyCode.LEFT || e.keyCode === $.ui.keyCode.RIGHT) {
e.stopImmediatePropagation();
}
else if ($input.val().length > 0) {
$.ajax({
type: "GET",
url: "http://localhost:11111/GetProducts/" + $input.val(),
dataType: "json",
data: "{}",
contentType: "application/json; charset=utf-8",
success: function (data) {
$input.autocomplete({
source: data,
select: function (event, ui) {
var v = ui.item.value;
var l = ui.item.label;
//Set "display" field with label
args.item[args.column.field] = l;
this.value = l;
//Set "hidden" id field with value
args.item["productid"] = v;
return false;
}
});
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert(textStatus);
}
});
}
})
};
this.destroy = function () {
$input.remove();
};
this.focus = function () {
$input.focus();
};
this.getValue = function () {
return $input.val();
};
this.setValue = function (val) {
$input.val(val);
};
this.loadValue = function (item) {
defaultValue = item[args.column.field] || "";
$input.val(defaultValue);
$input[0].defaultValue = defaultValue;
$input.select();
};
this.serializeValue = function () {
return $input.val();
};
this.applyValue = function (item, state) {
//item[args.column.field] = state;
};
this.isValueChanged = function () {
return (!($input.val() == "" && defaultValue == null)) && ($input.val() != defaultValue);
};
this.validate = function () {
if (args.column.validator) {
var validationResults = args.column.validator($input.val());
if (!validationResults.valid) {
return validationResults;
}
}
return {
valid: true,
msg: null
};
};
this.init();
}
You can try below code.
function add_new_row(){
item = {
"id": (Math.round(Math.random()*-10000))
};
data_view.insertItem(0, item);
}
Then, bind with button.
<button onclick="add_new_row()">Add Row</button>
Inside client control I generate a button, with script to run.
I want to call object's Print() method when this button is clicked, the result value must be passed to Print() as well.
How can I do that?
This is my object:
Type.registerNamespace("CustomControls");
CustomControls.FirstObj = function(element) {
CustomControls.FirstObj.initializeBase(this, [element]);
this._targetControlDelegate === null
this.markUp = '<div><input type="button" id="theButton" value="Button!" onclick="Foo()"/><script type="text/javascript">function Foo() {return "result";}</script></div>';
}
CustomControls.FirstObj.prototype = {
dispose: function() {
CustomControls.FirstObj.callBaseMethod(this, 'dispose');
},
initialize: function() {
var div;
div = document.createElement('div');
div.name = div.id = "divName";
div.innerHTML = this.markUp;
document.body.appendChild(div);
var targetControl = $get("theButton");
// if (targetControl != null) {
// if (this._targetControlDelegate === null) {
// this._targetControlDelegate = Function.createDelegate(this, this._targetControlHandler);
// }
// Sys.UI.DomEvent.addHandler(targetControl, 'click', this._targetControlDelegate);
// }
CustomControls.FirstObj.callBaseMethod(this, 'initialize');
},
// _targetControlHandler: function(event) {
//
//
// },
_Print: function(result) {
//Alert Result
},
}
CustomControls.FirstObj.registerClass('CustomControls.FirstObj', Sys.UI.Control);
if (typeof (Sys) !== 'undefined') Sys.Application.notifyScriptLoaded();
Update:
I guess there is no solution for my problem.
Maybe there is an alternative approach that you can suggest?
One way would be to store the FirstObj object in a property of the button it just created:
initialize: function() {
var div = document.createElement("div");
div.name = div.id = "divName";
div.innerHTML = this.markUp;
document.body.appendChild(div);
var targetControl = $get("theButton");
targetControl.__firstObj = this;
CustomControls.FirstObj.callBaseMethod(this, 'initialize');
}
That would allow you to use that property to refer to the FirstObj object inside your markup:
CustomControls.FirstObj = function(element) {
CustomControls.FirstObj.initializeBase(this, [element]);
this._targetControlDelegate = null;
this.markUp = '<div><input type="button" id="theButton" value="Button!"'
+ 'onclick="this.__firstObj._Print(Foo());" />'
+ '<script type="text/javascript">function Foo() {return "result";}'
+ '</script></div>';
}