Сегодня мне вдруг стало любопытно, как происходит байндинг булевых полей в 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" />
Вот и весь секрет :)
Возможно, это вполне стандартный прием у веб-программистов, но я о нём почему-то не знал, потому восполнить пробел было очень любопытно, и, уверен, этот приём мне еще не раз пригодится на практике.
Штука известная, да: http://stackoverflow.com/questions/220020/how-to-handle-checkboxes-in-asp-net-mvc-forms
А если во втором случае у TestModel IsDeleted = false? Мне кажется, или в этом случае баиндинг все равно вернет True, и значение в базе перезапишется?
нет, значение не перезапишется. Байндинг увидит, что параметра IsDeleted в запросе просто нет и менять его значение не станет.
Мне скорее интересно, насколько это стандартный прием за пределами asp.net mvc :)
Подожди-подожди? А как он увидит-то? Ведь hidden генерится в темплейте CheckBox’а. И если чекбокс изначально не рендерится, то и hidden’а не будет.
Да, хиддена не будет, байндер поймет, что поля в html просто не было и изменять значение модели не станет.
Я правильно понимаю, что ты говоришь о «Форме2» из примера в заметке и ситуации, когда изначальное значение TestModel.IsDeleted == true?
Да, я говорю об этой ситуации. В смысле «не станет заменять». На этапе баиндинга ему и нечего заменять — ему просто нужно выставить значение.
Ну так он просто не будет его выставлять. Тем самым оставив начальное значение, которое было после загрузки объекта из БД.
Блин, я затупил. Я почему-то думал, что ты вручную присваиваешь значение из модели, а ты ведь TryUpdateModel юзаешь…