سؤال

يبدو أنني تواجه مشكلة في ASP.NET MVC في ذلك، إذا كان لدي أكثر من نموذج على صفحة تستخدم نفس الاسم في كل واحد، ولكن كأنواع مختلفة (راديو / مخفي / إلخ)، إذن، عندما أول نموذج منشورات (اخترت زر الراديو "التاريخ" على سبيل المثال)، إذا تم إعادة تقديم النموذج (قل كجزء من صفحة النتائج)، يبدو لي أن هذه المشكلة التي تكون القيمة المخفية للبحث عنها على النماذج الأخرى يتم تغيير قيمة زر الراديو الأخير (في هذه الحالة، SearchTyPe.Name).

فيما يلي نموذج مثال لأغراض التخفيض.

<% Html.BeginForm("Search", "Search", FormMethod.Post); %>
  <%= Html.RadioButton("SearchType", SearchType.Date, true) %>
  <%= Html.RadioButton("SearchType", SearchType.Name) %>
  <input type="submit" name="submitForm" value="Submit" />
<% Html.EndForm(); %>

<% Html.BeginForm("Search", "Search", FormMethod.Post); %>
  <%= Html.Hidden("SearchType", SearchType.Colour) %>
  <input type="submit" name="submitForm" value="Submit" />
<% Html.EndForm(); %>

<% Html.BeginForm("Search", "Search", FormMethod.Post); %>
  <%= Html.Hidden("SearchType", SearchType.Reference) %>
  <input type="submit" name="submitForm" value="Submit" />
<% Html.EndForm(); %>

مصدر الصفحات الناتج (سيكون هذا جزءا من صفحة النتائج)

<form action="/Search/Search" method="post">
  <input type="radio" name="SearchType" value="Date" />
  <input type="radio" name="SearchType" value="Name" />
  <input type="submit" name="submitForm" value="Submit" />
</form>

<form action="/Search/Search" method="post">
  <input type="hidden" name="SearchType" value="Name" /> <!-- Should be Colour -->
  <input type="submit" name="submitForm" value="Submit" />
</form>

<form action="/Search/Search" method="post">
  <input type="hidden" name="SearchType" value="Name" /> <!-- Should be Reference -->
  <input type="submit" name="submitForm" value="Submit" />
</form>

من فضلك هل يمكن لأي شخص آخر مع RC1 تأكيد هذا؟

ربما يكون ذلك لأنني أستخدم المعين. لا أدري، لا أعرف. يجب أن أضيف أنه يمكنني التحايل على هذه المشكلة باستخدام علامات "دليل" الإدخال () في الحقول المخفية، ولكن إذا كنت أستخدم علامات MVC (<٪ = HTML.Hidden (...)٪>)، .NET MVC يحل محلها كل مرة.

تشكرات.

تحديث:

لقد رأيت هذا الخطأ مرة أخرى اليوم. يبدو أن هذه المحاصيل رأسها عند إرجاع الصفحة المنشورة واستخدام علامات النموذج المخفية MVC مع مساعد HTML. لقد اتصلت فيل هاك حول هذا، لأنني لا أعرف من أين أنتقل، ولا أعتقد أن هذا يجب أن يكون من المتوقع السلوك كما هو محدد من قبل ديفيد.

هل كانت مفيدة؟

المحلول

نعم، هذا السلوك حاليا بالتصميم. على الرغم من أنك تقوم بوضع القيم بشكل صريح، إذا عدت إلى نفس عنوان URL، فنحن ننظر في الحالة النموذجية واستخدام القيمة هناك. بشكل عام، يسمح لنا هذا بعرض القيمة التي قدمتها على عون البريد، بدلا من القيمة الأصلية.

هناك نوعان من الحلول الممكنة:

الحل 1.

استخدم أسماء فريدة لكل من الحقول. لاحظ أنه بشكل افتراضي نستخدم الاسم الذي تحدده كمعرف عنصر HTML. إنه HTML غير صالح أن يكون لديك عناصر متعددة لها نفس المعرف. لذلك استخدام الأسماء الفريدة هي ممارسة جيدة.

الحل 2

لا تستخدم المساعد المخفي. يبدو أنك حقا لا تحتاج إليها حقا. بدلا من ذلك، يمكنك القيام بذلك:

<input type="hidden" name="the-name" 
  value="<%= Html.AttributeEncode(Model.Value) %>" />

بالطبع، كما أفكر في هذا أكثر من ذلك، تغيير القيمة بناء على عملية إعادة النشر أمر منطقي لصناديق النصر، ولكنه أقل معنى أقل للمدخلات الخفية. لا يمكننا تغيير هذا ل V1.0، لكنني سأعتبرها V2. لكننا بحاجة إلى التفكير من خلال الآثار المترتبة على هذا التغيير بعناية.

نصائح أخرى

كما أن الآخرين كنت أتوقع استخدام النماذج لملء النموذج وكما نحن نستخدم النموذج بشكل صريح في التعبيرات في الرأي، فينبغي أن يستخدم النموذج وليس الطرازات.

إنه اختيار تصميم وأحصل على السبب: إذا فشلت التحقق من الصحة، فقد لا تكون قيمة الإدخال جديلة يمكن أن تكون جديلة للنمط في النموذج وما زلت ترغب في تقديم أي قيمة خاطئة يتم كتابتها المستخدم، لذلك من السهل تصحيحه.

الشيء الوحيد الذي لا أفهمه هو: لماذا لا يكون ذلك حسب التصميم الذي يتم استخدام النموذج، والذي يتم تعيينه بشكل صريح من قبل المطور وإذا حدث خطأ في التحقق من الصحة، فسيتم استخدام النماذج ModelState.

لقد رأيت الكثير من الناس يستخدمون الحلول مثل

  • modelstate.clear (): مسح جميع قيم الندمة، ولكن في الأساس تعطيل استخدام التحقق الافتراضي في MVC
  • ModelState.remove ("قضم"): نفس modelstate.clear () ولكن يحتاج إلى michansoman من مفاتيح النماذج، وهو الكثير من العمل ولا يشعر بالحق في ميزة ربط السيارات من MVC. يشعر وكأنه 20 سنة مرة أخرى عندما كنا ندخل أيضا نموذج ومفاتيح الاستعلام.
  • تقديم HTMLTHEM أنفسهم: الكثير من العمل والتفصيل والطرق بعيدا أساليب مساعد HTML مع الميزات الإضافية. مثال على ذلك: استبدال @ html.hiddenfor بواسطة m.name) "id =" @ html.idfor (m => m.name) "value =" @ html.attributeNcode (model.name) ">. أو استبدال html. DropDownListfor بواسطة ...
  • قم بإنشاء مساعدين HTML مخصص لاستبدال المساعدين MVC HTML الافتراضي لتجنب مشكلة التصميم حسب التصميم. هذا نهج أكثر عمومية، مما يجعل HTML الخاص بك، ولكن لا يزال يتطلب معرفة المزيد من HTML + MVC أو فك System.Web.MVC لا يزال بإمكانه حافظ على جميع الميزات الأخرى ولكن تعطيل الأسبقية النماذج فوق النموذج.
  • تطبيق نمط ما بعد إعادة التوجيه: هذا سهل في بعض البيئات، ولكن أصعب في تلك مع المزيد من التفاعل / التعقيد. هذا النمط له إيجابيات وسلبيات ولا يجب إجبارك على تطبيق هذا النمط بسبب اختيار التصميم من التصميم من النماذج فوق النموذج.

مشكلة

وبالتالي فإن المشكلة هي أن النموذج ممتلئ من النداوات، وفي العرض، حددنا صراحة لاستخدام النموذج. يتوقع الجميع استخدام القيمة النموذجية (في حالة تغييرها)، ما لم يكن هناك خطأ في التحقق من الصحة؛ ثم يمكن استخدام النماذج.

حاليا في Extensions Helper MVC، تحصل قيمة النماذج على الأسبقية على القيمة النموذجية.

المحلول

لذلك يجب أن يكون الإصلاح الفعلي لهذه المشكلة: لكل تعبير لسحب القيمة النموذجية، يجب إزالة قيمة النماذج إذا لم يكن هناك خطأ في التحقق من الصحة لهذه القيمة. إذا كان هناك خطأ في التحقق من الصحة لتحديد الإدخال، فلا يجب إزالة قيمة ModelState وسيتم استخدامها مثل طبيعية. أعتقد أن هذا يحل المشكلة بالضبط، وهو أفضل ثم معظم الحلول.

الرمز هنا:

    /// <summary>
    /// Removes the ModelState entry corresponding to the specified property on the model if no validation errors exist. 
    /// Call this when changing Model values on the server after a postback, 
    /// to prevent ModelState entries from taking precedence.
    /// </summary>
    public static void RemoveStateFor<TModel, TProperty>(this HtmlHelper helper,  
        Expression<Func<TModel, TProperty>> expression)
    {
        //First get the expected name value. This is equivalent to helper.NameFor(expression)
        string name = ExpressionHelper.GetExpressionText(expression);
        string fullHtmlFieldName = helper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);

        //Now check whether modelstate errors exist for this input control
        ModelState modelState;
        if (!helper.ViewData.ModelState.TryGetValue(fullHtmlFieldName, out modelState) ||
            modelState.Errors.Count == 0)
        {
            //Only remove ModelState value if no modelstate error exists,
            //so the ModelState will not be used over the Model
            helper.ViewData.ModelState.Remove(name);
        }
    }

ثم نقوم بإنشاء ملحقات مساعد HTML الخاصة بنا TODO هذا قبل استدعاء ملحقات MVC:

    public static MvcHtmlString TextBoxForModel<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
        Expression<Func<TModel, TProperty>> expression,
        string format = "",
        Dictionary<string, object> htmlAttributes = null)
    {
        RemoveStateFor(htmlHelper, expression);
        return htmlHelper.TextBoxFor(expression, format, htmlAttributes);
    }

    public static IHtmlString HiddenForModel<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
        Expression<Func<TModel, TProperty>> expression)
    {
        RemoveStateFor(htmlHelper, expression);
        return htmlHelper.HiddenFor(expression);
    }

يزيل هذا الحل المشكلة، ولكن لا يتطلب منك فك ضغط وتحليل وإعادة البناء كل ما تقدمه MVC عادة (لا تنس أيضا إدارة التغييرات مع مرور الوقت، واختلافات المتصفح، إلخ).

أعتقد أن منطق "القيمة النموذجية، ما لم خطأ التحقق من الصحة ثم modelstate" يجب أن يكون حسب التصميم. إذا كان الأمر كذلك، فلن يعانون من الكثير من الناس، لكنهم لا يزالون مغطى بما أقصده MVC.

ركضت فقط في نفس القضية. يبدو أن HTML Helpers مثل TextBox () الأسبقية للقيم التي تم تمريرها تتصرف تماما عكس ما استنتجته من توثيق حيث تقول:

قيمة عنصر إدخال النص. إذا كانت هذه القيمة مرجعية فارغة (لا يوجد شيء في Visual Basic)، فسيتم استرداد قيمة العنصر من كائن ViewDatAdAdiCtary. إذا لم يكن هناك قيمة موجودة هناك، فسيتم استرداد القيمة من كائن ModelStateDuctionary.

بالنسبة لي، قرأت أن القيمة، إذا تم استخدامها. لكن قراءة مربع النص () المصدر:

string attemptedValue = (string)htmlHelper.GetModelStateValue(name, typeof(string));
tagBuilder.MergeAttribute("value", attemptedValue ?? ((useViewData) ? htmlHelper.EvalString(name) : valueParameter), isExplicitValue);

يبدو أن النظام الفعلي هو عكس ما هو موثق. يبدو أن النظام الفعلي هو:

  1. modelstate.
  2. عرض البيانات
  3. القيمة (مرت إلى TextBox () بواسطة المتصل)

رؤساء المتابعة - هذا الخطأ لا يزال موجودا في MVC 3. أنا أستخدم بناء جملة علامات الحلاقة (مثل هذا الأمر حقا)، لكنني واجهت نفس الخطأ مع حلقة foreach التي أنتجت نفس القيمة لخاصية كائن كل مرة.

سيكون هذا هو القمامة المتوقعة - MVC لا تستخدم ViewState أو غيرها من الخدع ظهرك لتمرير معلومات إضافية في النموذج، لذلك ليس لديها أي فكرة عن النموذج الذي قدمته (اسم النموذج ليس جزءا من البيانات المقدمة فقط، فقط قائمة اسم / أزواج القيمة).

عندما تعرض MVC النموذج مرة أخرى، فهو يبحث ببساطة لمعرفة ما إذا كانت القيمة المقدمة بنفس الاسم موجودة - مرة أخرى، لا يوجد لديه طريقة لمعرفة القيمة المسماة جاءت من، أو حتى نوع التحكم الذي كان عليه (سواء كنت استخدم الراديو أو النص أو المخفية، كل شيء فقط اسم = قيمة عند تقديمها عبر http).

foreach (var s in ModelState.Keys.ToList())
                if (s.StartsWith("detalleProductos"))
                    ModelState.Remove(s);

ModelState.Remove("TimeStamp");
ModelState.Remove("OtherOfendingHiddenFieldNamePostedToSamePage1");
ModelState.Remove("OtherOfendingHiddenFieldNamePostedToSamePage2");

return View(model);

مثال لإعادة إنتاج "مشكلة التصميم"، وعمل ممكن. لا يوجد حل محل لمدة 3 ساعات فقدت محاولة العثور على "الخطأ" على الرغم من ... لاحظ أن هذا "التصميم" لا يزال في ASP.NET MVC 2.0 RTM.

    [HttpPost]
    public ActionResult ProductEditSave(ProductModel product)
    {
        //Change product name from what was submitted by the form
        product.Name += " (user set)";

        //MVC Helpers are using, to find the value to render, these dictionnaries in this order: 
        //1) ModelState 2) ViewData 3) Value
        //This means MVC won't render values modified by this code, but the original values posted to this controller.
        //Here we simply don't want to render ModelState values.
        ModelState.Clear(); //Possible workaround which works. You loose binding errors information though...  => Instead you could replace HtmlHelpers by HTML input for the specific inputs you are modifying in this method.
        return View("ProductEditForm", product);
    }

إذا كان النموذج الخاص بك يحتوي في الأصل على هذا: <%= Html.HiddenFor( m => m.ProductId ) %>

إذا كانت القيمة الأصلية ل "الاسم" (عند تقديم النموذج) هو "دمية"، بعد تقديم النموذج الذي تتوقع أن ترى "Dummy (مجموعة المستخدم)" المقدمة. بدون ModelState.Clear() ستظل ترى "دمية" !!!!!!

الحل الصحيح:

<input type="hidden" name="Name" value="<%= Html.AttributeEncode(Model.Name) %>" />

أشعر أن هذا ليس تصميما جيدا على الإطلاق، حيث يحتاج كل مطور نموذج MVC إلى وضع ذلك في الاعتبار.

لا تزال هذه المشكلة موجودة في MVC 5، ومن الواضح أنها لا تعتبر خطأ على ما يرام.

نحن نجد ذلك، على الرغم من التصميم، هذا ليس هو السلوك المتوقع بالنسبة لنا. بدلا من ذلك، نريد دائما أن تعمل قيمة الحقل المخفي بشكل مشابه لأنواع أخرى من الحقول وعدم تعامل بشكل خاص، أو اسحب قيمتها من بعض المجموعة الغامضة (والتي تذكرنا بالعرض!).

عدد قليل من النتائج (القيمة الصحيحة بالنسبة لنا هي القيمة النموذجية، غير صحيحة هي قيمة النماذج):

  • Html.DisplayFor() يعرض القيمة الصحيحة (يسحب من النموذج)
  • Html.ValueFor لا (يسحب من modelstate)
  • ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData).Model يسحب القيمة الصحيحة

حلنا هو ببساطة تنفيذ امتدادنا الخاص:

        /// <summary>
        /// Custom HiddenFor that addresses the issues noted here:
        /// http://stackoverflow.com/questions/594600/possible-bug-in-asp-net-mvc-with-form-values-being-replaced
        /// We will only ever want values pulled from the model passed to the page instead of 
        /// pulling from modelstate.  
        /// Note, do not use 'ValueFor' in this method for these reasons.
        /// </summary>
        public static IHtmlString HiddenTheWayWeWantItFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
                                                    Expression<Func<TModel, TProperty>> expression,
                                                    object value = null,
                                                    bool withValidation = false)
        {
            if (value == null)
            {
                value = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData).Model;
            }

            return new HtmlString(String.Format("<input type='hidden' id='{0}' name='{1}' value='{2}' />",
                                    htmlHelper.IdFor(expression),
                                    htmlHelper.NameFor(expression),
                                    value));
        }

قد يكون هذا "حسب التصميم" لكنه ليس ما يتم توثيقه:

Public Shared Function Hidden(  

  ByVal htmlHelper As System.Web.Mvc.HtmlHelper,  
  ByVal name As String, ByVal value As Object)  
As String  

عضو في system.web.mvc.html.inputextensions.

ملخص: إرجاع علامة الإدخال المخفية.

حدود:
HTMLHELPER: مساعد HTML.
الاسم: اسم حقل النموذج و system.web.mvc.viewdatactionary المفتاح المستخدم للبحث عن القيمة.
القيمة: قيمة المدخلات المخفية. إذا كانت NULL، فنتظر إلى system.web.mvc.viewdataDickeary ثم system.web.mvc.modelstatealary لهذه القيمة.

يبدو أن هذا يقترح أنه فقط عندما تكون معلمة القيمة فارغة (أو غير محددة) ستظل نظرة HTMLHELPER في مكان آخر للحصول على قيمة.

في تطبيقي، لدي نموذج حيث: HTML.Hidden ("عن بعد"، صحيح) هو التقديم كما<input id="remote" name="remote" type="hidden" value="False" />

ملاحظة يتم تجاوز القيمة من خلال ما هو في قاموس ViewData.modelstate.

أم هل فاتني شيء؟

لذلك في MVC 4 "مشكلة التصميم" لا يزال هناك. إليك الرمز الذي اضطررت إلى استخدامه من أجل ضبط القيم المخفية الصحيحة في مجموعة نظرا لأنه بغض النظر عما أقوم به في وحدة التحكم، أظهرت طريقة العرض دائما قيما غير صحيحة.

الرمز القديم

for (int i = 0; i < Model.MyCollection.Count; i++)
{
    @Html.HiddenFor(m => Model.MyCollection[i].Name) //It doesn't work. Ignores what I changed in the controller
}

تحديث الرمز

for (int i = 0; i < Model.MyCollection.Count; i++)
{
    <input type="hidden" name="MyCollection[@(i)].Name" value="@Html.AttributeEncode(Model.MyCollection[i].Name)" /> // Takes the recent value changed in the controller!
}

هل ثبت أن هذا في MVC 5؟

هناك الحل البديل:

    public static class HtmlExtensions
    {
        private static readonly String hiddenFomat = @"<input id=""{0}"" type=""hidden"" value=""{1}"" name=""{2}"">";
        public static MvcHtmlString HiddenEx<T>(this HtmlHelper htmlHelper, string name, T[] values)
        {
            var builder = new StringBuilder(values.Length * 100);
            for (Int32 i = 0; i < values.Length; 
                builder.AppendFormat(hiddenFomat,
                                        htmlHelper.Id(name), 
                                        values[i++].ToString(), 
                                        htmlHelper.Name(name)));
            return MvcHtmlString.Create(builder.ToString());
        }
    }

كما اقترح الآخرون، ذهبت مع استخدام رمز HTML المباشر بدلا من استخدام HTMLHELPERS (TextBoxfor، أختيار خيار، HIDDENFOR، إلخ).

المشكلة رغم أن هذا النهج هو أنك بحاجة إلى وضع سمات الاسم والمعرف كسلاسل. أردت أن أبقي خصائص النموذج الخاصة بي بقوة، لذلك استخدمت NameFor و IDFOR HTMLHELPERS.

<input type="hidden" name="@Html.NameFor(m => m.Name)" id="@Html.IdFor(m=>m.Name)" value="@Html.AttributeEncode(Model.Name)">

تحديث:إليك تمديد HTMLHELPER مفيد

    public static MvcHtmlString MyHiddenFor<TModel, TValue>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TValue>> expression, object htmlAttributes = null)
    {
        return new MvcHtmlString(
            string.Format(
                @"<input id=""{0}"" type=""hidden"" value=""{1}"" name=""{2}"">",
                helper.IdFor(expression),
                helper.NameFor(expression),
                GetValueFor(helper, expression)
            ));
    }

    /// <summary>
    /// Retrieves value from expression
    /// </summary>
    private static string GetValueFor<TModel, TValue>(HtmlHelper<TModel> helper, Expression<Func<TModel, TValue>> expression)
    {
        object obj = expression.Compile().Invoke(helper.ViewData.Model);
        string val = string.Empty;
        if (obj != null)
            val = obj.ToString();
        return val;
    }

يمكنك بعد ذلك استخدامها مثل

@Html.MyHiddenFor(m => m.Name)
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top