gpt4 book ai didi

asp.net-mvc - Knockout.js:无法绑定(bind)到自定义 @Html.EditorFor

转载 作者:行者123 更新时间:2023-12-04 15:17:29 25 4
gpt4 key购买 nike

我正在使用选择器来构建自定义 @Html.EditorFor(称为 @Html.FullFieldEditor)。它确定要生成的输入类型(文本框、下拉菜单、单选按钮、复选框等)。我一直试图将它连接到一个单选按钮列表中,因此:

@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)
}
}

任何帮助是极大的赞赏。

编辑
我正在尝试最小化现有的 JS代码(超过 1500 行)用 KO 显示/隐藏(这似乎可以大大减少代码)。我知道我有选择( visibleif 等)与 KO,但假设我想用 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 的代码的一部分.为简洁起见,省略了某些部分(例如 RequiredForToolTipForSpacerFor 的代码)。
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);
}

更新 3
选项不起作用。选项 1 甚至不会显示 data-bind<input> .选项 2 将不起作用,因为它只是检查该字段是否是必需的(如果是,代码只显示“必需”图像)。

当我在您的 之前尝试您的第一个建议时“更新2” ( 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 合并 TagBuilder 的一部分,输出 field-input <div> ,这就是它被放置的地方(这是合乎逻辑的)。注意它应该是 data-bind但显示为 data_bindfield-input类(class)。这就是我拥有 FullFieldEditor 的方式:
@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>

我怀疑我必须得到 htmlAttributes进入 Selector.cshtml上面,而不是 HtmlFormHelper.cs文件。 Selector.cshtml是在显示例如下拉列表、单选按钮列表或复选框列表(以及其他)之间做出决定的原因。 Selector.cshtmlShared\EditorTemplates 中的模板文件夹。

背景:我有几十个表格,代表几十个页面(或向导)上的数百个输入。我正在使用 @Html.FullFieldEditor因为它比为每种类型的输入(下拉、复选框、单选按钮等)都有意大利面条代码更容易维护。

更新 4
还是行不通。

我在 Selector.cshtml 中试过这个(它的 BuildInput 函数)代码并能够将“数据绑定(bind)”放入 <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 岁以上的人开放赏金。

最佳答案

更新3

下划线位的第一个好电话,我完全忘记了这一点。您的代码看起来几乎正确,但实际上应该是这样的:

@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" } })

更新2

所以我更新了你的代码,我 数据绑定(bind)属于您的 html(标记为 option1 和 option2)。如果您给我一个正在生成的 html 的片段,以及您需要数据绑定(bind)的位置,将会有帮助。
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));

原答案

我有一种感觉,尝试混合 razor 和 knockout 会发生什么,是 razor 的东西会渲染,然后当附加了 knockout 时,knockout View 模型中的值将覆盖 razor View 中的任何内容。

如果您尝试重构 knockout ,这是我的建议:
  • 创建仅 html View (无 Razor )。
  • 将您的 knockout 绑定(bind)添加到它。
  • 使用 Json.NET 以 json 对象的形式将模型数据传递给 knockout .您可以通过多种方式执行此操作,但最简单的方法是将数据填充到 <script> 内部的 View 中。您的 javascript 可以找到的标记。
  • 使用Knockout Mapping将 json 对象加载到 View 模型中。
  • 关于asp.net-mvc - Knockout.js:无法绑定(bind)到自定义 @Html.EditorFor,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10234385/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com