Hi Radu,
I was banging my head against a wall for a few days on this too and was going to go down the route of removing the form tag, but have just got it working in a slightly less hacky way if you are still interested. My scenario was similar in that I have a form with a collection of elements to validate initially, but users can dynamically add new rows via ajax.
I'll break it down so hopefully it'll be easier to see what is going on. Looking at the MVC source code, the form and validation works roughly as so:
Html.BeginForm() outputs the opening form tag then creates and returns a new instance of MvcForm, which doesn't outwardly do much except make the scope of the form easier to manage for you.
It does however create a new FormContext and stores this within ViewContext.FormContext. It is this FormContext that tracks the client validation.
The last thing Html.BeginForm() does is set the FormId property of the new FormContext, using the id of the form tag. This is required so the client script can match up forms and validation rules.
Html.EndForm() disposes the MvcForm. This Dispose method outputs the form closing tag and then calls ViewContext.OutputClientValidation() which is resposible for outputting the javascript. Lastly it removes the current FormContext and sets it back to the parent FormContext or null if there isn't one.
So to not output the form tag we somehow need to take some of the FormContext management out of the MvcForm constructor/destructor.
So within my Partial View I did the following:
At the top I check if the ViewContext.FormContext has a value. If so we we are in the initial load so no need to mess around. If not, it is an ajax call, so I enable client validation, create a new MvcForm directly (not with BeginForm) - this causes a FormContext to be created - and set the FormContext.FormId to the same as my parent page
At the end of the view, I check if I have a form instance and if so, call ViewContext.OutputClientValidation() and reset the ViewContext.FormContext to null. I do not Dispose() the MvcForm as this would output the closing tag and MvcForm does not contain disposable objects.
The skeleton of the view looks as so:
<%
MvcForm dummyForm = null;
if (this.ViewContext.FormContext == null)
{
Html.EnableClientValidation();
dummyForm = new MvcForm(this.ViewContext);
this.ViewContext.FormContext.FormId = "mainform";
}
%>
// standard partial view markup goes here
<%
if (dummyForm != null)
{
this.ViewContext.OutputClientValidation();
this.ViewContext.FormContext = null;
}
%>
You could quite easily wrap this up into an extension method
Phil