I wonder if it's possible to have controls (dataanotation) on hidden fields (HiddenFor or hidden EditorFor) ?
I don't think so, but we never know.
There are a lot of posts on how to hide EditorFor such as :
TextBoxFor vs EditorFor, and htmlAttributes vs additionalViewData
In my case,in a view I have a jquery call to a WCF REST service, that in success case fill my EditorFor. I would like that the Required DataAnotation to be applied on that EditorFor, would it be possible ?
I think that as long as the EditorFor is invisible the DataAnotation cannot be applied. Would it have a way to apply the DataAnotation on the hidden EditorFor ?
Here is the code :
To hide the EditorFor :
#Html.EditorFor(model => model.VilleDepart, "CustomEditor", new {style = "display:none;" })
The CustomEditor :
#{
string s = "";
if (ViewData["style"] != null) {
// The ViewData["name"] is the name of the property in the addtionalViewData...
s = ViewData["style"].ToString();
}
}
#Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue, new { style = s })
the model :
string _VilleDepart;
[Required]
[Display(Name = "Ville Départ")]
public string VilleDepart
{
get
{
if (Commune != null) {
return Commune.Commune1;
}
return _VilleDepart;
}
set {
_VilleDepart = value;
}
}
The JQuery call to WCF REST Service :
$(document).ready(function () {
$([document.getElementById("IVilleDepart"), document.getElementById("IVilleArrivee")]).autocomplete({
source: function (request, response) {
$.ajax({
cache: false,
type: "GET",
async: false,
dataType: "json",
url: GetSearchCommunetURl + "(" + request.term + ")",
success: function (data) {
//alert(data);
response($.map(data, function (item) {
return {
label: item['Commune'] + ' (' + item['CodePostal'] + ')',
val: item
}
}))
},
error: function (response) {
alert("error ==>" + response.statusText);
},
failure: function (response) {
alert("failure ==>" + response.responseText);
}
});
},
select: function (e, i) {
if (e.target.id == "IVilleDepart") {
VilleDepart = i.item.val;
EVilleDepart.value = VilleDepart.Commune;
ECodePostalDepart.value = VilleDepart.CodePostal;
ECodeINSEEDepart.value = VilleDepart.CodeINSEE;
}
if (e.target.id == "IVilleArrivee") {
VilleArrivee = i.item.val;
EVilleArrivee.value = VilleArrivee.Commune;
ECodePostalArrivee.value = VilleArrivee.CodePostal;
ECodeINSEEArrivee.value = VilleArrivee.CodeINSEE;
}
},
minLength: 2
});
});
If I don't hide the EditorFor I can see it is correctly filled after the WCF REST service call and the Required DataAnotation is applied.
There are other way to hide the EditorFor, for instance to apply the style='width:0px;height:0px'
It hides but disable the Required DataAnotation,
if I apply the style='width:0px;height:1px', we don't see a lot of the EditorFor but the Required DataAnotation is active.
I've seen an answer at http://www.campusmvp.net/blog/validation-of-hidden-fields-at-the-client-in-asp-net-mvc
(but it seems i had badly searched precedently, the validation of hidden field is treated in some blogs and sites).
To active the validation of hidden fields, you just have to add this little javascript line :
$.validator.setDefaults({ ignore: null });
and it works !
Apparently it doesn't work with mvc2, but works since mvc3.
I am implementing server side paging & sorting for jqGrid in MVC 4. I am passing view model object as postData to jqGrid url action method. have a look at grid definition.
var isGridDefined = false;
$(document).ready(function () {
function DefineGrid(Year, Month) {
var mygrid = $("#RptUpload");
mygrid.jqGrid({
loadonce: false,
async: false,
datatype: 'json',
postData: { bReload: true, Year: Year, Month: Month },
url: '#Url.Action("DMEUploadDetailsList", "Reports")',
jsonReader: { repeatitems: false, root: "DataRows" },
colNames: ['#VirtuOxAdmin.DMEUploadDetails_Grid_RptUpload_OrderID',
'#VirtuOxAdmin.DMEUploadDetails_Grid_RptUpload_CompanyName',
'#VirtuOxAdmin.DMEUploadDetails_Grid_RptUpload_PatientID',
'#VirtuOxAdmin.DMEUploadDetails_Grid_RptUpload_PatientName',
"#VirtuOxAdmin.DMEUploadDetails_Grid_RptUpload_DOB",
'#VirtuOxAdmin.DMEUploadDetails_Grid_RptUpload_Insurance',
"#VirtuOxAdmin.DMEUploadDetails_Grid_RptUpload_UploadDate"
],
colModel: [
{ name: 'ReadingID', index: 'ReadingID', width: 55, fixed: true, sorttype: 'integer', align: 'center' },
{
name: 'CompanyName', index: 'CompanyName', align: 'center', width: 200,
cellattr: function (rowId, tv, rawObject, cm, rdata) { return 'style="white-space: normal!important;' },
},
{ name: 'PatientID', index: 'PatientID', width: 55, fixed: true, sorttype: 'integer', align: 'center' },
{
name: 'PatientName', index: 'PatientName', align: 'center', width: 200,
cellattr: function (rowId, tv, rawObject, cm, rdata) { return 'style="white-space: normal!important;' },
},
{
name: 'DOB', index: 'DOB', width: 80, fixed: true, sorttype: 'date', formatter: 'date', formatoptions: { srcformat: 'm/d/Y', newformat: 'm/d/Y' },
align: 'center'
},
{ name: 'InsuranceType', index: 'InsuranceType', align: 'center', width: 150, cellattr: function (rowId, tv, rawObject, cm, rdata) { return 'style="white-space: normal!important;' }, },
{
name: 'UploadDate', index: 'UploadDate', width: 80, fixed: true, sorttype: 'date', formatter: 'date', formatoptions: { srcformat: 'm/d/Y', newformat: 'm/d/Y' },
align: 'center'
}
],
rowNum: 20,
rowList: [20, 50, 100, 200],
pager: '#UploadPager',
caption: '#VirtuOxAdmin.DMEUploadDetails_Grid_RptUpload_Title',
viewrecords: true,
height: 'auto',
width: 770,
hidegrid: false,
shrinkToFit: true,
scrollOffset: 0,
headertitles: true,
loadError: function (xhr, status, error) {
alert(status + " " + error);
},
//onPaging: function (pgButton) {
// $("#RptUpload").jqGrid("setGridParam", { postData: { bReload: false } });
//},
loadCompete: function () {
$("#RptUpload").jqGrid("setGridParam", { datatype: 'json', postData: { bReload: false } });
}
});
mygrid.navGrid('#UploadPager', { edit: false, add: false, del: false, search: false, refresh: false });
isGridDefined = true;
}
$("#rptRefresh").click(function (e) {
e.preventDefault();
var Form = $("form[id='FrmDMEUploadDetails']");
Form.validate();
if (Form.valid()) {
RemoveValidatioMessages();
$("#gridContainer").show();
var Year = $("#Year").val();
var Month = $("#Month").val();
if (!isGridDefined)
DefineGrid(Year, Month);
else
$("#RptUpload").jqGrid("setGridParam", { datatype: "json", page: 1, postData: { bReload: true, Year: Year, Month: Month } }).trigger("reloadGrid");
}
else {
$("#RptUpload").clearGridData();
$("#gridContainer").hide();
}
$(".chzn-select-deselect").trigger("liszt:updated");
return false;
});
});
& my action method is as follows
public ActionResult DMEUploadDetailsList(bool bReload, string Year, string Month, string nd, int rows, int page, string sidx, string sord, string filters)
{
DataSet SearchResult = null;
List<ReportData> ResultRows = new List<ReportData>();
JQGridResult Result = new JQGridResult();
if (bReload)
{
SearchResult = DB.ExecuteDataset("ConnectionString", "pc_GetUploadDetail",
new SqlParameter("#Year", Year),
new SqlParameter("#Month", Month));
Common.SetSession(SearchResult, null, "DMEUploadByMonth");
}
else
SearchResult = SessionManager.GetSession().GetAttribute("DMEUploadByMonth") as DataSet;
if (SearchResult != null)
{
DataTable dtSearchResult = SearchResult.Tables[0];
# region Handle server side Filtering, sorting and paging
int totalRecords = dtSearchResult.Rows.Count; //before paging
int totalPages = (int)Math.Ceiling((decimal)totalRecords / (decimal)rows); //--- number of pages
int startIndex = ((page > 0 ? page - 1 : 0) * rows);
if (sidx != "")
{
dtSearchResult.DefaultView.Sort = sidx + " " + sord;
dtSearchResult = dtSearchResult.DefaultView.ToTable();
}
# endregion
for (int i = startIndex; i < dtSearchResult.Rows.Count; i++)
{
ResultRows.Add(new ReportData()
{
ReadingID = Convert.ToInt32(dtSearchResult.Rows[i][0]),
CompanyName = Convert.ToString(dtSearchResult.Rows[i][1]),
PatientID = Convert.ToInt32(dtSearchResult.Rows[i][2]),
PatientName = Convert.ToString(dtSearchResult.Rows[i][3]),
DOB = (dtSearchResult.Rows[i][4] != DBNull.Value ? Convert.ToDateTime(dtSearchResult.Rows[i][4]) : (DateTime?)null),
InsuranceType = Convert.ToString(dtSearchResult.Rows[i][5]),
UploadDate = (dtSearchResult.Rows[i][6] != DBNull.Value ? Convert.ToDateTime(dtSearchResult.Rows[i][6]) : (DateTime?)null)
});
if (ResultRows.Count == rows) break;
}
Result.DataRows = ResultRows;
Result.page = page;
Result.total = totalPages;
Result.records = totalRecords;
}
return Json(Result, JsonRequestBehavior.AllowGet);
}
The problem with current implementation is that my action method DMEUploadDetailsList is not getting called though view model object is getting passed to request successfuly.
& This implementation were working fine when used client side paging & sorting.
Please suggest me If I am missing anything or correct my mistakes to get server side paging & sorting working.
This grid is defined or reloaded on refresh button. Now what I want is to identify whether action method is called on refresh button click or paging & sorting operation?
[ Now I would like to describe the last two sentence of problem statement. It specifies that when my page is loaded grid is not defined. As soon as I select filter & clicks refresh button my grid is defined for first time & reloaded for subsequent clicks of refresh. If you go through the action method code you will see that i am trying to use bReload bit variable for, when it is true [in case of refresh button click] I would like to query data from SQL otherwise from dataset stored in session [in case of paging or sorting request]. Now If you looked at the postData parameter in definition or in reload call I am passing breload as true. Where as I am not aware of how can I override this parameter to false when user request for sorting & paging. Or else if there is any another simple way with which in action method I can get whether this request is of load data or paging & sorting.]
Sorry, but I don't see any implementation of paging in your code. You calculate the number of records which need be skipped and save it in startIndex, but you don't use startIndex later. Your current code just get data from DataTable and returns all items of the table. You need to skip startIndex items. For example you can start for loop from i = startIndex instead of i = 0.
In general it would be more effective to construct SELECT statement in SqlCommand which uses TOP construct or use STORED PROCEDURE like described in the answer (see another answer too). In the way your server code will get from the SQL server only one page of data instead of fetching all records of data and returning only one page.
UPDATED: I would rewrite your client code to something like the following
$(document).ready(function () {
var templateDate = {
width: 80,
fixed: true,
sorttype: "date",
formatter: "date",
formatoptions: { srcformat: "m/d/Y", newformat: "m/d/Y" }
},
templateInt = { width: 55, fixed: true, sorttype: "integer" },
templateText = {
width: 200,
cellattr: function () {
return 'style="white-space: normal!important;'
}
},
mygrid = $("#RptUpload");
// create the grid without filling it (datatype: "local")
mygrid.jqGrid({
datatype: "local", // to revent initial loading of the grid
postData: {
bReload: true,
Year: function () { return $("#Year").val(); },
Month: function () { return $("#Month").val(); }
},
url: '#Url.Action("DMEUploadDetailsList", "Reports")',
jsonReader: { repeatitems: false, root: "DataRows" },
colNames: [ "#VirtuOxAdmin.DMEUploadDetails_Grid_RptUpload_OrderID",
"#VirtuOxAdmin.DMEUploadDetails_Grid_RptUpload_CompanyName",
"#VirtuOxAdmin.DMEUploadDetails_Grid_RptUpload_PatientID",
"#VirtuOxAdmin.DMEUploadDetails_Grid_RptUpload_PatientName",
"#VirtuOxAdmin.DMEUploadDetails_Grid_RptUpload_DOB",
"#VirtuOxAdmin.DMEUploadDetails_Grid_RptUpload_Insurance",
"#VirtuOxAdmin.DMEUploadDetails_Grid_RptUpload_UploadDate"
],
colModel: [
{ name: "ReadingID", template: templateInt },
{ name: "CompanyName", template: templateText },
{ name: "PatientID", template: templateInt },
{ name: "PatientName", template: templateText },
{ name: "DOB", template: templateDate },
{ name: "InsuranceType", width: 150, template: templateText },
{ name: "UploadDate", template: templateDate }
],
cmTemplate: { align: "center" },
rowNum: 20,
rowList: [20, 50, 100, 200],
pager: "#UploadPager",
caption: "#VirtuOxAdmin.DMEUploadDetails_Grid_RptUpload_Title",
viewrecords: true,
height: "auto",
width: 770,
hidegrid: false,
headertitles: true,
loadError: function (xhr, status, error) {
alert(status + " " + error);
}
});
mygrid.navGrid("#UploadPager",
{ edit: false, add: false, del: false, search: false, refresh: false });
mygrid.closest(".ui-jqgrid").hide(); // hide the grid
$("#rptRefresh").click(function (e) {
var Form = $("form[id=FrmDMEUploadDetails]");
e.preventDefault();
Form.validate();
if (Form.valid()) {
RemoveValidatioMessages();
mygrid.jqGrid("setGridParam", { datatype: "json" })
.trigger("reloadGrid", [{page: 1}])
.closest(".ui-jqgrid").show(); // show the grid;
} else {
mygrid.clearGridData();
mygrid.closest(".ui-jqgrid").hide(); // hide the grid
}
$(".chzn-select-deselect").trigger("liszt:updated");
return false;
});
});
The grid will be created with datatype: "local", so the url and postData will be ignored. After that it seems to me the usage of bReload property in postData and on the server side seems me unneeded. Nevertheless I included bReload still in the JavaScript code till you remove it from the server code.
Additionally I simplified the colModel by usage of column templates (cmTemplate option of jqGrid and template property of colModel). See the old answer for more information. I removed also some unneeded options of jqGrid which values are default (see "Default" column in the documentation of options).
About usage of new versions of STORED PROCEDURE (pc_GetUploadDetail in your code) you can consider to introduction new version (like pc_GetUploadDetailPaged) which supports more parameters. It will not break existing code which uses the old procedure, but you can still use sorting and paging of data on SQL Server instead of getting all query results to web server and implementing sorting and paging in C#.
I solved my initial problem of jqGrid url not getting called by removing Form.serialize() as postData parameter in jqGrid definition & reload calles. This problem was not related to server side sorting & paging. This was caused due to my previous postData parameter definition as Form.serialize(). Instead I used pass individual parameters to the postData array.
My next problem was related to sorting & paging at server side. In which I want to fetch data from session dataset when user page through the grid or want to sort the grid data; otherwise reload new data by querying SQL server.
According to Oleg answer I must have adopted the simplified way of doing paging & sorting at SQL end instead of in c#. But I was not allowed to add new version of stored procedure as per Oleg suggestion. So I stick to use extra parameter in postData array named operCode like this in grid definition.
postData: {
operCode: "Reload",
Year: function () { return $("#Year").val(); },
InsuranceID: function () { return $("#InsuranceType").val(); },
CustomerID: function () { return $("#CompanyName").val(); }
},
Now I added onPaging & onSortCol event to override operCode postData parameter value like this
onPaging: function (pgButton) {
mygrid.jqGrid("setGridParam", { datatype: 'json', postData: { operCode: "Paging" } });
},
onSortCol: function (index, iCol, sortorder) {
mygrid.jqGrid("setGridParam", { datatype: 'json', postData: { operCode: "Sorting" } });
}
Now whenever user clicks refresh button operCode is sent as Reload, on paging as "Paging" & on sorting as "Sorting"
My server side action method code is as follows
public ActionResult DMEUploadDetails(string operCode, string Year, string Month, string nd, int rows, int page, string sidx, string sord, string filters)
{
DataSet SearchResult = null;
List<ReportData> ResultRows = new List<ReportData>();
JQGridResult Result = new JQGridResult();
if (operCode == "Reload")
{
SearchResult = DB.ExecuteDataset("ConnectionString", "pc_GetUploadDetail",
new SqlParameter("#Year", Year),
new SqlParameter("#Month", Month));
Common.SetSession(SearchResult, null, "POXMonthlyUploads");
}
else
SearchResult = (SessionManager.GetSession().GetAttribute("POXMonthlyUploads") as System.Web.UI.WebControls.GridView).DataSource as DataSet;
if (SearchResult != null)
{
DataTable dtSearchResult = SearchResult.Tables[0];
# region Handle server side Filtering, sorting and paging
int totalRecords = dtSearchResult.Rows.Count; //before paging
int totalPages = (int)Math.Ceiling((decimal)totalRecords / (decimal)rows); //--- number of pages
int startIndex = ((page > 0 ? page - 1 : 0) * rows);
if (sidx != "" && operCode == "Sorting")
{
dtSearchResult.DefaultView.Sort = sidx + " " + sord;
dtSearchResult = dtSearchResult.DefaultView.ToTable();
SearchResult.Tables.RemoveAt(0);
SearchResult.Tables.Add(dtSearchResult);
Common.SetSession(SearchResult, null, "POXMonthlyUploads");
}
# endregion
for (int i = startIndex; i < dtSearchResult.Rows.Count; i++)
{
ResultRows.Add(new ReportData()
{
//code to fill data to structure object
});
if (ResultRows.Count == rows) break;
}
Result.DataRows = ResultRows;
Result.page = page;
Result.total = totalPages;
Result.records = totalRecords;
}
return Json(Result, JsonRequestBehavior.AllowGet);
}
Very much Thanks to Oleg for giving me some extra knowledge about jqGrid & giving me good idea about server side paging & sorting.