Пишем код

Заметки о .net разработке

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

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

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

<br />
public class TestModel {<br />
   public string Name {get;set;}<br />
   public bool IsDeleted {get;set;}<br />
}<br />

И есть две хтмл-формы для редактирования этой модели, в первой из которых присутствует флаг IsDeleted, а во второй — нет:
Форма 1:
<br />
@using (Html.BeginForm) {<br />
    @Html.TextboxFor(x => x.Name)<br />
    @Html.CheckboxFor(x => x.IsDeleted)<br />
    <input type="submit" value="OK" /><br />
}<br />

Форма 2:
<br />
@using (Html.BeginForm) {<br />
    @Html.TextboxFor(x => x.Name)<br />
    <input type="submit" value="OK" /><br />
}<br />

При этом серверный обработчик этой формы выглядит вполне типично:
<br />
public ActionResult UpdateModel(int id) {<br />
  var model = Db.Load<TestModel>(id);<br />
  TryUpdateModel(model);<br />
  return RedirectToAction();<br />
}<br />

Предположим, что в базе у 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:

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

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

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

Written by Shaddix

Апрель 15th, 2012 at 10:52 пп

Posted in .net,MVC,web

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

Subscribe to comments with RSS or TrackBack to 'Хитрость байндинга булевых полей в asp.net mvc'.

  1. Штука известная, да: http://stackoverflow.com/questions/220020/how-to-handle-checkboxes-in-asp-net-mvc-forms

    А если во втором случае у TestModel IsDeleted = false? Мне кажется, или в этом случае баиндинг все равно вернет True, и значение в базе перезапишется?

    AlexIdsa

    16 Апр 12 at 01:42

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

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

    Shaddix

    16 Апр 12 at 12:59

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

    AlexIdsa

    16 Апр 12 at 16:20

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

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

    Shaddix

    16 Апр 12 at 22:13

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

    AlexIdsa

    16 Апр 12 at 22:41

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

    Shaddix

    16 Апр 12 at 23:09

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

    AlexIdsa

    17 Апр 12 at 00:10

Leave a Reply