对于有想法的人来说,有点奇怪......我在特定页面上渲染一个隐藏的布尔字段。但是,对于同一个字段,我会得到两个略有不同的标记,具体取决于特定事件是否在流程之前发生。生成的两个字段是;
<input id="HasPreviouslyIssuedCard" name="HasPreviouslyIssuedCard" type="hidden" value="false" />
和
<input id="HasPreviouslyIssuedCard" name="HasPreviouslyIssuedCard" type="hidden" value="False" />
问题是“值”属性中的文本的情况,您会注意到它是不同的,后来影响JS条件。产生这个的剃刀标记是;
@Html.Hidden("HasPreviouslyIssuedCard", Model.HasPreviouslyIssuedCard?.ToString(), new { id = nameof(Model.HasPreviouslyIssuedCard) })
但是,我也尝试使用以下变体,在渲染隐藏字段时具有相同的差异;
@Html.HiddenFor(m => m.HasPreviouslyIssuedCard)
我该怎么做才能获得这种差异?要获得大写变体,我在进入相关页面之前点击浏览器后退按钮。两种方法都以相同的方式加载数据,并以相同的方式将相同的值传递给渲染器。两种不同的输出。
请记住,这是一个呈现的布尔值类型字段。应该没有太大的修补空间。有很多方法可以解决这个问题,但由于我们在积压工作中有几个与布尔字段和后退按钮有关的项目,我想解释一下而不是解决它。
我最好的猜测是,按下后退按钮会以某种方式改变渲染器的状态,或者模型中的其他一些标志是不同的(因为它是70多个字段的向导)正在改变渲染器的方式解释如何设置布尔值。值相同,页面相同,数据以相同的方式读取。
基于此页面(Why does Boolean.ToString output "True" and not "true"),我们应假设始终获得大写变体,但这不是结果。
任何接受者/想法/想法?
编辑1
在HiddenFor()
方法中挖掘MVC的渲染逻辑,最终调用Convert.ToString(value, CultureInfo.CurrentCulture)
。直接调用时,我不能让它产生一个小写的布尔值,但它显然是这样做的。我当前的文化代码设置为en-IE
,但我直接调用它时会看到大写的布尔值。
编辑2
我已经对我的应用程序进行了更多的修补和跟踪,并且可以提供更详细的信息,但我还没有能够在更简单的应用程序中重现这一点
MVC 5应用程序:它有;
/
检索到的根网域GET
上的初始目标网页。布尔input
标记呈现为True
/ False
/Apply
检索到的网址GET
的向导中的第一页。布尔input
标记呈现为True
/ False
POST
检索。 input
代码的情况现在呈现为true
/ false
。input
标签现在以大写形式返回呈现(原始报告的问题)。我已经使用ILSpy深入研究MVC库以尝试扫描和MVC(如果我正确地读取代码)实际上使用IConverter
的实现来编写布尔值,不像我原先想的那样Convert.ToString(value, CultureInfo.CurrentCulture)
。
从HiddenFor()
的调用中追踪的代码堆栈(我认为);
System.Web.Mvc.InputExtentions.HiddenFor()
(公共职能)System.Web.Mvc.InputExtentions.HiddenHelper()
(私有函数,这里有一些关于数组的逻辑,但在我们的情况下并不适用)System.Web.Mvc.InputExtentions.InputHelper()
(私人功能,魔法发生在这里) System.Web.Mvc.InputExtentions.InputHelper()
;
private static MvcHtmlString InputHelper(HtmlHelper htmlHelper, InputType inputType, ModelMetadata metadata, string name, object value, bool useViewData, bool isChecked, bool setId, bool isExplicitValue, string format, IDictionary<string, object> htmlAttributes)
{
string fullHtmlFieldName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);
if (string.IsNullOrEmpty(fullHtmlFieldName))
{
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "name");
}
TagBuilder tagBuilder = new TagBuilder("input");
tagBuilder.MergeAttributes<string, object>(htmlAttributes);
tagBuilder.MergeAttribute("type", HtmlHelper.GetInputTypeString(inputType));
tagBuilder.MergeAttribute("name", fullHtmlFieldName, true);
string text = htmlHelper.FormatValue(value, format);
bool flag = false;
switch (inputType)
{
case InputType.CheckBox:
{
bool? flag2 = htmlHelper.GetModelStateValue(fullHtmlFieldName, typeof(bool)) as bool?;
if (flag2.HasValue)
{
isChecked = flag2.Value;
flag = true;
}
break;
}
case InputType.Hidden:
goto IL_131;
case InputType.Password:
if (value != null)
{
tagBuilder.MergeAttribute("value", text, isExplicitValue);
goto IL_16C;
}
goto IL_16C;
case InputType.Radio:
break;
default:
goto IL_131;
}
if (!flag)
{
string text2 = htmlHelper.GetModelStateValue(fullHtmlFieldName, typeof(string)) as string;
if (text2 != null)
{
isChecked = string.Equals(text2, text, StringComparison.Ordinal);
flag = true;
}
}
if (!flag && useViewData)
{
isChecked = htmlHelper.EvalBoolean(fullHtmlFieldName);
}
if (isChecked)
{
tagBuilder.MergeAttribute("checked", "checked");
}
tagBuilder.MergeAttribute("value", text, isExplicitValue);
goto IL_16C;
IL_131:
string text3 = (string)htmlHelper.GetModelStateValue(fullHtmlFieldName, typeof(string));
tagBuilder.MergeAttribute("value", text3 ?? (useViewData ? htmlHelper.EvalString(fullHtmlFieldName, format) : text), isExplicitValue);
IL_16C:
if (setId)
{
tagBuilder.GenerateId(fullHtmlFieldName);
}
ModelState modelState;
if (htmlHelper.ViewData.ModelState.TryGetValue(fullHtmlFieldName, out modelState) && modelState.Errors.Count > 0)
{
tagBuilder.AddCssClass(HtmlHelper.ValidationInputCssClassName);
}
tagBuilder.MergeAttributes<string, object>(htmlHelper.GetUnobtrusiveValidationAttributes(name, metadata));
if (inputType == InputType.CheckBox)
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.Append(tagBuilder.ToString(TagRenderMode.SelfClosing));
TagBuilder tagBuilder2 = new TagBuilder("input");
tagBuilder2.MergeAttribute("type", HtmlHelper.GetInputTypeString(InputType.Hidden));
tagBuilder2.MergeAttribute("name", fullHtmlFieldName);
tagBuilder2.MergeAttribute("value", "false");
stringBuilder.Append(tagBuilder2.ToString(TagRenderMode.SelfClosing));
return MvcHtmlString.Create(stringBuilder.ToString());
}
return tagBuilder.ToMvcHtmlString(TagRenderMode.SelfClosing);
}
编辑3
只是重述,因为有一些关于javascript方面的评论。我在尝试诊断问题时就考虑过这个问题。为了排除任何潜在的JS干扰/操纵,我使用Fiddler来捕获传输中的HTML。 MVC正在生成的HTML正在改变案例 - 我可以在Fiddler中看到这一点,在JS甚至加载之前的某个时刻,从不介意运行。这不是JS问题。
答案 0 :(得分:3)
似乎我错了,毕竟 JS,虽然不是我试图排除它的时候。重新产生这一事件的事件顺序;
form
onclick
事件中,有一段JS将布尔字段设置为true
(小写)在POST之后,布尔字段将具有小写值而不是大写。
发生了什么?好吧,如果HiddenFor()
中存在密钥,ViewData.ModelState
(及其变体)将呈现ToString()
的布尔值,而不是模型属性的ModelState
值采集。看起来很直观,但是可以(并且对我而言)抛出的是所有其他数据类型,模型绑定器非常具体 - ModelState
值和模型的属性值将是相同的。除了布尔值的情况 - 模型绑定器足够聪明,可以在转换POST时将True
和true
视为相同,但这会留下ModelState
值和字符串属性值如果它是通过JS布尔设置的那么重要。 JS会将ModelState
值设置为true
,而模型属性的ToString()
值则为True
。在JS中,"true" !== "True"
。
至于为什么在点击浏览器的后退按钮后重置为我 - 我们所拥有的陷阱页面没有表格值,并通过HTTP GET链接回应用程序,导致{{1调用模型属性HiddenFor
而不是将其从.ToString()
中拉出来,因为它不在那个时间点。同样地,在我的测试中,用户在通过JS设置之后很久就会处于向导中的某个位置,因此在它们继续通过向导时它将保持大写。
我的假设是JS在页面加载后开始使用。这实际上是由于JS在开始POST之前设置了一个值,并且小写值在页面生命周期中通过ModelState
持续存在。去图。
修改强>
要重现的代码;
模型;
ModelState
Razor标记;
public class Test
{
public bool Sample { get; set; }
}
控制器;
@model TestModelValueCase.Models.Test
@{
ViewBag.Title = "Test Page";
}
@using (Html.BeginForm())
{
@Html.HiddenFor(m => m.Sample)
<div>
<label>Hidden Value:</label>
<span id="_uiValue"></span>
</div>
<button type="submit">Try Post</button>
}
@section scripts
{
<script type="text/javascript" language="javascript">
$(document).ready(function() {
var source = $('#@nameof(Model.Sample)');
$('#_uiValue').html(source.val());
source.val(true);
});
</script>
}
答案 1 :(得分:0)
来自控制器的boolean comming的情况非常棘手,因为大部分时间被解释为字符串。
@Hidden 似乎就是这种情况。
对我来说,实现它的更简单方法是创建一个复选框并将显示设置为“none”
@html.CheckBoxFor(m=> m.HasPreviouslyIssuedCard, new { style ="display:none"}))
使用此解决方案,您将在两种语言(C#和JavaScript)中使用更多类型的变量。