This project is read-only.

DataGrid [Required] attribute prevents postback on cancelled adds

Dec 29, 2011 at 3:54 AM

Hi There,

Imagine I have a ViewModel object which has properties with the [Required] attribute. In certain scenarios I can't post the data back to the server. 

For example say I have a Contact Grid which has an Address Line 1 and a City both with the [Required] attribute.

I click add to create a new line and enter the AddressLine1. I then realise that I don't need the new address after all and so I click the cancel button. This hides the new line from me, but in the background it still has AddressLine1 set but not city.

So when I try to Save the page it does nothing. It doesn't show the validation because the line is hidden and so to the user it appears the page is broken.

If I then comment out the //[Required] attribute and redo the above scenario then it all works fine.

Am I doing something wrong? Is there some way to turn off validation for items which have been 'Deleted' or 'Cancelled'?

Cheers

Coordinator
Dec 29, 2011 at 12:47 PM
Edited Dec 29, 2011 at 12:49 PM

for sure there is something wrong in what you do. The grid in then example that comes with the binary distribution on codeplex has required fields, and works properly! Namely suppose;

  1. you add a row
  2. Write something
  3.  delete all text in all fields
  4. press the cancel button

After step 4 the row disappears and is substituded by the row that contains the add button....now if you hit the register button (after having filled all other required fileds in the form..) the form submits!

Behind the scene...when you perform an undo of an editing the row that was in edit mode, is completely detached from the DOM not simply hidden! 

Hiding something doesn't prevent validation, but detaching it from the DOM prevent validation.

 

As a conclusion, there must be something else wrong in what you do....pls show some code.

 

Are you using the right data buttons (you need the button that undoes the editing to make the insert row disappear, that ius the "cancel" button)?

Dec 29, 2011 at 10:09 PM
Edited Dec 29, 2011 at 10:10 PM

Hi There,

Thanks for the response. It appears the critical step is item 3 below. I.e. if you don't delete all text out of all textboxes then it doesn't seem to detach it from the DOM.
Does this sound correct to you?
If not I'll send you some code so you can take a look.
If it is correct then it's not very intuitive for the user, all they see is a cancel button, they don't realise that they have to delete all data first.
How do you normally handle this. Do you add an event to the cancel button click to check that all fields have been cleared, and if not, clear them?
Cheers
Craig
Coordinator
Dec 29, 2011 at 10:31 PM

no, no I don't care if fields are cleared or not...I just detach the row from the DOM...ALWAYS. I said clear everything just to be sure that the required validation is triggered, but the row can be also partially filled...it doesn't matter. Try yourself the example here: BinariesWithSimpleExamples. Each row has various required fields...experiment with it. Start the project WITHOUT debugging otherwie...due to the big amount of small in-line js script to be loaded by the debugger everything  is very slow.

 

Maybe, your problem is due to some name conflict...namely the fields in the row have the same names as other fields in the same form (full Html name), so also when the row is detached the omonimous fields cause the same validation to be triggered....Maybe...anyway I advice to run the example and then to compare the grid that is here with your grid...do this...and if you are not able to find the error, please send me some code.

Sincerely, I don't think there might be a so BIG bug...because there are thousands of installation working, so a  trivial bug would have already been appeared. However, maybe there is some strange combination of events that ...someway tilts the validation system...or maybe there is some previous javascript error that prevents the corrects execution of the js code that detaches the row...try to disable  event handlers you might have attached to the rows....or other js code you added to the grid someway.

Dec 30, 2011 at 1:50 AM
Hi There,

Once again thanks for the response and forcing me to take a closer look at this.

I've found the problem.

In my DisplayItemEditDelete template I had some HiddenFields which held the ContactId and AddressId. I didn't have any ValidationFor fields in there though so I wouldn't have expected it to manifest itself in that way but that is clearly the problem. If I remove these hidden fields it works as it should.

Thanks again for your help, I now have a much greater understanding of how these grids work.

Hope you have a Happy New Year.

Cheers

Craig

Mar 22, 2012 at 5:54 AM

Following along this same topic I have been having trouble deleting items from datagrid. The actual deletion would work but on the server the .Value item was not null (although most of it's properties were null).

I finally tracked down the issue. One of the controls on the line I was attempting to delete has this Html behind it.

<input name="CorporateActionAllocations.$$1.$.Item.Value.IsDRP" disabled="disabled" id="CorporateActionAllocations___1___Item_Value_IsDRP" type="checkbox" readOnly="readonly" data-val-required="The Is DRP field is required." data-val="true" isDRP="True" data-prefix="CorporateActionAllocations___1___Item_Value" value="true" /><input name="CorporateActionAllocations.$$1.$.Item.Value.IsDRP" type="hidden" value="false" /><label id="IsDRP_YN">No</label>

Replacing that with the following fixed the problem
<input disabled="disabled" class="check-box" type="checkbox" /><label id="IsDRP_YN">No</label>

I believe the problem is that it didn't remove the input <input name="CorporateActionAllocations.$$1.$.Item.V... so I manually blasted it using the following javascript. Pretty heavy handed I know but we don't allow the option of cancelling a delete so it works fine.

 

if (data.ChangeType == "ItemDeleting") {
        UnbindLineHandlers();
        $(GetLineItem($('input[isDRP]', data.ItemChanged), "IsDRP")).parent().remove();
    }
Coordinator
Mar 22, 2012 at 9:52 AM

very strange....

The point is that I remove the whole row...so if the input is inside the row it should be removed too. The only reason I might avoid to remove it is because for some reason it has been rendered OUTSIDE the row. Maybe you "move around" someway the "stuffs" that are within the row...so when the row is removed the inout...that is no more a "descendant in the DOM" of the row is not removed....however since information to the server are sent based on the NAMES ( CorporateActionAllocations.$$1.$.Item.Value.IsDRP ) of the input fields ...the information is sent to the server as it was in the row...

Now the name of CorporateActionAllocations.$$1.$.Item.Value.IsDRP is a name of an item within the grid .....so if you gave it this name   and the item is OUTSIDE the row you have just two possibilities:

1) Rename it if you don't WANT it to be a part of the row (in which case the name given was just ...an error)

2) If you consider that input TO BE PART OF THE ROW ALSO ifit is physically not a DOM descendant of the row container...then you have to take care to remove it manually on the row deleted event...(as you have done...). HOWEVER, consider rows can be also undeleted...and all changes in the grig might be UNDONE...so in this case you should be able to put it in place

 

Please let me know if the above might explain what happened to you...otherwise give me more information....I CAN'T ALLOW that fields are not removed in my grid :) so I need to understand why this happens. 

Mar 22, 2012 at 11:20 PM
Edited Mar 22, 2012 at 11:21 PM

Hi There,

I have created a very simple test project which proves this issue. I have emailed it to you, let me know if there are any problems.

For everyone elses benefit the issue seems is that when you delete an item from the grid the .Value property of the Tracker object is not returned as null.

HTML FOR LINE THAT DOES NOT WORK: 

<td><ul><li>
  <input name="CorporateActionAllocations.$$1.$.Item.Value.IsDRP" disabled="disabled" id="CorporateActionAllocations___1___Item_Value_IsDRP" type="checkbox"CHECKED="checked" isDRP="True" data-val-required="The Is DRP field is required." data-val="true" data-prefix="CorporateActionAllocations___1___Item_Value"value="true" /><input name="CorporateActionAllocations.$$1.$.Item.Value.IsDRP" type="hidden" value="false" /><label id="IsDRP_YN">Yes</label>
</li></ul></td>

 

HTML FOR LINE THAT DOES WORK:

<td><ul><li> <input disabled="disabled" class="check-box" type="checkbox" CHECKED="checked" /><label id="IsDRP_YN">Yes</label></li></ul></td>

Coordinator
Mar 23, 2012 at 10:34 AM

I think I found the problem:

1) NO INPUT FIELD IS ALLOWED IN THE DISPLAY TEMPLATE...otherwise it will be submitted as it were in the edit template...

2) in the display template there is a check-box that is an input field BUT it has the disabled="disabled", THIS IS OK. When you use input fields for display only purpose they MUST ALWAYS HAVE  disabled="disabled" so they ARE NOT SUBMITTED and the readonly Html attribute so user can't change their values.

3) SO EVERYTHING :...seems ok...BUT....unluckly when one uses the checkbox helper it adds also an hidden field with the false value: <input name="CorporateActionAllocations.$$1.$.Item.Value.IsDRP" type="hidden" value="false" /> .THIS ONE HAS NO disabled attribute...and it is submitted(THIS IS THE CAUSE OF THE PROBLEM) ....also if the row is deleted because display rows are not removed...but just made hidden, while edit colums are removed to prevent they will submit their value.....Now that input filed...will cause also other problems...not only with deleted rows but also with the other rows ...since thei value of the display row is submitted ALWAYS ....this means you will get wrong values in the edited columns, since the value from the display row will interfere.

 

SOLUTION? 

Simple...I run through the code and I have seen that your helper uses the DisplayFor to display the boolean. Just modify the default display template for boolean using custom checkbox...without an associated hidden field...that hidden filed is useful just when the checkbox is used to ACTUALLY provide a value, not for display only purpose...I don't know if the template you used is the standared Mvc one or if you customized it....but in both cases that display template is WRONG

There is also a better way to display boolean values The _D helper allow to pass an arrow of image urls that it uses to display booleans or enumerations. This is shown in the grid example that comes with the binaries.It contains a boolean filed that is called "Important" that is displayed this way.

You can use the _D helper to define a smart way to display boolean with two images one for false and the other for true.

Mar 25, 2012 at 11:00 PM

Thank you very much for taking the time to look at this in detail. I understand the problem now.

I do need the checkbox on each of the display lines as my jquery code searches all lines (edit and display) for certain attributes. If these attributes exist and a header element is changed, then I need to recalculate the values. 

Unfortunately the standard MVC3 Checkbox automatically creates the Hidden field you identified. 

So to get round this I'm going to handcrank my DisplayYesNoFor template so it creates a checkbox which is disabled and readonly WITHOUT the hidden checkbox behind it. 

Thanks again for your help, I didn't feel comfortable removing the whole line so am pleased you got to the bottom of the issue.

Coordinator
Mar 26, 2012 at 12:18 PM

Instead, of changing your helper, since you helper uses the DispalyFor....I would change the Display template that Mvc uses for display boolean: It is WRONG.

For each data type the mvc engine provide standard way to display and edit them. You can invoke them respectively with  DisplayFor and EditFor. Now while the Edit for MUST have the hidden field because it is necessary to subtit succesfully the value to the server, ...the DisplayFor has the checkbox displayed and readonly...that is correct...but the Microsof developer...forgot about the Hidden field....

Thus redefine the Display template for the boolean and put it in the shared folder. See Brad Wilson full series of post  about display and edit templates: http://bradwilson.typepad.com/blog/2009/10/aspnet-mvc-2-templates-part-1-introduction.html

It explais how does they work and how to customize them.