1

I'm trying to build a dynamically generated view. My controller class for Create action looks like this

 public ActionResult Create()
 {
        List<FormMetadata> formItems = GetFormItems();

        return View(formItems);
 }

and the View so far is something like this

<% using (Html.BeginForm())
   {%>
<table>
    <% foreach (var item in Model)
       {
           if (!item.IsActive)
           {
               continue;
           }
    %>
    <tr>
        <td>
            <%=Html.Encode(item.DisplayValue)%>
        </td>
        <td>
            <% 
                if (item.FieldType == "TextBox")
                {%>
            <%=Html.TextBox(item.Field, null, new { tabindex = item.SortOrder })%>
            <%}
                if (item.FieldType == "CheckBox")
                {%>
            <%=Html.CheckBox(item.Field, false, new { tabindex = item.SortOrder })%>
            <%}

            %>
        </td>
        <td>
        </td>
    </tr>
    <%} %>
</table>

I want to show the same view with the values retained when there are validation errors. Code like the following is used to catch the validation errors

 if (string.IsNullOrEmpty(collection[item.ToString()]))
 {
         ModelState.AddModelError(key, "Required.");
 }

How can I show a view with validation errors while retaining the values which have been entered for this scenario?

4

3 回答 3

0

EDIT2

I have created a quick sample project which works. There is one thing I do not like and that is that I cannot pass the list itself around. I have to create the blank list every time and read all the values from the textboxes and save this in the list and give this updated list to the new view. Next round same thing. But it works.

Basically:

    public ActionResult About() {
        List<FormMetaData> formItems = GetFormItems();

        return View(formItems);
    }

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult About(FormCollection form)
    {
        List<FormMetaData> formItems = GetFormItems();
        //TryUpdateModel(formItems);


        // update project members       
        foreach (var key in form.AllKeys) {
            if (key.ToString().StartsWith("TextBox")) {
                string field = (key.ToString().Replace("TextBox", ""));
                if (!string.IsNullOrEmpty(form.Get(key.ToString()))) {
                    formItems.Find(delegate(FormMetaData t) { return t.Field == field; }).Value = form.Get(key.ToString());
                } 
                else { }
                   // this.ProjectRepository.DeleteMemberFromProject(id, userId);
            }
        }

        ModelState.AddModelError("test", "this is a test error");
        if(ModelState.IsValid)
        {
                ///
        }
        else
        {
            return View(formItems);
        }
        return View(formItems);
    }

    private List<FormMetaData> GetFormItems() {
            List<FormMetaData> output = new List<FormMetaData>();

            FormMetaData temp1 = new FormMetaData("TextBox",true,"temp1","displayText1");
            FormMetaData temp2 = new FormMetaData("TextBox", true, "temp2", "displayText2");
            output.Add(temp1);
            output.Add(temp2);

            return output;
        }

and then you have your view:

<% using (Html.BeginForm()) {%>
<table>
    <% foreach (var item in Model) {
           if (!item.isActive) {
               continue;
           }   %>
    <tr>
        <td>
            <%=Html.Encode(item.DisplayValue)%>
        </td>
        <td>
            <% if (item.FieldType == "TextBox") {%>
            <%=Html.TextBox("TextBox"+item.Field, item.Value)%>
            <%} if (item.FieldType == "CheckBox") {%>
            <%=Html.CheckBox("Value")%>
            <%}%>
        </td>
        <td>
        </td>
    </tr>
    <%} %>
    <p>
            <input type="submit" value="submit" />
        </p>
      <% } %>
</table>

I have uploaded a zipfile for you @ http://www.bastijn.nl/zooi/dynamicSample.rar

EDIT

I have tried this example and it goes wrong with the modelbinder. When I use "FormCollection form" as input to the POST create method the values of my textboxes are there under the key supplied. So you have to either your custom model binder or make a model which will work with the default model binder.

To be more specific. It goes wrong because in this case your textboxes are updating properties in objects inside the List, which is the Model passed. Normally your textboxes are updating properties inside the Object which is also your Model and the key used for the textbox (for automatic modelbinding) is the name of the property you update.

So I suppose the model binder does not bind the values in the textbox to your items in the list since it simply does not know how to do this automatically. It is 3.17 am here right now so I'm off to bed, the question is interesting and I might finish the answer tomorrow.

original

<%=Html.TextBox(item.Field, null, new { tabindex = item.SortOrder })%>

It seems you are generating your form every time with the values set to null.

Try to init them something like:

<%=Html.TextBox(item.Field, **item.Value**, new { tabindex = item.SortOrder })%>

And in your controller, when you check for ModelState.isValid do something like:

if(ModelState.isValid){
     //code when it works
}
else{
    return View(formItems)    // these should contain the just added values
}

That should do the trick.

So, in a simple example you get something like:

public ActionResult Create()
{
    List<FormMetadata> formItems = GetFormItems();

    return View(formItems);
}

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(List<FormMetadata> formItems)
{
    if(ModelState.isValid)
    {
        MyUpdate()
        save()
    }
    else
    {
        return view(formItems)
    }
}

And your view:

<% 
   if (item.FieldType == "TextBox")
   {%>
         <%=Html.TextBox(item.Field, **item.Value**, new { tabindex = item.SortOrder })%>
         <%}
            if (item.FieldType == "CheckBox")
            {%>
              <%=Html.CheckBox(item.Field, **item.Value**, new { tabindex = item.SortOrder })%>
            <%}
于 2009-11-24T22:32:18.080 回答
0

我现在使用以下方法,目前仅使用文本框

视图有

<%
for (int i = 0; i < Model.Count; i++)
{
    var name = "formItems[" + i + "].Field";


    var htmlAttributes = new Dictionary<string, object>
                             {
                                 {"tabindex", Model[i].SortOrder},
                                 {"class", Model[i].ClientSideValidation}
                             };


%>
    <div> <%=Html.Encode(Model[i].DisplayValue)%> 
    <%=Html.TextBox(name, Model[i].DefaultValue, htmlAttributes)%> 
    <%= Html.ValidationMessage(Model[i].Field) %>
    </div>

<% } %>

和控制器 Action 方法 GET

public ActionResult Create()
{
    List<FormMetadata> formItems = GetFormItems();

    HttpContext.Cache[FormCacheKey] = formItems;

    return View(formItems);
}

POST(部分代码)

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Create(List<FormMetadata> formItems)
    {
        var formDefinition = new List<FormMetadata>();

        try
        {
            if (HttpContext.Cache[FormCacheKey] != null)
            {
                formDefinition = HttpContext.Cache[FormCacheKey] as List<FormMetadata>;
            }
            else
            {
                formDefinition = GetFormItems();
                HttpContext.Cache[FormCacheKey] = formItems;
            }

            var formValues = new Dictionary<string, string>();

            for (int i = 0; i < formDefinition.Count; i++)
            {
                var key = formDefinition[i].Field;

                var value = formItems[i].Field ?? string.Empty;
于 2009-11-26T20:25:57.867 回答
0

我会先检查 NerdDinner,基本上使用相同的方法。

于 2009-11-24T23:58:24.487 回答