So I have a jqGrid on an ASP.NET MVC 3 website. It's loading the data, searching, filtering, and saving rows with the built in pop-up editor. What I can't get to work is saving a nullable property. I'm using LargeJsonResult instead of the built in JsonResult, so an example of a row in the grid is this:
// C# class
public class Row
{
public string A { get; set; }
public string B { get; set; }
public int C { get; set; }
}
// an example object instance, let's say these values come from the DB
var ret = new Row { A = "a", B = null, C = 5 };
// the JSON string sent to the grid will look like this (notice B is omitted)
// "{ A: 'a', C: 5 }"
Now, the grid will show this as:
A B C
a undefined 5
And this brings me to my problem. The pop-up edit form will show "undefined" in the textbox for B, and will also post this to the server. So if I save that to the database, I'll have "undefined" in my DB instead of null.
How do I get jqGrid to preserve the null value round trip? One solution that seems to me very hacky is based on something Oleg solved in another thread:
// override jqGrid serialization
jQuery.extend(jQuery.jgrid.edit, { ajaxEditOptions: { contentType: "application/json" }, serializeEditData: function (data) {
return JSON.stringify(data).replace(/"undefined"/g, 'null');
}});
This will work, but seems dangerous because it's doing mass edits of data without the user's knowledge. In thinking more about it, I guess this is the fundamental problem of saving back null instead of "undefined" or some other string representation of null (empty string, etc.). The desired behavior would be:
if the property is null, and the user doesn't change the value, it posts as null
if the user changes the value, the property is no longer null
Can we get the grid's edit form to behave like this for nullable properties? Or would I have to create a custom edit form that tracks what the user does with a property?
I hope that I understand your problem. In one my applications where I used jqGrid I had once the problem with NULL values. At the time of development of the application I was not sure how to solve the problem and I placed on the server side the text value "(NULL)" instead of null value of one property. The grid was for advanced users who understand what "(NULL)" is. The value "(NULL)" has no sense in the field (one had no user account with the name) and inside of server code for the Edit operation I could distinguish "(NULL)" value from the real field value. In the way I could come over the problem.
In your case you should solve at least two problems:
You should decide how the null value should be displayed. The "undefined" text seems me not the best one. You can solve the problem with the "undefined" text either on the server side (like I did in my case) or with respect of custom formatter. The custom formatter is very simple thing. It define how a cell value should be displayed as a HTML fragment of the <td> contain. You can for example include an additional hidden <span> element or other HTML element or attribute which will save the information that the value was null.
You should solve the problem with decoding of the null value on the server side during Edit operations. You can solve the problem very easy on the server side (like I do with compare of the corresponding field to the "(NULL)") or with respect of custom unformatter on the client side. The custom unformatter will get the information from the grid cell (from the hidden <span> or other hidden HTML element or attribute) and place the information in the server request.
You can look at the demo for the answer to see an example how one can use a hidden <span> to save an additional information in the cell with respect of custom formatter and to read the information later with respect of custom unformatter.
Related
I have a project work work with hundreds of grids. On one grid in particular, the original designer created a view and added it as a nested model inside our driver model. This is a problem because Kendo seems to not be able to detect the field type of a field nested this way. When it can't, it assumes the field is a string.
In my particular case, I have two fields that contain dates that we want to filter on, but the date filtering is broken because of this.
I was able to find numerous examples of specifying it in the data source, but only for jquery grids (one example: https://docs.telerik.com/kendo-ui/knowledge-base/use-nested-model-properties ). I can't convert this to mvc because the methods aren't lining up.
Any idea on how to do this in mvc? Note: I also tried just changing the UI on the filter to date and that didn't work either. I got a stack trace claiming date needed to be a string.
As far as I understand the model passed to the Grid's field is structured like this:
public class OrderViewModel
{
public int OrderID
{
get;
set;
}
public CityModel ShipCity
{
get;
set;
}
}
public class CityModel
{
public int CityID
{
get;
set;
};
public string CityName
{
get;
set;
};
}
Then you can utilize the ClientTemplate configuration to get the CityName to populate the column:
columns.Bound(p => p.ShipCity).ClientTemplate("#=ShipCity.CityName#");
For more information refer to the Kendo UI Templates article
or
this forum answer that shows how to set a default to a nested field in a Grid's DataSource Model configuration.
Unfortunately, according to Telerik , the fact is that you simply can't do what I was trying to do. The solution is to flatten the model. I pulled the questionable fields into notmapped fields in the master model.
The relevant part of their response:
In MVC the nested properties are properties to another Model. As the Telerik UI Grid needs a flat structure of data, the nested properties could be shown with the help of a ClientTemplate, but they will always be string typed.
In order to achieve the desired behavior, I would recommend making the pointed Date fields - part of the Main Model and using them as flat data out of the box.
Link: https://www.telerik.com/forums/kendo-mvc-wrapper-grid-how-do-i-specify-the-data-type-of-a-nested-model-field
The caveat here is you can't filter a linq-for-queries datasource to a notmapped field because it doesn't exist in it but in my case, a .toList() didn't cause any performance problems because the dataset was small, with only a few hundred records.
However, you can edit the incoming DatasourceRequest object to modify the filters and sorts to point at the sub model, after your controller read is hit.
I have abstract group box. Inside this group box I have smart field, and on change I would like to fill some other fields in form.
Problems are :
on abstract group box new box form data can't be created.
if I try to import form data, because import form data called setValue loop is detected.
My try was to change abstract group box to "normal group box" and set values like this :
#Override
protected void execChangedValue() {
AbstractCarSelectionBoxData formData = new AbstractCarSelectionBoxData();
FormDataUtility.exportFormData(this.getParentField(), formData);
formData = BEANS.get(IOfferFormService.class).loadCarInformations(formData);
FormDataUtility.importFormFieldData(this.getParentField(), formData, false, null, null);
}
This approach actually work, and fill the data, but I get
2016-03-22 10:18:57,448 WARN scout-model-thread-20 o.e.s.rt.client.ui.form.fields.AbstractValueField - Loop detection in com.sixt.leasing.scout.client.template.AbstractCarBox$CarTypeSixtField with value 279096 [m4042 # ]
java.lang.Exception: null
at org.eclipse.scout.rt.client.ui.form.fields.AbstractValueField.setValue(AbstractValueField.java:324) [org.eclipse.scout.rt.client-5.2.0.M5.jar:5.2.0.M5]
at org.eclipse.scout.rt.client.ui.form.fields.AbstractValueField.importFormFieldData(AbstractValueField.java:219) [org.eclipse.scout.rt.client-5.2.0.M5.jar:5.2.0.M5]
What is right approach to deal with this case?
I know that I could always get only the DTO object and set values in client, but this is not what I would like to have...
Solution for problem 1
Create an abstract method AbstractCarSelectionBoxData createNewBoxData();
in AbstractCarSelectionBox and implement it in the corresponding subclasses returning the new form data, e.g. new MyFormDataContainingCarSelectionBox().getCarSelectionBox();.
Solution for problem 2
Before importing the form data, call setValueSet(false) on the field that triggered the execChangedValue, e.g. formData.getMySmartfield().setValueSet(false).
This will make sure that it's not imported into the form, thus no loop detection should occur.
I think the importFormFieldData on this.getParentField() triggers the execChangedValue on the smart field again.
Try to box updated fields in a own group box and import the data only in this group box or do de import manually on each field you want to update.
My model looks like
public class Template
{
Id
Title
List<Field> Fields
}
The “Field” Entity contains information like Name, Caption, Type (TextBox/Select/Radio), Options, and validation rules (Range, Required, string length).
The standard validation in MVC is based on DataAnnotations, but I wants to validate (Both client and Server Side) the form dynamically based on Field Metadata which is dynamic and configurable.
Is it possible? Any pointers?
PS. I searched for the similar questions, but not able to find a solid answer.
I had a similar situation, this is how I handled it:
Server Side
When the POST happened I iterated over all the Fields values and did the Validation based on the validation rules I had on my objects. Then you can simply add ModelErrors to the Field object.
Since you push a Template object to the View you can access the Fields by name Fields[x].SomeProperty. Make sure you have a ValidationMessageFor for SomeProperty
ModelState.AddModelError("Fields[x].SomeProperty", "The Error Message you want to show.);
Client side
Make sure your form has an Id so you can access the Validate method().
Then you iterate over all the fields and just add the validation as you please.
For all the validations rules check the validation Jquery documentation.
$('#frmYourForm').validate();
for (var i = 0; i < 'CountOfAllFields'; i++)
{
$('#Fields_' + i + '__Foo').rules('add', { required: true, messages: { required: 'The Foo field is required'} });
$('#Fields_' + i + '__Bar').rules('add', { required: true, messages: { required: 'The Bar field is required'} });
}
I hope I helped you on your way !
Ps, use FireBug to help you find the correct names of the properties and that's how you can link them with the ModelErrors in the modelstate etc.
I am somewhat new to MVC 3 so this could be something simple. I have a Compact SQL 4 database. There is a Post object that has a Contents (string), Subject (string), When (DateTime) and Reposted (DateTime nullable) fields. During the Create's Post method, I manually force the When property to be DateTime.Now and it works great.
However, on my Edit View, I do NOT want to show this field or at least not let it be an editable field. When I do NOT put it as an editable field on the Edit View and they change say the Contents and then click Save I get the message:
An overflow occurred while converting to datetime.
During the Edit Postback, I am setting the Reposted field to DateTime.Now so it has to be the When field. When I debug on that postback I see that the "Post" object is only filled in with values of the fields I am displaying on the screen. So I decided to just grab the When field out with a LINQ query directly from the data but when I get to the SaveChanges it fails and says the following:
An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key
So how can I update an object and let the user only change a few pertinant fields? Seems like the Post object is not getting ALL of the existing values since they are not showing on the screen. How can I fill that object completely?
Edit: Here is the Post method.
[HttpPost]
public ActionResult Edit(Post post) {
if (ModelState.IsValid) {
db.Posts.Attach(post);
int Successes = 0;
int Failures = 0;
this.SendEMail(post, out Successes, out Failures);
post.Successes = Successes;
post.Failures = Failures;
post.Reposted = DateTime.Now;
db.ObjectStateManager.ChangeObjectState(post, EntityState.Modified);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(post);
}
Include a hidden field in your view for the fields that you do not want to display e.g.
#Html.HiddenFor(model => model.When)
In my model I have the following property:
[DataType(DataType.Currency)]
public decimal? Budget { get; set; }
When the user enters in $1,200.34, I need that value to be valid and strip out the currency symbol and comma.
In my controller I'm doing:
if (race.Budget != null)
{
race.Budget.ToString().Replace("$", "").Replace(",", "");
}
The problem is that client validation doesn't pass the value for budget into the controller. I get a value of null. How can I override the client validation so that I can strip out the currency symbol and comma?
Thank you in advance for the help.
UPDATE
So here's the strange thing. Let's say I want to bypass client validation all together. I added #{ Html.EnableClientValidation(false); } to my view and it's still sending a null value for Budget when I submit to the controller.
This isn't a client side validation problem. Your model has a field of type decimal? The model binder will try to bind a value of $123,456.78 into that and fail, so the value will be null. Here's one way to get around this:
Change your model to have a string property that masks your decimal:
public decimal? Budget { get; set; }
public string BudgetText {
get {
return Budget.HasValue ? Budget.ToString("$") : string.Empty;
}
set {
// parse "value" and try to put it into Budget
}
}
Then, just bind to BudgetText from your View. Validate it as a string with a regular expression that accepts only money input. It'll probably be the same regex you can use for your BudgetText's set method
So you can probably hook in some JQuery to pre-process the form field to strip the characters off you don't want (prior to form submission to the server). This is probably the quickest, dirtiest approach.
For something reusable, have a look into custom client validation adapters. The links aren't spot on, but should get you in the right direction. For Brad's screencast, I believe the relevant parts are fairly early on.
Check out the support for jQuery localization
cliente validation using jQuery validate for currency fields
also there is a plugin for currency validation as well
http://code.google.com/p/jquery-formatcurrency/
check out this recent post as well for a $ in binding
.NET MVC 3 Custom Decimal? Model Binder