Правильный способ привязки ракеты-носителя mvc3 к модели

У меня есть представление, которое содержит список радиообмена для моих условий и условий сайта.

например.

Yes
@Html.RadioButtonFor(model => model.TermsAndConditions, "True")
No
@Html.RadioButtonFor(model => model.TermsAndConditions, "False",
     new { Checked = "checked" })
</div>
@Html.ValidationStyledMessageFor(model => model.TermsAndConditions)

Все в порядке, если пользователь заполняет форму без каких-либо ошибок, однако, если я проведу проверку серверов и обновляю страницу, я теряю выбор, который пользователь сделал для радиообмена, а выбранное радио возвращается к ложному полю по умолчанию.

Как я должен связывать радиообъект, поэтому, если пользователь выбирает значение true, это значение сохраняется даже после проверки на стороне сервера?

Любые предложения были бы замечательными!

Ответ 1

Для короткого ответа вам нужно сделать три вещи:

  • Удалите new { Checked = "checked" } со второго переключателя. Это жестко запрограммированное значение будет отменять все магические действия.
  • Когда вы возвращаете свой ViewResult из действия контроллера, дайте ему экземпляр вашего модельного класса, где TermsAndConditions является ложным. Это предоставит ложное значение по умолчанию, которое вам нужно для того, чтобы для вас была выбрана ложная радиокнига.
  • Используйте true и false как значения для ваших радиокнопок вместо "True" и "False". Это связано с тем, что ваше свойство имеет тип bool. Строго говоря, вы случайно выбрали правильные строковые представления для true и false, но параметр значения для метода RadioButtonFor имеет тип object. Лучше всего передать фактический тип, который вы хотите сравнить, вместо того, чтобы преобразовывать его в строку самостоятельно. Подробнее об этом ниже.

Вот что происходит в глубине:

Рамка хочет сделать все это за вас автоматически, но вы сделали эти первые две вещи неправильно, что заставляет вас бороться с каркасом, чтобы получить нужное вам поведение.

Метод RadioButtonFor вызывает .ToString() по значению указанного вами свойства и сравнивает его с .ToString() значения, которое вы передали при создании переключателя. Если они равны, то он внутренне устанавливает isChecked = true и заканчивает рендеринг checked="checked" в HTML. Вот как он решает, какой переключатель выбрать. Он просто сравнивает значение переключателя со значением свойства и проверяет ту, которая соответствует.

Вы можете отображать переключатели для почти любого свойства таким образом, и это будет волшебным образом работать. Строки, ints и даже перечисления все работают! Любой объект, который имеет метод ToString, который возвращает строку, которая однозначно представляет значение объекта, будет работать. Вам просто нужно убедиться, что вы настроили значение переключателя на значение, которое может иметь ваше свойство. Самый простой способ сделать это - просто передать значение, а не строковое представление значения. Пусть структура преобразует его в строку для вас.

(Так как вам удалось передать правильные строковые представления true и false, то эти значения будут работать до тех пор, пока вы исправите две свои фактические ошибки, но по-прежнему разумно передавать фактические значения, а не их строки.)

Ваша первая реальная ошибка заключалась в жестком кодировании Checked = "checked" для переключателя "Нет". Это переопределит то, что система пытается сделать для вас, и результаты этого переключателя всегда проверяются.

Очевидно, что вы хотите, чтобы переключатель "Нет" был предварительно выбран, но вы должны сделать это так, чтобы он совместим со всем выше. Вам нужно предоставить представление экземпляра вашего модельного класса, где для параметра "Условия использования" установлено значение "ложь" и "привязать" его к переключателям. Обычно действие контроллера, которое отвечает на первоначальный запрос GET URL-адреса, вообще не отображает экземпляр класса модели. Как правило, вы просто return View();. Однако, поскольку вы хотите, чтобы значение по умолчанию было выбрано, вы должны предоставить представление экземпляру вашей модели, для которого условия TermsAndConditions установлены на false.

Вот несколько исходных кодов, иллюстрирующих все это:

Какой-то класс учетной записи, который вы, вероятно, уже имеете. (Модель вашего вида):

public class Account
{
    public bool TermsAndConditions { get; set; }
    //other properties here.
}

Некоторые методы в вашем контроллере:

//This handles the initial GET request.
public ActionResult CreateAccount()
{
    //this default instance will be used to pre-populate the form, making the "No" radio button checked.
    var account = new Account
    {
        TermsAndConditions = false
    };

    return View( account );
}

//This handles the POST request.
[HttpPost]
public ActionResult CreateAccount( Account account )
{
    if ( account.TermsAndConditions )
    {
        //TODO: Other validation, and create the account.
        return RedirectToAction( "Welcome" );
    }
    else
    {
        ModelState.AddModelError( "TermsAndConditionsAgreement", "You must agree to the Terms and Conditions." );
        return View( account );
    }           
}

//Something to redirect to.
public ActionResult Welcome()
{
    return View();
}

Весь вид:

@model Account
@{
    ViewBag.Title = "Create Account";
}
@using ( Html.BeginForm() )
{
    <div>
        <span>Do you agree to the Terms and Conditions?</span>
        <br />
        @Html.RadioButtonFor( model => model.TermsAndConditions, true, new { id = "TermsAndConditions_true" } )
        <label for="TermsAndConditions_true">Yes</label>
        <br />
        @Html.RadioButtonFor( model => model.TermsAndConditions, false, new { id = "TermsAndConditions_false" } )
        <label for="TermsAndConditions_false">No</label>
        <br />
        @Html.ValidationMessage( "TermsAndConditionsAgreement" )
    </div>
    <div>
        <input id="CreateAccount" type="submit" name="submit" value="Create Account" />
    </div>
}

БОНУС:. Вы заметите, что я добавил немного дополнительных функций к переключателям. Вместо того, чтобы просто использовать простой текст для ярлыков радиокнопки, я использовал элемент HTML label с атрибутом for, установленным на идентификаторы каждого переключателя. Это позволяет пользователям щелкнуть по метке, чтобы выбрать радиокнопку вместо того, чтобы щелкнуть по самой кнопке. Это стандартный HTML. Для этого я должен был установить ручные идентификаторы на переключателях, иначе они оба получат одинаковый идентификатор только "TermsAndConditions", который не будет работать.

Ответ 2

Есть несколько вещей, которые вам нужно сделать здесь, чтобы гарантировать, что выбор пользователя поддерживается после проверки на стороне сервера.

a) Привяжите свойство "checked" каждого радио к вашей модели в представлении, например:

Yes
@Html.RadioButtonFor(model => model.TermsAndConditions, "True", model.TermsAndConditions == true ? new { Checked = "checked" } : null)
No
@Html.RadioButtonFor(model => model.TermsAndConditions, "False", model.TermsAndConditions == false ? new { Checked = "checked" } : null)

b) Чтобы определить начальное значение по умолчанию при первом просмотре, инициализируйте модель, возвращенную в представление в запросе GET (в действии контроллера), например:

public ActionResult SomeForm()
{
    return View(new SomeModel { TermsAndConditions = false });
}

b) Убедитесь, что в действии вашего контроллера [HttpPost] вы возвращаете модель при неудачной проверке, например:

[HttpPost]
public ActionResult SomeForm(SomeModel model)
{
    if (!ModelState.IsValid)
        return View(model);

    // Do other stuff here
}

Таким образом, когда представление отображается в ответе после завершения проверки, оно будет иметь фактическое состояние модели, которое было передано (таким образом, поддерживая выбор пользователя).

Ответ 3

Я не могу сказать, так как вы не показывали свой код, но я подозреваю, что если вы отказываетесь от проверки на стороне сервера, вы просто возвращаете необработанное представление. Когда это не удается, вам нужно заполнить представление с помощью модели, которая была отправлена, так же, как если бы вы возвращали любые другие ошибки проверки. В противном случае вы получите значения модели по умолчанию (которые всегда будут ложными для логической регистрации).

Может быть, вы можете опубликовать свой код на стороне сервера?

Ответ 4

Здесь я предлагаю еще один более сложный пример.

 public enum UserCommunicationOptions
    {
        IPreferEmailAndSMS = 1,
        IPreferEmail = 2,
        IPreferSMS = 3
    }

Html

@model   UserProfileView

// Some other code

 <div class="form-group">
                    <label class="col-lg-2 control-label">Communication</label>
                    <div class="col-lg-10">
                        <div class="  col-xs-">
                            @if (Model.UserCommunicationOption.ToString() == UserCommunicationOptions.IPreferEmailAndSMS.ToString())
                            {
                                @Html.RadioButtonFor(x => x.UserCommunicationOption, (int)UserCommunicationOptions.IPreferEmailAndSMS, new { @checked = "checked" })
                            }
                            else
                            {
                                @Html.RadioButtonFor(x => x.UserCommunicationOption, (int)UserCommunicationOptions.IPreferEmailAndSMS)
                            }
                            <label class="  control-label" for="@Model.UserCommunicationOption">I Prefer Email And SMS</label>
                        </div>
                        <div class=" col-xs-">
                            @if (Model.UserCommunicationOption.ToString() == UserCommunicationOptions.IPreferEmail.ToString())
                            {
                                @Html.RadioButtonFor(x => x.UserCommunicationOption, (int)UserCommunicationOptions.IPreferEmail, new { @checked = "checked" })
                            }
                            else
                            {
                                @Html.RadioButtonFor(x => x.UserCommunicationOption, (int)UserCommunicationOptions.IPreferEmail)
                            }
                            <label class=" control-label" for="@Model.UserCommunicationOption">I Prefer Email</label>
                        </div>
                        <div class="  col-xs-">
                            @if (Model.UserCommunicationOption.ToString() == UserCommunicationOptions.IPreferSMS.ToString())
                            {
                                @Html.RadioButtonFor(x => x.UserCommunicationOption, (int)UserCommunicationOptions.IPreferSMS, new { @checked = "checked" })
                            }
                            else
                            {
                                @Html.RadioButtonFor(x => x.UserCommunicationOption, (int)UserCommunicationOptions.IPreferSMS)
                            }

                            <label class=" control-label" for="@Model.UserCommunicationOption">@DLMModelEntities.Properties.Resource.IPreferSMS</label>
                        </div>
                    </div>
                </div>


Model

   [Required(ErrorMessageResourceName = "Communications", ErrorMessageResourceType = typeof(Resource))]
        [Display(Name = "Communications", ResourceType = typeof(DLMModelEntities.Properties.Resource))]
        public UserCommunicationOptions UserCommunicationOption { get; set; }

GET

   var client = AppModel.Clients.Single(x => x.Id == clientId);           

                if (Convert.ToBoolean(client.IsEmailMessage) && Convert.ToBoolean(client.IsSMSMessage))
                {
                    model.UserCommunicationOption = UserCommunicationOptions.IPreferEmailAndSMS;
                }
                else if (Convert.ToBoolean(client.IsEmailMessage))
                {
                    model.UserCommunicationOption = UserCommunicationOptions.IPreferEmail;
                }
                else if ( Convert.ToBoolean(client.IsSMSMessage))
                {
                    model.UserCommunicationOption = UserCommunicationOptions.IPreferSMS;
                }

POST

  [HttpPost]
        public ActionResult MyProfile(UserProfileView model)
        {
 // Some code
 var client = AppModel.Clients.Single(x => x.Id == clientId);

            if (model.UserCommunicationOption == UserCommunicationOptions.IPreferEmail)
            {
                client.IsSMSMessage = false;
                client.IsEmailMessage = true;
            }
            else if (model.UserCommunicationOption == UserCommunicationOptions.IPreferEmailAndSMS)
            {
                client.IsSMSMessage = true;
                client.IsEmailMessage = true;
            }
            else if (model.UserCommunicationOption == UserCommunicationOptions.IPreferSMS)
            {
                client.IsSMSMessage = true;
                client.IsEmailMessage = false;
            }

            AppModel.SaveChanges();
//Some code


}

База данных

enter image description here

страница

enter image description here

Ответ 5

У меня была аналогичная проблема и я решил проблему, установив значение ViewData в контроллере для отслеживания того, что выбрал пользователь.