0

さまざまな入力があるフォームがあります。いくつかの選択肢があるオプションのパラメーターがたくさんあります。ユーザーがこれらのオプションのパラメーターを次の方法で選択できるようにしたいと思います。

まず、ユーザーがフォームの下部にある [コンポーネントの追加] ボタンをクリックすると、ボタンの上に 2 つの新しいドロップダウンが表示されます。最初のドロップダウンには、選択できるタイプのリストがあり、2 番目のドロップダウンは無効になります。ユーザーが最初のドロップダウンで有効な選択肢を選択すると、指定されたタイプに固有の値を 2 番目のドロップダウンに入力したいと考えています。ユーザーは、必要なオプションのコンポーネントがすべて追加されるまで、新しいコンポーネント (ドロップダウンのペア) を追加し続けることができます。理想的には、すべてのフィールドが入力され、必要なコンポーネントが追加されるまで、フォームは投稿されません。

私の質問は次のとおりです。 フォームが送信されてエラーが発生したときに、動的に追加されたフィールド (コンポーネント) がページに残り、正しい値を表示するように設計するにはどうすればよいですか?

[コンポーネントの追加] ボタンを、部分ビューを取得する Ajax.ActionLink にすることを計画していました。

<div id="divComponentHolder"></div>
<%= Ajax.ActionLink("Add a Component", "GetComponentSelector", new AjaxOptions { UpdateTargetId = "divComponentHolder", InsertionMode = InsertionMode.InsertAfter}) %>

この部分的なビューは次のようになります。

   <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<MVCAndWebFormsTest.Models.ComponentSelectorModel>" %>
   <%= Html.Encode("Type:")%>
   <%= Html.DropDownList("ComponentType", Model.ComponentTypes, "", new {onchange = "updateCompValues(this);"}) %>
   <%= Html.Encode("File/Folder:")%>
   <div id="selectdiv">
       <% Html.RenderPartial("ComponentValueSelector", Model.ComponentValues); %>
   </div> 
   <br/>
   <script type="text/javascript" language="javascript">
        function updateCompValues(obj) {
            $.ajax({
                url: <% Url.Action("GetCompValues") %>,
                async: true,
                type: 'POST',
                data: { type: obj.value },
                dataType: 'text',
                success: function(data) { $("#selectdiv").html(data); },
                error: function() {
                    console.log('Erreur');
                }
            });
        }
   </script>

ComponentValueSelector パーシャルは非常に単純です。

    <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<MVCAndWebFormsTest.Controllers.ViewModels.ComponentValueModel>" %>
    <%= Html.DropDownList("CompValue", Model.SelectList) %>
4

2 に答える 2

2

MVC の提出リストを見てみましょう。ここにいくつかの便利なサイトがあります。

これは、構築中の動的 DOM を送信するのに役立ちます。

部分ビューをレンダリングするために ajax 呼び出しを行う代わりに、jquery を使用して常に要素を DOM に直接追加する別の方法があります。たとえば、リスト ボックスをコピーする jquery clone ( $('element').clone(); ) メソッドを使用してから、正規表現を実行して入力ボックスの ID を変更し、一意の ID/名前を持つようにします。

これらの「選択肢」のリストをコントローラーに渡しているときは、それらをモデルに戻し、ビューでそれらを繰り返し処理して、追加された正しい量の選択肢を表示する必要があります。

これは基本的な例です。これはあなた自身にとって最良の実装ではないかもしれませんし、他の誰かがより良いアイデアを持っているかもしれません.

意見

<% for (int i = 0; i < in Model.Results.Count; i++) { %>
    //render better HTML but you should get the point!
    <%= Html.Hidden("choices[" + i + "].ID", i) %>
    <%= Html.DropDownList("choices[" + i + "].Choice1", ...) %>
    <%= Html.DropDownList("choices[" + i + "].Choice2", ...) %>
<% } %>

- add button

jQuery

$('#addButton').click(function()
{
    //say if your choice drop downs were in a table then take the last
    //row and clone it
    var row = $('table tr:last').clone(true);
    var newId = //work out the new id from how many rows in the table

    //make sure to update the id and name parameters of inputs 
    //of the cloned row
    row.find(':input')
    .attr('id', function()
    {
       return $(this).attr('id').replace(/\[[\d+]\]/g, '[' + newlId + ']');
       //this replaces the cloned [id] with a new id
    })
    .attr('name', function()
    {
       return $(this).attr('name').replace(/\[[\d+]\]/g, '[' + newId + ']');
    });

    row.find(':hidden').val(newId); //update the value of the hidden input

    //alert(row.html()); //debug to check your cloned html is correct!
    //TODO: setup ajax call for 1st drop down list to render 2nd drop down

    $('table tr:last').after(row);//add the row

    return false;
});

コントローラ

public ActionResult YourMethod(IList<YourObject> choices, any other parameters)
{
   YourViewModel model = new YourViewModel();
   model.Results = choices; //where Results is IList<YourObject>

   return View(model);
}
于 2010-02-01T20:19:22.247 に答える
0

David Liddle からのアドバイスに基づいて、もう少しエレガントな別のデザインを見つけました。より多くの jQuery を使用し、部分ビューと Ajax リクエストを減らします。

たくさんの DropDownList を追加する代わりに、テーブル、ドロップダウンのペア、および [追加] ボタンを使用することにしました。ユーザーが最初のドロップダウンで [タイプ] オプションを選択すると、2 番目の [値] ドロップダウンを設定するための部分ビューを取得するために、引き続き ajax が使用されます。値オプションを選択したら、ユーザーは [追加] ボタンをクリックします。

jQuery を使用して、2 つの非表示の入力がページに追加されます。David からのリンクの命名規則は、これらの要素 (comps[0].Type および comps[0].Value) の名前付けに使用されます。また、追加された内容をユーザーに視覚的にフィードバックするために、同じタイプと値を持つ新しい行がテーブルに追加されます。

また、Type プロパティと Value プロパティだけを持つ Component クラスを定義し、モデルに List を追加しました。ビューで、このリストを繰り返し処理し、モデル内のすべてのコンポーネントをテーブルに非表示の入力として追加します。

インデックスビュー

<table id="componentTable">
    <tr>
        <th>Type</th>
        <th>Deploy From</th>
    </tr>
    <% foreach (Component comp in Model.comps) { %>
        <tr>
            <td><%= Html.Encode(comp.Type) %></td>
            <td><%= Html.Encode(comp.Value) %></td>
        </tr> 
    <% } %>
</table>

<div id="hiddenComponentFields">
<% var index = 0;%>
<% foreach (Component comp in Model.comps) { %>
    <input type="hidden" name="comps[<%= Html.Encode(index) %>].Type" value="<%= Html.Encode(comp.Type) %>" />
    <input type="hidden" name="comps[<%= Html.Encode(index) %>].Value" value="<%= Html.Encode(comp.value) %>" />
    <% index++; %>
<% } %>
</div>

<%= Html.DropDownList("ComponentTypeDropDown", Model.ComponentTypes, "", new { onchange = "updateCompValues();"}) %>
<span id="CompValueContainer">
    <% Html.RenderPartial("ComponentValueSelector", new ComponentValueModel()); %>
</span>

<span class="button" id="addComponentButton" onclick="AddComponentButtonClicked()">Add the File</span>

<span id="componentStatus"></span>

ComponentValueSelector PartialView

<%@ Control Language="C#" Inherits="ViewUserControl<ComponentValueModel>" %>

<% if(Model.SelectList == null) { %>
    <select id="CompValue" name="CompValue" disabled="true">
        <option></option>
    </select>
<% } else { %>
    <%= Html.DropDownList("CompValue", Model.SelectList, "") %>
<% } %>

jQuery

function updateCompValues() {
    $.ajax({
        url: '<%= Url.Action("GetComponentValues") %>',
        async: true,
        type: 'POST',
        data: { type: $("#CompValue").value },
        dataType: 'text',
        success: function(data) { $("#CompValueContainer").html(data); enable($("#CompValue")) },
        error: function() {
            console.log('Erreur');
        }
    });
}

function AddComponentButtonClicked() {
    UpdateCompStatus("info", "Updating...");
    var type = $("#ComponentTypeDropDown").val();
    var value = $("#CompValue").val();
    if (type == "" || value == "") {  // No values selected
        UpdateCompStatus("warning", "* Please select both a type and a value");
        return;  // Don't add the component
    }
    AddComponent(type, value);
}

function AddComponent(type, setting_1) {
    // Add hidden fields
    var newIndex = GetLastCompsIndex() + 1;
    var toAdd = '<input type="hidden" name="comps[' + newIndex + '].Type" value="' + type + '" />' +
                '<input type="hidden" name="comps[' + newIndex + '].Setting_1" value="' + setting_1 + '" />';
    $("#hiddenComponentFields").append(toAdd);

    // Add to page
    // Note: there will always be one row of headers so the selector should always work.
    $('#componentTable tr:last').after('<tr><td>'+type+'</td><td>'+setting_1+'</td>remove</tr>');
}

function GetLastCompsIndex() {
    // TODO
    alert("GetLastCompsIndex unimplemented!");
    // haven't figured this out yet but something like 
    // $("#hiddenComponentFields input:hidden" :last).useRegExToExtractIndexFromName(); :)
}

function UpdateCompStatus(level, message) {
    var statusSpan = $("#componentStatus");
    // TODO Change the class to reflect level (warning, info, error?, success?)
    // statusSpan.addClassName(...)
    statusSpan.html(message);
}

コントローラ

public ActionResult Index() { 
    SelectList compTypes = repos.GetAllComponentTypesAsSelectList();
    return View(new IndexViewModel(compTypes));
}

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index(Component[] comps, other params...) {
    foreach(Component comp in comps) {
        // Do something with comp.Type and comp.Value
    }
    return RedirectToAction(...);
}

public ActionResult GetComponentValues(string type) {
    ComponentValueModel valueModel = new ComponentValueModel();
    valueModel.SelectList = repos.GetAllComponentValuesForTypeAsSelectList(type);
    return PartialView("ComponentValueSelector", valueModel);
}

IndexViewModel

public class IndexViewModel {
    public List<Component> comps { get; set; }
    public SelectList ComponentTypes { get; set; }

    public IndexViewModel(SelectList types) {
        comps = new List<Component>();
        ComponentTypes = types;
    }
}
于 2010-02-02T18:23:03.687 に答える