4

セレクターを使用して、カスタムの@ Html.EditorFor(@ Html.FullFieldEditorと呼ばれる)を作成しています。生成する入力のタイプ(テキストボックス、ドロップダウン、ラジオボタン、チェックボックスなど)を決定します。私はラジオボタンリストのためにそれを1つにフックしようとしていました、したがって:

@Html.FullFieldEditor(m => m.MyModel.MyRadioButton, new Dictionary<string, object> { { "data_bind", "myRadioButton" } })

またはこのように:

@Html.FullFieldEditor(m => m.MyModel.MyRadioButton, new { data_bind = "checked: myRadioButton" })

しかし、運がありません。

私は自分のselector.cshtmlコードを修正しようとしていましたが、ひどい混乱を引き起こしてしまいました。これが私が実装しようとする前に私のために働いたコードですknockout.js

@{
var supportsMany = typeof (IEnumerable).IsAssignableFrom(ViewData.ModelMetadata.ModelType);
var selectorModel = (Selector)ViewData.ModelMetadata.AdditionalValues["SelectorModelMetadata"];
var fieldName = ViewData.TemplateInfo.GetFullHtmlFieldName("");
var validationClass = ViewData.ModelState.IsValidField(fieldName) ? "" : "input-validation-error";


// Loop through the items and make sure they are Selected if the value has been posted
if(Model != null)
{
    foreach (var item in selectorModel.Items)
    {
        if (supportsMany)
        {
            var modelStateValue = GetModelStateValue<string[]>(Html, fieldName) ?? ((IEnumerable)Model).OfType<object>().Select(m => m.ToString());
            item.Selected = modelStateValue.Contains(item.Value);
        }
        else
        {
            var modelStateValue = GetModelStateValue<string>(Html, fieldName);
            if (modelStateValue != null)
            {
                item.Selected = modelStateValue.Equals(item.Value, StringComparison.OrdinalIgnoreCase);
            }
            else
            {
                Type modelType = Model.GetType();
                if (modelType.IsEnum)
                {
                    item.Selected = item.Value == Model.ToString();
                }
            }
        }
    }
}
}
@functions
{
    public MvcHtmlString BuildInput(string fieldName, 
    SelectListItem item, string inputType, object htmlAttributes) 
    // UPDATE: Trying to do it above
    {
    var id = ViewData.TemplateInfo.GetFullHtmlFieldId(item.Value);
    var wrapper = new TagBuilder("div");
    wrapper.AddCssClass("selector-item");

    var input = new TagBuilder("input");
    input.MergeAttribute("type", inputType);
    input.MergeAttribute("name", fieldName);
    input.MergeAttribute("value", item.Value);
    input.MergeAttribute("id", id);

    input.MergeAttributes(new RouteValueDictionary(htmlAttributes)); 
    // UPDATE: and trying above, but see below in the 
    // @foreach...@BuildInput section

    input.MergeAttributes(Html.GetUnobtrusiveValidationAttributes(fieldName, ViewData.ModelMetadata));

    if(item.Selected)
        input.MergeAttribute("checked", "checked");

    wrapper.InnerHtml += input.ToString(TagRenderMode.SelfClosing);


    var label = new TagBuilder("label");
    label.MergeAttribute("for", id);
    label.InnerHtml = item.Text;
    wrapper.InnerHtml += label;

    return new MvcHtmlString(wrapper.ToString());
}

/// <summary>
/// Get the raw value from model state
/// </summary>
public static T GetModelStateValue<T>(HtmlHelper helper, string key)
{
    ModelState modelState;
    if (helper.ViewData.ModelState.TryGetValue(key, out modelState) && modelState.Value != null)
        return (T)modelState.Value.ConvertTo(typeof(T), null);
    return default(T);
}
}
@if (ViewData.ModelMetadata.IsReadOnly)
{
var readonlyText = selectorModel.Items.Where(i => i.Selected).ToDelimitedString(i => i.Text);
if (string.IsNullOrWhiteSpace(readonlyText))
{
    readonlyText = selectorModel.OptionLabel ?? "Not Set";
}

@readonlyText

foreach (var item in selectorModel.Items.Where(i => i.Selected))
{
@Html.Hidden(fieldName, item.Value)
}
}
else
{
if (selectorModel.AllowMultipleSelection)
{
    if (selectorModel.Items.Count() < selectorModel.BulkSelectionThreshold)
    {
<div class="@validationClass">
    @foreach (var item in selectorModel.Items)
            {
        @BuildInput(fieldName, item, "checkbox") // throwing error here if I leave this as is (needs 4 arguments)
        //But if I do this:
        //@BuildInput(fieldName, item, "checkbox", htmlAttributes) // I get does not exit in current context

            }
</div>
    }
    else
    {
@Html.ListBox("", selectorModel.Items)
    }
}
else if (selectorModel.Items.Count() < selectorModel.BulkSelectionThreshold)
{
<div class="@validationClass">
    @*@if (selectorModel.OptionLabel != null)
        {
        @BuildInput(fieldName, new SelectListItem { Text = selectorModel.OptionLabel, Value = "" }, "radio")
        }*@
    @foreach (var item in selectorModel.Items)
        {
        @BuildInput(fieldName, item, "radio")//same here
        }
</div>
}
else
{
@Html.DropDownList("", selectorModel.Items, selectorModel.OptionLabel)
}
}

どんな助けでも大歓迎です。

編集 私はJSKOで表示/非表示にするために既存のコード(1500行以上)を最小化しようとしています(これはコードをかなり削減できるようです)。KOには選択肢(visibleifなど)があることはわかっていますが、KOで達成したいことが実行可能であると仮定すると、で実行できますvisible。いずれにせよ、拘束力のあるハードルを乗り越えることは、私がそこまで到達することを妨げます。

これは私がプレーンで表示/非表示にするために使用しているいくつかのコードの例ですJS

$(document).ready(function () {
    $("input[name$='MyModel.MyRadioButton']").click(function () {
        var radio_value = $(this).val();
        if (radio_value == '1') {
            $("#MyRadioButton_1").show();
            $("#MyRadioButton_2").hide();
            $("#MyRadioButton_3").hide();
        }
        else if (radio_value == '2') {
            $("#MyRadioButton_1").show();
            $("#MyRadioButton_2").hide();
            $("#MyRadioButton_3").hide();
        }
        else if (radio_value == '3') {
            $("#MyRadioButton_1").show();
            $("#MyRadioButton_2").hide();
            $("#MyRadioButton_3").hide();
        }
    });
    $("#MyRadioButton_1").hide();
    $("#MyRadioButton_2").hide();
    $("#MyRadioButton_3").hide();
});

KOは上記を最小限に抑えることができると思いました。繰り返しになりますが、私は20〜30の入力を調べていますが、そのほとんどは3つ以上の選択肢があります(ドロップダウンに10の選択肢があるものもあります)。これは1500回線で維持するのが難しくなり、成長しています。

そして、私のview中でこれが起こっています:

<div id="MyRadioButton_1">
    @Helpers.StartingCost(MyModel.Choice1, "1")
</div>
<div id="MyRadioButton_2">
    @Helpers.StartingCost(MyModel.Choice2, "2")
</div>
<div id="MyRadioButton_3">
    @Helpers.StartingCost(MyModel.Choice2, "2")
</div>

上記のviewコードはKOによってわずかに変更されますが、これもJS私が削減しようとしているものです。

編集2 これはのコードの一部ですFullFieldEditor。一部の部分は簡潔にするために省略されています(、、のコードなどRequiredFor)。ToolTipForSpacerFor

public static MvcHtmlString FullFieldEditor<T, TValue>(this HtmlHelper<T> html, Expression<Func<T, TValue>> expression)
    {
        return FullFieldEditor(html, expression, null);
    }

    public static MvcHtmlString FullFieldEditor<T, TValue>(this HtmlHelper<T> html, Expression<Func<T, TValue>> expression, object htmlAttributes)
    {
        var metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);

        if (!metadata.ShowForEdit)
        {
            return MvcHtmlString.Empty;
        }

        if (metadata.HideSurroundingHtml)
        {
            return html.EditorFor(expression);
        }

        var wrapper = new TagBuilder("div");
        wrapper.AddCssClass("field-wrapper");
        var table = new TagBuilder("table");
        table.Attributes["border"] = "0";
        table.Attributes["width"] = "100%";//added this to even out table columns
        var tbody = new TagBuilder("tbody");
        var tr = new TagBuilder("tr");
        var td1 = new TagBuilder("td");
        td1.Attributes["width"] = "40%";
        td1.Attributes["valign"] = "top";
        var label = new TagBuilder("div");
        label.AddCssClass("field-label");
        label.AddCssClass("mylabelstyle");
        label.InnerHtml += html.MyLabelFor(expression);
        td1.InnerHtml = label.ToString();
        var td2 = new TagBuilder("td");
        td2.Attributes["width"] = "50%";
        td2.Attributes["valign"] = "top";
        var input = new TagBuilder("div");
        input.AddCssClass("field-input");

        input.InnerHtml += html.EditorFor(expression);
        td2.InnerHtml = input.ToString();
        var td3 = new TagBuilder("td");
        td3.Attributes["width"] = "5%";
        td3.Attributes["valign"] = "top";
        if (metadata.IsRequired && !metadata.IsReadOnly)
        {
            td3.InnerHtml += html.RequiredFor(expression);
        }
        var td4 = new TagBuilder("td");
        td4.Attributes["width"] = "5%";
        td4.Attributes["valign"] = "middle";
        if (!string.IsNullOrEmpty(metadata.Description))
        {
            td4.InnerHtml += html.TooltipFor(expression);
        }
        else td4.InnerHtml += html.SpacerFor(expression);
        td4.InnerHtml += html.ValidationMessageFor(expression);
        tr.InnerHtml = td1.ToString() + td2.ToString() + td3.ToString() + td4.ToString();
        tbody.InnerHtml = tr.ToString();
        table.InnerHtml = tbody.ToString();
        wrapper.InnerHtml = table.ToString();
        return new MvcHtmlString(wrapper + Environment.NewLine);
    }

UPDATE3 オプションが機能していません。data-bindオプション1はに表示されません<input>。オプション2は、フィールドが必須かどうかをチェックするだけなので機能しません(必要な場合、コードは「必須」の画像を表示するだけです)。

「UPDATE2」( )の前に最初の提案を試したときinput.MergeAttributes(new RouteValueDictionary(htmlAttributes));、これは次の出力でした。

<div class="field-input" data_bind="checked: MyRadioButton">
    <div class="">
        <div class="selector-item">
            <input id="MyModel_MyRadioButton_Choice1" name="MyModel.MyRadioButton" type="radio" value="Choice1">
            <label for="MyModel_MyRadioButton_Choice1">I am thinking about Choice 1.</label>
        </div>
        <!--Just showing one radio button for brevity-->
    </div>
</div>

属性を、を出力しているのinput部分とマージしたので、それが配置されている場所です(これは論理的です)。あるべきですが、クラスのように表示されていることに注意してください。これは私が持っている方法です:TagBuilderfield-input <div>data-binddata_bindfield-inputFullFieldEditor

@Html.FullFieldEditor(m => m.MyModel.MyRadioButton, new Dictionary<string, object> { { "data_bind", "myRadioButton" } })

これがどのように表示されるか、私は思います:

<div class="field-input">
    <div class="">
        <div class="selector-item">
            <!-- "data-bind" should be showing up in the following INPUT, correct?-->
            <input data-bind="checked: MyRadioButton" id="MyModel_MyRadioButton_Choice1" name="MyModel.MyRadioButton" type="radio" value="Choice1">
            <label for="MyModel_MyRadioButton_Choice1">I am thinking about Choice 1.</label>
        </div>
        <!--Just showing one radio button for brevity-->
    </div>
</div>

私が疑うのは、ファイルではなくhtmlAttributesSelector.cshtml上記にそれを入れなければならないということです。HtmlFormHelper.csこれSelector.cshtmlは、たとえば、ドロップダウンリスト、ラジオボタンリスト、またはチェックボックスリスト(とりわけ)を表示するかどうかを決定するものです。はフォルダ内Selector.cshtmlのテンプレートです。Shared\EditorTemplates

背景:数十ページ(またはウィザード)にわたる数百の入力を表す数十のフォームがあります。@Html.FullFieldEditor入力の種類(ドロップダウン、チェックボックス、ラジオボタンなど)ごとにスパゲッティコードを使用するよりも保守が簡単だったため、を使用しています。

UPDATE4 まだ機能していません。

Selector.cshtml(その関数)コードでこれを試し、リスト内の各ラジオボタンBuildInputのタグに「データバインド」を取得することができました。<input>

input.MergeAttribute("data-bind", htmlAttributes);

次に、同じファイルでこれを下に移動しました。

@foreach (var item in selectorModel.Items)
    {
        @BuildInput(fieldName, item, "radio", "test")
    }

そして私のHTML出力はこれです:

<div class="selector-item">
    <input data-bind="test" id="MyModel_MyRadioButton_Choice1" name="MyModel.MyRadioButton" type="radio" value="Choice1">
    <label for="MyModel_MyRadioButton_Choice1">Choice 1</label>
</div>

Selector.cshtmlこれが、ファイルではなくファイルであると私に信じさせている理由HtmlFormHelper.csです。

私は50歳以上のすべての人に賞金を開放するつもりです。

4

2 に答える 2

4

UPDATE3

アンダースコアビットの最初の良い呼び出し、私はそれを完全に忘れました。あなたのコードはほぼ正しいように見えますが、実際には次のようになります。

@Html.FullFieldEditor(m => m.MyModel.MyRadioButton, new Dictionary<string, object> { { "data_bind", "checked:myRadioButton" } })

したがって、チェックボックスとテキストのどちらかを動的に選択しているので、いくつかのことを行う必要があります。チェックボックスの場合は、上記のコードを使用する必要があります。テキストボックスの場合は、次を使用する必要があります。

@Html.FullFieldEditor(m => m.MyModel.MyTextBox, new Dictionary<string, object> { { "data_bind", "value:MyTextBox" } })

UPDATE2

そこで、データバインドがHTMLに含まれていると思われるコードを更新しました(option1およびoption2とマークされています)。生成されるhtmlのスニペットと、データバインドが必要な場所を教えていただければ便利です。

public static MvcHtmlString FullFieldEditor<T, TValue>(this HtmlHelper<T> html, Expression<Func<T, TValue>> expression)
{
    return FullFieldEditor(html, expression, null);
}

public static MvcHtmlString FullFieldEditor<T, TValue>(this HtmlHelper<T> html, Expression<Func<T, TValue>> expression, object htmlAttributes)
{
    var metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);

    if (!metadata.ShowForEdit)
    {
        return MvcHtmlString.Empty;
    }

    if (metadata.HideSurroundingHtml)
    {
        return html.EditorFor(expression);
    }

    var wrapper = new TagBuilder("div");
    wrapper.AddCssClass("field-wrapper");

    var table = new TagBuilder("table");
    table.Attributes["border"] = "0";

    //added this to even out table columns
    table.Attributes["width"] = "100%";

    var tbody = new TagBuilder("tbody");

    var td1 = new TagBuilder("td");
    td1.Attributes["width"] = "40%";
    td1.Attributes["valign"] = "top";

    var label = new TagBuilder("div");
    label.AddCssClass("field-label");
    label.AddCssClass("mylabelstyle");
    label.InnerHtml += html.MyLabelFor(expression);

    td1.InnerHtml = label.ToString();

    var td2 = new TagBuilder("td");
    td2.Attributes["width"] = "50%";
    td2.Attributes["valign"] = "top";

    var input = new TagBuilder("div");
    input.AddCssClass("field-input");

    // option1
    input.InnerHtml += html.EditorFor(expression, htmlAttributes);
    td2.InnerHtml = input.ToString();

    var td3 = new TagBuilder("td");
    td3.Attributes["width"] = "5%";
    td3.Attributes["valign"] = "top";

    if (metadata.IsRequired && !metadata.IsReadOnly)
    {
        // option2
        td3.InnerHtml += html.RequiredFor(expression, htmlAttributes);
    }

    var td4 = new TagBuilder("td");
    td4.Attributes["width"] = "5%";
    td4.Attributes["valign"] = "middle";

    if (!string.IsNullOrEmpty(metadata.Description))
    {
        td4.InnerHtml += html.TooltipFor(expression);
    }
    else
    {
        td4.InnerHtml += html.SpacerFor(expression);
    }

    td4.InnerHtml += html.ValidationMessageFor(expression);

    var tr = new TagBuilder("tr");
    tr.InnerHtml = td1.ToString() + td2.ToString() + td3.ToString() + td4.ToString();

    tbody.InnerHtml = tr.ToString();
    table.InnerHtml = tbody.ToString();
    wrapper.InnerHtml = table.ToString();

    return new MvcHtmlString(wrapper + Environment.NewLine);
}

アップデート

以下が「正しい」答えであると私はまだ信じていますが、カスタム属性を渡すことができるように、タグビルダーに欠けているものは次のとおりです。

input.MergeAttributes(new RouteValueDictionary(htmlAttributes));

元の回答

かみそりとノックアウトを混ぜようとすると、かみそりのものがレンダリングされ、ノックアウトがアタッチされると、ノックアウトビューモデルの値がかみそりビューの値を上書きするように感じます。

ノックアウトにリファクタリングしようとしている場合の私の提案は次のとおりです。

  1. HTMLのみのビューを作成します(かみそりはありません)。
  2. ノックアウトバインディングを追加します。
  3. Json.NETを使用して、モデルデータをjsonオブジェクトの形式でノックアウトに渡します。これはさまざまな方法で実行できますが、最も簡単なのは<script>、JavaScriptが検出できるタグ内のビューにデータを詰め込むことです。
  4. ノックアウトマッピングを使用して、jsonオブジェクトをビューモデルにロードします。
于 2012-04-19T20:14:23.697 に答える
0

私はあなたの難しさを理解したかどうかわかりません。ただし、プロパティのレンダリングに使用した入力フィールドに応じてデータバインドをどこにどのように配置するかが難しい場合は、MvcControlsToolkitのClientBlocks機能を確認してください。。テンプレート(つまり、ページの一部を表示するかみそりヘルパーまたはPartialViews)またはフルビューを事前にコンパイルすることにより、バインディングのほとんどを自動的に計算します。つまり、@ Html.TextBoxFor(m => m.myProperty)と書くだけで、テキストボックスとクライアント側のmyPropertyの間に適切なデータバインドが作成されます。ViewModelなどがselect、radiosなどで作成されます。ヘルパーの使用を避け、ファイルに「適切な名前」を付けるだけで、バインディングが自動的に計算されるようにすることもできます。さらに、サーバー側のViewModelのどの部分をクライアントに転送するかを決定できます...クライアント側のViewmOdelは、投稿時にサーバー側のViewModel内に自動的に転送されます。

于 2012-05-01T11:14:40.637 に答える