Хитрость байндинга булевых полей в asp.net mvc

Сегодня мне вдруг стало любопытно, как происходит байндинг булевых полей в MVC.
Причину любопытства можно пояснить на примере:
Допустим, у нас есть модель:

public class TestModel {
   public string Name {get;set;}
   public bool IsDeleted {get;set;}
}

И есть две хтмл-формы для редактирования этой модели, в первой из которых присутствует флаг IsDeleted, а во второй — нет:
Форма 1:

@using (Html.BeginForm) {
    @Html.TextboxFor(x => x.Name)
    @Html.CheckboxFor(x => x.IsDeleted)
    <input type="submit" value="OK" />
}

Форма 2:

@using (Html.BeginForm) {
    @Html.TextboxFor(x => x.Name)
    <input type="submit" value="OK" />
}

При этом серверный обработчик этой формы выглядит вполне типично:

public ActionResult UpdateModel(int id) {
  var model = Db.Load<TestModel>(id);
  TryUpdateModel(model);
  return RedirectToAction();
}

Предположим, что в базе у TestModel IsDeleted == true. Что произойдет, если

  • мы отсабмитим Форму1 со снятым флагом?
  • мы отсабмитим Форму2, в которой флага IsDeleted просто нет?

Как и ожидалось, в обоих случаях MVC отработал отлично, и в случае 1 в базе у сущности TestModel поле IsDeleted стало false, а в случае 2 — осталась в true.
В чем же причина любопытства? Мне было любопытно, как же именно это работает.
В «классическом html» чекбоксы обычно представлены в виде <input name=’IsDeleted’ type=’checkbox’ />. А при сабмите формы на сервер это отправляется в виде:

  • http://localhost/?name=zcx&IsDeleted=on — если флажок проставлен
  • http://localhost/?name=zcx — если флажок не стоит

Как видно, в «классическом случае» ситуация отсутствия чекбокса в форме как такового и ситуация, когда он есть, но «галочка не стоит» абсолютно одинаковы, и выполнить корректный байндинг не представляется возможным.

Именно поэтому мне было интересно заглянуть «под капот» и узнать, как же это реализовано в mvc3.
Решение оказалось простым: на каждое булево поле mvc генерит такой html:

<input type="checkbox" name="IsDeleted" value="true" /><input type="hidden" name="IsDeleted" value="false" />

Вот и весь секрет :)
Возможно, это вполне стандартный прием у веб-программистов, но я о нём почему-то не знал, потому восполнить пробел было очень любопытно, и, уверен, этот приём мне еще не раз пригодится на практике.

Опубликовать в Facebook
Опубликовать в Google Plus

7 комментариев

  1. нет, значение не перезапишется. Байндинг увидит, что параметра IsDeleted в запросе просто нет и менять его значение не станет.

    Мне скорее интересно, насколько это стандартный прием за пределами asp.net mvc :)

  2. Подожди-подожди? А как он увидит-то? Ведь hidden генерится в темплейте CheckBox’а. И если чекбокс изначально не рендерится, то и hidden’а не будет.

  3. Да, хиддена не будет, байндер поймет, что поля в html просто не было и изменять значение модели не станет.

    Я правильно понимаю, что ты говоришь о «Форме2» из примера в заметке и ситуации, когда изначальное значение TestModel.IsDeleted == true?

  4. Да, я говорю об этой ситуации. В смысле «не станет заменять». На этапе баиндинга ему и нечего заменять — ему просто нужно выставить значение.

  5. Ну так он просто не будет его выставлять. Тем самым оставив начальное значение, которое было после загрузки объекта из БД.

  6. Блин, я затупил. Я почему-то думал, что ты вручную присваиваешь значение из модели, а ты ведь TryUpdateModel юзаешь…

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *