Комментарии 76
Во-первых, с самого начала возникла необходимость из шаблона обращаться к методам основного объекта веб-приложения (назовем его Main) — например, конфигурация, менеджер тем, к методам вызывающего контроллера и так далее.
По MVC-шной идеологии в норме не должно быть необходимости из View обращаться к внешним ресурсам — вьюшка получает модель со всем необходимым. Я не прав? :)
ControlHelper — он реализован как свойство базовой страницы для удобства использования? Это не набор статических по сути хэлперов?
Про разметку МастерПейджФайла тегами, к сожалению, не понял идею. Возможно, потому что апологет Razor'a… :)
Вы всегда точно знаете, где у вас лежит MasterPage? Или же параметр все-таки может зависеть от настроек, от выбранной темы, от выбранного стиля, от фазы луны....?
«Во-первых, с самого начала возникла необходимость из шаблона обращаться к методам основного объекта веб-приложения (назовем его Main) — например, конфигурация, менеджер тем, к методам вызывающего контроллера и так далее.»
В этот момент вы и нарушили MVC. Дальше можно не читать.
Все, что представлению нужно, должно быть во (вью)модели.
В этот момент вы и нарушили MVC. Дальше можно не читать.
Все, что представлению нужно, должно быть во (вью)модели.
Подскажите, как View узнает текущую тему или например имя пользователя и его роли?
Model.CurrentTheme
Model.User.Name
Model.User.Roles
(последнее, кстати, View знать не надо в 98% случаев)
Model.User.Name
Model.User.Roles
(последнее, кстати, View знать не надо в 98% случаев)
Это значит надо каждый раз во View передавать значения этих полей? т.е. сделать базовый класс для всех моделей?
«Это значит надо каждый раз во View передавать значения этих полей? т.е. сделать базовый класс для всех моделей?»
Несколько вариантов решения:
— базовый класс для всех моделей
— дженерик-класс для моделей (это чище с точки зрения семантики)
— инъекция дополнительной модели во viewbag на уровне фильтра и простенький хелпер, который ее оттуда достает (самое семантически чистое решение, но чуть более грязный код)
«Почему же. В зависимости от ролей показывать те или иные ссылки, части страницы и данные (в одном представлении). Это про Roles „
Это классическая ошибка при разделении обязанностей. В идеальном view нет ни одного if, в реальных view if опираются на данные представления (“надо показать»), а не бизнес-логику («если пользователь — админ»).
Надо показать разные данные? Отфильтруйте данные в контроллере.
Надо показать разные ссылки (меню)? Положите меню в модель и меняйте в контроллере.
Надо показать разные части страницы? Сделайте в модели признаки класса ShowEditToolbar и опирайтесь на них, а не на User.Roles.Contains(«Admin»)
Несколько вариантов решения:
— базовый класс для всех моделей
— дженерик-класс для моделей (это чище с точки зрения семантики)
— инъекция дополнительной модели во viewbag на уровне фильтра и простенький хелпер, который ее оттуда достает (самое семантически чистое решение, но чуть более грязный код)
«Почему же. В зависимости от ролей показывать те или иные ссылки, части страницы и данные (в одном представлении). Это про Roles „
Это классическая ошибка при разделении обязанностей. В идеальном view нет ни одного if, в реальных view if опираются на данные представления (“надо показать»), а не бизнес-логику («если пользователь — админ»).
Надо показать разные данные? Отфильтруйте данные в контроллере.
Надо показать разные ссылки (меню)? Положите меню в модель и меняйте в контроллере.
Надо показать разные части страницы? Сделайте в модели признаки класса ShowEditToolbar и опирайтесь на них, а не на User.Roles.Contains(«Admin»)
>> В идеальном view нет ни одного if
а если вам нужно разным цветом строчку в табличке нарисовать в зависимости от статуса? неужели цвет из контроллера передавать будите или всеже @helper метод?
а если вам нужно разным цветом строчку в табличке нарисовать в зависимости от статуса? неужели цвет из контроллера передавать будите или всеже @helper метод?
В идеальном случае — буду передавать цвет. В неидеальном — использовать функцию GetColorByStatus.
я бы второй сценарий посчитал более «идеальным». в нем вопросы предствления данных решает только ViewEngine
представьте сбебе тесты контроллера по первому сценарию, или задачу по редизайну, которая приводит к изменению свойств модели данных
представьте сбебе тесты контроллера по первому сценарию, или задачу по редизайну, которая приводит к изменению свойств модели данных
«представьте сбебе тесты контроллера по первому сценарию, или задачу по редизайну, которая приводит к изменению свойств модели данных „
Легко и непринужденно. А вот представьте себе автоматическое тестирование, что что нужная строчка действительно отображается нужным цветом, при втором сценарии?
Легко и непринужденно. А вот представьте себе автоматическое тестирование, что что нужная строчка действительно отображается нужным цветом, при втором сценарии?
сценарии тестирования UI — отдельная песня. лично я не склонен их автоматизировать, но даже если так, то данный кейс должен проверятся именно «там».
контроллер не в курсе, кто и как будет представлять модель данных пользователю. возьмем, например, другой ViewEngine который вместо рендеринга html будет поросто возвращать модель в xml или json. зачем нам тут свойство «цвет»?
контроллер не в курсе, кто и как будет представлять модель данных пользователю. возьмем, например, другой ViewEngine который вместо рендеринга html будет поросто возвращать модель в xml или json. зачем нам тут свойство «цвет»?
«контроллер не в курсе, кто и как будет представлять модель данных пользователю»
Это как?
Вот как раз контроллер-то и в курсе, больше некому. Именно контроллер говорит, что возвращать — View или Json или НашГениальныйActionResult.
«зачем нам тут свойство «цвет»»
Потому что в приведенном примере оно является частью бизнес-логики.
Вообще же мы тут вступаем на зыбкую почву деления логики на представление и бизнес, и там однозначные ответы бывают не очень часто.
Это как?
Вот как раз контроллер-то и в курсе, больше некому. Именно контроллер говорит, что возвращать — View или Json или НашГениальныйActionResult.
«зачем нам тут свойство «цвет»»
Потому что в приведенном примере оно является частью бизнес-логики.
Вообще же мы тут вступаем на зыбкую почву деления логики на представление и бизнес, и там однозначные ответы бывают не очень часто.
>>Вот как раз контроллер-то и в курсе, больше некому. Именно контроллер говорит, что возвращать — View или Json или НашГениальныйActionResult.
View() это метод базового класса, который на основе сконфигуренного в web.config ViewEngine выполнит действие по преобразованию модели в Response для клиента. мы можем использовать любой ViewEngine, не обзязательно Razor. Такмим образом контроллер об этом не знает.
Json это наследник ActionResult который к ViewEngine не имеет отношения.
View() это метод базового класса, который на основе сконфигуренного в web.config ViewEngine выполнит действие по преобразованию модели в Response для клиента. мы можем использовать любой ViewEngine, не обзязательно Razor. Такмим образом контроллер об этом не знает.
Json это наследник ActionResult который к ViewEngine не имеет отношения.
«View() это метод базового класса, который на основе сконфигуренного в web.config ViewEngine выполнит действие по преобразованию модели в Response для клиента. мы можем использовать любой ViewEngine, не обзязательно Razor. Такмим образом контроллер об этом не знает.»
Говоря «View» контроллер _ожидает_, что там будет именно представление. А раз представление — значит, цвет имеет смысл, кто и как его отрисует — вопрос пятнадцатый.
«Json это наследник ActionResult который к ViewEngine не имеет отношения. „
Не вы ли написали: “другой ViewEngine который вместо рендеринга html будет поросто возвращать модель в xml или json»?
Вот именно чтобы не нарушать семантику и очевидность происходящего не надо писать такие viewengine.
Говоря «View» контроллер _ожидает_, что там будет именно представление. А раз представление — значит, цвет имеет смысл, кто и как его отрисует — вопрос пятнадцатый.
«Json это наследник ActionResult который к ViewEngine не имеет отношения. „
Не вы ли написали: “другой ViewEngine который вместо рендеринга html будет поросто возвращать модель в xml или json»?
Вот именно чтобы не нарушать семантику и очевидность происходящего не надо писать такие viewengine.
>> Говоря «View» контроллер _ожидает_, что там будет именно представление. А раз представление — значит, цвет имеет смысл, кто и как его отрисует — вопрос пятнадцатый.
настоящий контроллер ожидает что метод View() вернет наследника ActionResult, а кто там ему что в респонс напишет — определяется в конфигурации приложения.
>>Не вы ли написали: “другой ViewEngine который вместо рендеринга html будет поросто возвращать модель в xml или json»?
я решал подобную задачу (делал restful сервис) но там я вообще выкинул все ViewEngine и написал своего наследника ActionResult, который в зависимости от заголовков в реквесте формировал респонс в правильном формате.
мы с вами расходимся в тезисе, что свойство «цвет строчки в таблице» является частью бизнес логики. тут уж ничего не поделать.
настоящий контроллер ожидает что метод View() вернет наследника ActionResult, а кто там ему что в респонс напишет — определяется в конфигурации приложения.
>>Не вы ли написали: “другой ViewEngine который вместо рендеринга html будет поросто возвращать модель в xml или json»?
я решал подобную задачу (делал restful сервис) но там я вообще выкинул все ViewEngine и написал своего наследника ActionResult, который в зависимости от заголовков в реквесте формировал респонс в правильном формате.
мы с вами расходимся в тезисе, что свойство «цвет строчки в таблице» является частью бизнес логики. тут уж ничего не поделать.
«настоящий контроллер ожидает что метод View() вернет наследника ActionResult, а кто там ему что в респонс напишет — определяется в конфигурации приложения.»
Тем не менее, написать туда должны именно представление. Смысл всей этой операции именно такой.
«я решал подобную задачу (делал restful сервис) но там я вообще выкинул все ViewEngine и написал своего наследника ActionResult, который в зависимости от заголовков в реквесте формировал респонс в правильном формате.»
Вы решали совершенно другую задачу, которая вообще к MVC отношения не имеет.
Тем не менее, написать туда должны именно представление. Смысл всей этой операции именно такой.
«я решал подобную задачу (делал restful сервис) но там я вообще выкинул все ViewEngine и написал своего наследника ActionResult, который в зависимости от заголовков в реквесте формировал респонс в правильном формате.»
Вы решали совершенно другую задачу, которая вообще к MVC отношения не имеет.
>>Тем не менее, написать туда должны именно представление. Смысл всей этой операции именно такой.
это ваше личное мнение, которое к MVC отношения не имеет )
это ваше личное мнение, которое к MVC отношения не имеет )
То есть семантика слова View, ViewResult и ViewEngine (а так же их «случайное» совпадение с V из триады MVC) — это мое личное мнение, а не банальный английский язык?
Bad design, что я могу сказать. МакКоннел в таких местах очень ругается. Если я пишу return new ViewResult() — то я возвращаю представление, так явно написано в коде, и противоречить этому нехорошо и не стоит.
(Вот то, как это представление генерится, что в реальности там отдается — html, который отобразится браузером, или json для клиентского шаблона, который взаимодействует с кастомным viewengine — дело пятое.)
Не надо в стремлении к универсальности нарушать прозрачность понимания кода.
Bad design, что я могу сказать. МакКоннел в таких местах очень ругается. Если я пишу return new ViewResult() — то я возвращаю представление, так явно написано в коде, и противоречить этому нехорошо и не стоит.
(Вот то, как это представление генерится, что в реальности там отдается — html, который отобразится браузером, или json для клиентского шаблона, который взаимодействует с кастомным viewengine — дело пятое.)
Не надо в стремлении к универсальности нарушать прозрачность понимания кода.
>>То есть семантика слова View, ViewResult и ViewEngine (а так же их «случайное» совпадение с V из триады MVC) — это мое личное мнение, а не банальный английский язык?
View не обязательно то, что умеет отображать цвета и представляется html'ом. представление в общем случае может быть любым.
если вы считаете «цвет строчки в табличке» данными приложения, а не параметром представления — я с вами никак не могу согласиться.
View не обязательно то, что умеет отображать цвета и представляется html'ом. представление в общем случае может быть любым.
если вы считаете «цвет строчки в табличке» данными приложения, а не параметром представления — я с вами никак не могу согласиться.
«View не обязательно то, что умеет отображать цвета и представляется html'ом. представление в общем случае может быть любым.»
Тем не мене, это именно представление. Поэтому ваш комментарий про «это ваше личное мнение, которое к MVC отношения не имеет» как-то не к месту.
«если вы считаете «цвет строчки в табличке» данными приложения, а не параметром представления — я с вами никак не могу согласиться. „
Это может быть данными приложения как минимум в двух случаях: это записывается пользователем (настраиваемый интерфейс), и этот цвет напрямую зависит от бизнес-логики.
Конечно, лучше абстрагировать цвета через признаки. Но это не всегда удается.
Тем не мене, это именно представление. Поэтому ваш комментарий про «это ваше личное мнение, которое к MVC отношения не имеет» как-то не к месту.
«если вы считаете «цвет строчки в табличке» данными приложения, а не параметром представления — я с вами никак не могу согласиться. „
Это может быть данными приложения как минимум в двух случаях: это записывается пользователем (настраиваемый интерфейс), и этот цвет напрямую зависит от бизнес-логики.
Конечно, лучше абстрагировать цвета через признаки. Но это не всегда удается.
напомню изначальный сценарий:
>>а если вам нужно разным цветом строчку в табличке нарисовать в зависимости от статуса? неужели цвет из контроллера передавать будите?
ваш ответ был утвердительным.
вы согласны с тезисом, что передавать значение цвета элемена UI из контроллера — есть нарушение принципа разделения ответственности в MVC приложении как бы удобно это не выглядело?
>>а если вам нужно разным цветом строчку в табличке нарисовать в зависимости от статуса? неужели цвет из контроллера передавать будите?
ваш ответ был утвердительным.
вы согласны с тезисом, что передавать значение цвета элемена UI из контроллера — есть нарушение принципа разделения ответственности в MVC приложении как бы удобно это не выглядело?
«ваш ответ был утвердительным.»
Это не так, я указал два варианта ответа.
«вы согласны с тезисом, что передавать значение цвета элемена UI из контроллера — есть нарушение принципа разделения ответственности в MVC приложении как бы удобно это не выглядело?»
Нет, не согласен. Потому что это «в зависимости от статуса». Зависимость чего-то от статуса вполне может быть бизнес-логикой. А бизнес-логике не место в представлении.
Вы, надеюсь, помните, что в asp.net mvc M — не чистая модель, а viewmodel, то есть view-specific data?
Это не так, я указал два варианта ответа.
«вы согласны с тезисом, что передавать значение цвета элемена UI из контроллера — есть нарушение принципа разделения ответственности в MVC приложении как бы удобно это не выглядело?»
Нет, не согласен. Потому что это «в зависимости от статуса». Зависимость чего-то от статуса вполне может быть бизнес-логикой. А бизнес-логике не место в представлении.
Вы, надеюсь, помните, что в asp.net mvc M — не чистая модель, а viewmodel, то есть view-specific data?
еще маленький пример. представьте что нужно передать не цвет, а много всего (размеры, отступы, цвета, шрифты). вам сразу же захочется передавать это все одним css классом (иначе html будет уныл). если так, то у вас появляется зависимость логики от представления со всеми вытекающими.
«представьте что нужно передать не цвет, а много всего (размеры, отступы, цвета, шрифты).»
С трудом себе это представляю.
Тем не менее, если это зависит от какой-то бизнес-логики («если автор сообщения такой-то, то делать так»), то корректно выносить это из представления выше, чтобы это было тестируемо. Можно прямо в контроллер, можно в промежуточный фильтр.
Но чаще всего это вырождается в следующее: отображаемые объекты (например, письма) имеют признаки (например: непрочитанные, личные, важные). Допустимо (хотя и противоречит пуристскому подходу) писать во view код следующего толка:
if (message.IsImportant) {сделать жирным}
if (message.IsPersonal) {добавить иконку}
Но вот писать там же
if (message.Size > 15M) {нарисовать иконку гири}
уже не стоит.
Грань очень тонкая, на самом деле.
С трудом себе это представляю.
Тем не менее, если это зависит от какой-то бизнес-логики («если автор сообщения такой-то, то делать так»), то корректно выносить это из представления выше, чтобы это было тестируемо. Можно прямо в контроллер, можно в промежуточный фильтр.
Но чаще всего это вырождается в следующее: отображаемые объекты (например, письма) имеют признаки (например: непрочитанные, личные, важные). Допустимо (хотя и противоречит пуристскому подходу) писать во view код следующего толка:
if (message.IsImportant) {сделать жирным}
if (message.IsPersonal) {добавить иконку}
Но вот писать там же
if (message.Size > 15M) {нарисовать иконку гири}
уже не стоит.
Грань очень тонкая, на самом деле.
я бы делал это так
@helper GetMessage(MyMessage msg)
{
if(msg.Size > 15)
{
<img картинка с гирей
}
if (msg.IsImportant)
{
<span class=жирно и красиво @msg.Text</span
}
else
{
@msg.Text
}
}
и далее во View там, где нужно нарисовать сообщение, вызываем наш хелпер метод:
@GetMessage(item)
в приведенном вами примере нет бизнес логики.
@helper GetMessage(MyMessage msg)
{
if(msg.Size > 15)
{
<img картинка с гирей
}
if (msg.IsImportant)
{
<span class=жирно и красиво @msg.Text</span
}
else
{
@msg.Text
}
}
и далее во View там, где нужно нарисовать сообщение, вызываем наш хелпер метод:
@GetMessage(item)
в приведенном вами примере нет бизнес логики.
Может быть подскажите Best Practices с использованием правильного MVC? Понятно, что бизнес логика должна быть в контроллере (или в сервисах) еще подумаю на сложными задачами. Но хотелось бы посмотреть реальный пример, как это все работает, а не на пример из коробки где все просто.
опс, неверно понял смысл с первого раза.
>> в реальных view if опираются на данные представления (“надо показать»), а не бизнес-логику («если пользователь — админ»).
вобщем да — все так.
>> в реальных view if опираются на данные представления (“надо показать»), а не бизнес-логику («если пользователь — админ»).
вобщем да — все так.
Почему же. В зависимости от ролей показывать те или иные ссылки, части страницы и данные (в одном представлении). Это про Roles
Логику относительно разных ролей контроллер должен обрабатывать.
Логику — да, а представление (что показать а что нет). Вот синтетический пример: админу нужно показать курс валюты $/евро, а пользователю — $/руб. Решение — дополнительное поле в Model?
Есть такая штука для ролей: User.IsInRole(«Admin»)
Какое-то все у вас не MVCшное.
Может не вник, но ViewModel не пробовали поделать под ваши нужды?
Разделения ответственности у вас в приложении получается, что нету.
Может не вник, но ViewModel не пробовали поделать под ваши нужды?
Разделения ответственности у вас в приложении получается, что нету.
>> с самого начала возникла необходимость из шаблона обращаться к методам основного объекта веб-приложени
Как уже было отмечено, вы концептуально нарушаете паттерн MVC
>>Стандартный класс System.Web.Mvc.ViewPage не предоставляет удобного функционала для этого
зато есть HtmlHelper и куча расширений к нему. в вашем случае можно использовать Html.RenderAction для вынесения всей логики по вызову «объекта Main» в контроллер.
Как уже было отмечено, вы концептуально нарушаете паттерн MVC
>>Стандартный класс System.Web.Mvc.ViewPage не предоставляет удобного функционала для этого
зато есть HtmlHelper и куча расширений к нему. в вашем случае можно использовать Html.RenderAction для вынесения всей логики по вызову «объекта Main» в контроллер.
И еще.
«Во-вторых, возникла необходимость в представлениях не задавать прямой путь MasterPageFile, а размечать его дополнительными тегами а-ля «CurrentTheme.SiteMaster», «UserTheme.SiteMaster» и т.п. К сожалению, при записи подобной строки в атрибут MasterPageFile директивы @Page я получал ошибку синтаксического анализатора, ругавшегося на отсутствие файла „~/Views/{CurrentTheme.SiteMaster}“. Единственное найденное решение — создание своего атрибута для директивы @Page, например MasterPagePath»
В Razor это делается напрямую — в Layout можно писать что угодно. В WebForms такие штуки можно делать что на уровне action — просто отдавая View с указанием нужного мастерпейджа, что на уровне actionfilter, записывая MasterName, что написав собственный ViewEngine.
codeofrob.com/archive/2009/11/01/dynamically-switching-between-master-pages-in-asp.net-mvc.aspx
«Во-вторых, возникла необходимость в представлениях не задавать прямой путь MasterPageFile, а размечать его дополнительными тегами а-ля «CurrentTheme.SiteMaster», «UserTheme.SiteMaster» и т.п. К сожалению, при записи подобной строки в атрибут MasterPageFile директивы @Page я получал ошибку синтаксического анализатора, ругавшегося на отсутствие файла „~/Views/{CurrentTheme.SiteMaster}“. Единственное найденное решение — создание своего атрибута для директивы @Page, например MasterPagePath»
В Razor это делается напрямую — в Layout можно писать что угодно. В WebForms такие штуки можно делать что на уровне action — просто отдавая View с указанием нужного мастерпейджа, что на уровне actionfilter, записывая MasterName, что написав собственный ViewEngine.
codeofrob.com/archive/2009/11/01/dynamically-switching-between-master-pages-in-asp.net-mvc.aspx
А зачем потребовалось создавать ViewTypeParserFilter? Я точно помню, что в asp.net mvc совершенно аналогичный класс был. Собственно поэтому там обобщённые типы в aspx-, ascx-, master- файлах там и так работали.
Господа, а статья-то отличная.
Пример не сильно удачный, с построением связи из View -> Controller
Да, действительно, View ни чего не должна «знать» о контроллере.
А статья отличная, т.к. показывает как можно сделать следующее:
— View.cshtml —
Предположим, у нас был сайт, и рано или поздно кто-то захотел сделать CMS и менять какие-то блоки.
Можно сделать два подхода:
@Html.Placeholder(Model.Placeholder1)
@Html.Placeholder(Model.Placeholders, «placeholderName»)
А можно использовать эту статью, и просто в те, места что можно переопределять добавить дополнительный аттрибут и просто уже в рантайме засунуть туда нужный контент.
Есть и 4й более сложный способ — это переопределить RenderingEngine, но там очень много нужно сделать.
Автору зачёт.
Пример не сильно удачный, с построением связи из View -> Controller
Да, действительно, View ни чего не должна «знать» о контроллере.
А статья отличная, т.к. показывает как можно сделать следующее:
— View.cshtml —
Предположим, у нас был сайт, и рано или поздно кто-то захотел сделать CMS и менять какие-то блоки.
Можно сделать два подхода:
@Html.Placeholder(Model.Placeholder1)
@Html.Placeholder(Model.Placeholders, «placeholderName»)
А можно использовать эту статью, и просто в те, места что можно переопределять добавить дополнительный аттрибут и просто уже в рантайме засунуть туда нужный контент.
Есть и 4й более сложный способ — это переопределить RenderingEngine, но там очень много нужно сделать.
Автору зачёт.
Каждую конкретную проблему класса «что-то переопределить в представлении в рантайме» всегда можно решить намного проще, чем «дополнительным атрибутом».
Приведите конкретный пример — и вам расскажут, как это сделать. Для master page уже рассказали.
asp.net mvc, удивительным образом, очень хорошо расширяемый механизм. Причем прозрачно и (по большей части) очевидно.
Приведите конкретный пример — и вам расскажут, как это сделать. Для master page уже рассказали.
asp.net mvc, удивительным образом, очень хорошо расширяемый механизм. Причем прозрачно и (по большей части) очевидно.
Да?
Поделитесь рассуждением на тему.
У меня есть сайт с 1К+ вьюх.
Приходит change request который, коротко, говорит:
View1 — переобределить контент div'a с id='aaa' — брать из XML/SQL/etc
View2 — переопределить span с id='bbb' и брать из…
Самый простой способ это сделать, это сделать postprocessing используя идеи описаные в этой статье.
А не хоть по 1K и более вьюх и руками проставлять @Html.Placeholder или ещё что-то.
Поделитесь рассуждением на тему.
У меня есть сайт с 1К+ вьюх.
Приходит change request который, коротко, говорит:
View1 — переобределить контент div'a с id='aaa' — брать из XML/SQL/etc
View2 — переопределить span с id='bbb' и брать из…
Самый простой способ это сделать, это сделать postprocessing используя идеи описаные в этой статье.
А не хоть по 1K и более вьюх и руками проставлять @Html.Placeholder или ещё что-то.
«Приходит change request который, коротко, говорит:
View1 — переобределить контент div'a с id='aaa' — брать из XML/SQL/etc
View2 — переопределить span с id='bbb' и брать из…»
Ну, вообще так делать не надо, и такие CR надо спустить нафиг. (А сам факт их наличия показывает на некоторую недостаточность модульности в системе.)
Но если вам очень хочется так сделать, то вот вам три решения:
— написать свой viewengine, который будет парсить готовый html
— перехватить глобальным фильтром на выполнение результата
— в сайт.мастере/лэйауте вбросить jq-функцию, которая сделает это на клиенте.
А в среднем, хочу сказать, что обычно когда приходит такой CR, выясняется, что нужные дивы ничем не помечены.
View1 — переобределить контент div'a с id='aaa' — брать из XML/SQL/etc
View2 — переопределить span с id='bbb' и брать из…»
Ну, вообще так делать не надо, и такие CR надо спустить нафиг. (А сам факт их наличия показывает на некоторую недостаточность модульности в системе.)
Но если вам очень хочется так сделать, то вот вам три решения:
— написать свой viewengine, который будет парсить готовый html
— перехватить глобальным фильтром на выполнение результата
— в сайт.мастере/лэйауте вбросить jq-функцию, которая сделает это на клиенте.
А в среднем, хочу сказать, что обычно когда приходит такой CR, выясняется, что нужные дивы ничем не помечены.
Выше я высказывала мнение, что использовать свой ViewEngine достаточно трудоёмко.
Вбросить .js функцию на клиент, это свежее решение, но не идеальное. Подумаю в выходные над ним.
Что вы имеете ввиду, когда пишите: Перехватить глобальным фильтром?
P.S. Если они ни чем не помечены, то xPath и используем расположение :)
Вбросить .js функцию на клиент, это свежее решение, но не идеальное. Подумаю в выходные над ним.
Что вы имеете ввиду, когда пишите: Перехватить глобальным фильтром?
P.S. Если они ни чем не помечены, то xPath и используем расположение :)
«Выше я высказывала мнение, что использовать свой ViewEngine достаточно трудоёмко.»
Вообще-то, ничем не более трудоемко, чем написать свой парсер страницы (а это именно то, что понадобится делать для подхода автора).
«Что вы имеете ввиду, когда пишите: Перехватить глобальным фильтром?»
msdn.microsoft.com/en-us/library/gg416513%28VS.98%29.aspx
«Если они ни чем не помечены, то xPath и используем расположение»
Это если вам повезло и там валидный xml.
Вообще-то, ничем не более трудоемко, чем написать свой парсер страницы (а это именно то, что понадобится делать для подхода автора).
«Что вы имеете ввиду, когда пишите: Перехватить глобальным фильтром?»
msdn.microsoft.com/en-us/library/gg416513%28VS.98%29.aspx
«Если они ни чем не помечены, то xPath и используем расположение»
Это если вам повезло и там валидный xml.
> P.S. Если они ни чем не помечены, то xPath и используем расположение :)
Зачем пытаться изобрести XSLT из какого-либо другого ViewEngine'а? ;)
Зачем пытаться изобрести XSLT из какого-либо другого ViewEngine'а? ;)
У нас например для тем некоторые вьюхи основные переопределяются. Лежат себе в отдельной папке с теми же относительными путями. Правдо используют другой layout. Для релизации пришлось перегрузить контроллер и свой вьюэнджин.
Да, скорее всего я ушел от чистого MVC, но ушел осознанно, т.к. мне не нравится подход, в котором модели необходимо передавать ВСЕ данные для отображения. Имхо это ничем не отличается от php-кода вида
<?php
echo ""
if (..) echo "1"; else echo…
?>
где все жестко забито без изменений. Чтобы что-то изменить, нужно лезть в код самого приложения, малейшее изменение в модели (необходимое дизайнеру) мгновенно затрагивает программиста, от чего я и пытаюсь уйти, предоставляя дизайнеру некую свободу действий. Да, не чистый MVC. Но мы не на соревновании чистоты паттернов.
<?php
echo ""
if (..) echo "1"; else echo…
?>
где все жестко забито без изменений. Чтобы что-то изменить, нужно лезть в код самого приложения, малейшее изменение в модели (необходимое дизайнеру) мгновенно затрагивает программиста, от чего я и пытаюсь уйти, предоставляя дизайнеру некую свободу действий. Да, не чистый MVC. Но мы не на соревновании чистоты паттернов.
При правильно построенном mvc у дизайнера как раз полная свобода действий. Ключевое слово — правильно построенном.
Просто не надо путать дизайн и бизнес.
Просто не надо путать дизайн и бизнес.
ViewModel — контракт между контроллером и View. Вы, похоже, пытаетесь сделать что-то сверх-супер-пупер-универсальное, с блэкджеком, для всех ситуаций, включая такие, чтобы «дизайнер мог править все без программиста». По опыту — ни к чему хорошему не приводит это. Получается сложно-поддерживаемый монстр в итоге. Есть задача — она очень хорошо и просто реализуется на MVC (если это веб).
А изменение в данных — это значит изменение в концепции проекта, то есть меняется сам вид данных, которые нужно показывать — логично, что нужно переделывать ViewModel. А если у вас на форме новое поле, вы тоже хотите без участия программиста это решать, силами дизайнера?
А изменение в данных — это значит изменение в концепции проекта, то есть меняется сам вид данных, которые нужно показывать — логично, что нужно переделывать ViewModel. А если у вас на форме новое поле, вы тоже хотите без участия программиста это решать, силами дизайнера?
Я так понимаю, что на вопросы по существу вам отвечать не хочется?
Вы уточните вопрос, на который ответ нужен.
Зачем вы это делаете (нарушаете паттерн, влезаете внутрь страницы для обработки masterpage, когда есть существенно более простые способы это сделать) и какую именно задачу вы пытаетесь решить?
приведу очень простую схему, которая используется в некоторых форумных движках:
Есть выбранная по-умолчанию тема сайта, есть тема, выбранная пользователем. Index.aspx не знает о том, какая тема выбрана, ему знать это и не надо. Ему автоматом подставляется нужный masterpage итоговой темы.
Пример 2. Есть основная тема сайта, есть маленькая «дочерняя» тема, которая отличается от основной только стилями Css и изображениями (к примеру), выходит некое наследование тем. Невозможно каждый путь к изображениям заключать в placeholder-ы, а стандартные Skin И Theme ASP.NET могут не устраивать по функционалу. В таком случае пути к ресурсам тем (например, картинка /Theme/Main/Images/Logo.jpg в masterpage прописывается как {ThemePath}/Images/Logo.jpg. Хелпер возвращает путь /Theme/Main/Images/Logo.jpg для основной темы, и /Theme/MainBlack/Images/Logo.jpg для дочерней.
Пример 3. У меня реализованы информационные блоки в представлениях. Грубо говоря, это отдельный usercontrol, с шапкой и телом, например как блок «Информация о блоге» в верхней части этой страницы. Виджеты дизайнер волен вставлять на странице в любую точку. Но нюанс в том, что виджеты могут быть разные от темы к теме, они могут блокироваться на определенных страницах. Обращаться к ним следует так <%=Helper.Widget(«name»)%>. Как предложите реализовывать их стандартными средствами, через зарегистрированный UserControl? А где тогда тут MVC? виджет это собственный маленький MVC, где Controller=объект виджета, View=представление для виджета, Model — отдельная модель для взаимодействия виджета и его представления.
Нюансов море, я еще не сделал и половины работы, но стандартные средства меня не устроили. А вот вы предложите вариант парсинга такого тегированного MasterPageFile с помощью кастомного Viewengine.
Есть выбранная по-умолчанию тема сайта, есть тема, выбранная пользователем. Index.aspx не знает о том, какая тема выбрана, ему знать это и не надо. Ему автоматом подставляется нужный masterpage итоговой темы.
Пример 2. Есть основная тема сайта, есть маленькая «дочерняя» тема, которая отличается от основной только стилями Css и изображениями (к примеру), выходит некое наследование тем. Невозможно каждый путь к изображениям заключать в placeholder-ы, а стандартные Skin И Theme ASP.NET могут не устраивать по функционалу. В таком случае пути к ресурсам тем (например, картинка /Theme/Main/Images/Logo.jpg в masterpage прописывается как {ThemePath}/Images/Logo.jpg. Хелпер возвращает путь /Theme/Main/Images/Logo.jpg для основной темы, и /Theme/MainBlack/Images/Logo.jpg для дочерней.
Пример 3. У меня реализованы информационные блоки в представлениях. Грубо говоря, это отдельный usercontrol, с шапкой и телом, например как блок «Информация о блоге» в верхней части этой страницы. Виджеты дизайнер волен вставлять на странице в любую точку. Но нюанс в том, что виджеты могут быть разные от темы к теме, они могут блокироваться на определенных страницах. Обращаться к ним следует так <%=Helper.Widget(«name»)%>. Как предложите реализовывать их стандартными средствами, через зарегистрированный UserControl? А где тогда тут MVC? виджет это собственный маленький MVC, где Controller=объект виджета, View=представление для виджета, Model — отдельная модель для взаимодействия виджета и его представления.
Нюансов море, я еще не сделал и половины работы, но стандартные средства меня не устроили. А вот вы предложите вариант парсинга такого тегированного MasterPageFile с помощью кастомного Viewengine.
«Есть выбранная по-умолчанию тема сайта, есть тема, выбранная пользователем. Index.aspx не знает о том, какая тема выбрана, ему знать это и не надо. Ему автоматом подставляется нужный masterpage итоговой темы.»
Для этого masterpage, как уже писалось выше, можно передавать из контроллера, подставлять в глобальном фильтре, подставлять во viewengine. Ничего из того, что вы делаете, не нужно.
«В таком случае пути к ресурсам тем (например, картинка /Theme/Main/Images/Logo.jpg в masterpage прописывается как {ThemePath}/Images/Logo.jpg. Хелпер возвращает путь /Theme/Main/Images/Logo.jpg для основной темы, и /Theme/MainBlack/Images/Logo.jpg для дочерней.»
Данные по теме пишутся во viewbag (инъекцией в глобальном фильтре), дальше пишется один хелпер, который достает эти данные (синтаксисом Html.Theme().Path), дальше эти данные *явно* используются в странице (т.е., для генерации пути к картинке делается Url.Content(string.Format("{0}/Images/Logo.jpg", Html.Theme().Path)).
«виджет это собственный маленький MVC, где Controller=объект виджета, View=представление для виджета, Model — отдельная модель для взаимодействия виджета и его представления.»
Я не очень понимаю, что вы имеете в виду под «Controller=объект виджета» (и думаю, что здесь можно обойтись обычным html helper), но обычно когда надо такое сделать (использовать отдельную mvc-триаду), берут Html.RenderAction(), который осуществляет полную обработку запроса (контроллер-действие-вывод). Ну и никто не мешает сделать Html.Widget(«name»), который внутри себя спрячет то, что нужно.
Видите? Все сделано стандартными средствами, без какого-либо тегирования.
Для этого masterpage, как уже писалось выше, можно передавать из контроллера, подставлять в глобальном фильтре, подставлять во viewengine. Ничего из того, что вы делаете, не нужно.
«В таком случае пути к ресурсам тем (например, картинка /Theme/Main/Images/Logo.jpg в masterpage прописывается как {ThemePath}/Images/Logo.jpg. Хелпер возвращает путь /Theme/Main/Images/Logo.jpg для основной темы, и /Theme/MainBlack/Images/Logo.jpg для дочерней.»
Данные по теме пишутся во viewbag (инъекцией в глобальном фильтре), дальше пишется один хелпер, который достает эти данные (синтаксисом Html.Theme().Path), дальше эти данные *явно* используются в странице (т.е., для генерации пути к картинке делается Url.Content(string.Format("{0}/Images/Logo.jpg", Html.Theme().Path)).
«виджет это собственный маленький MVC, где Controller=объект виджета, View=представление для виджета, Model — отдельная модель для взаимодействия виджета и его представления.»
Я не очень понимаю, что вы имеете в виду под «Controller=объект виджета» (и думаю, что здесь можно обойтись обычным html helper), но обычно когда надо такое сделать (использовать отдельную mvc-триаду), берут Html.RenderAction(), который осуществляет полную обработку запроса (контроллер-действие-вывод). Ну и никто не мешает сделать Html.Widget(«name»), который внутри себя спрячет то, что нужно.
Видите? Все сделано стандартными средствами, без какого-либо тегирования.
«Для этого masterpage, как уже писалось выше, можно передавать из контроллера, подставлять в глобальном фильтре, подставлять во viewengine. Ничего из того, что вы делаете, не нужно.»
Контроллеру абсолютно ничего не нужно знать про тему и про masterpage. Возможно, дизайнер вообще отказался от использования masterpage в данном представлении и он не нужен, а контроллер продолжает передавать ему ненужную информацию.
«Данные по теме пишутся во viewbag (инъекцией в глобальном фильтре), дальше пишется один хелпер, который достает эти данные (синтаксисом Html.Theme().Path), дальше эти данные *явно* используются в странице (т.е., для генерации пути к картинке делается Url.Content(string.Format(»{0}/Images/Logo.jpg", Html.Theme().Path))."
опять же во ViewBag писать нужно из контроллера. Контроллер не должен знать про тему.
насчет виджета да, варианты есть, не спорю. Можно вообще сделать свой server control и работать так: />.
Контроллеру абсолютно ничего не нужно знать про тему и про masterpage. Возможно, дизайнер вообще отказался от использования masterpage в данном представлении и он не нужен, а контроллер продолжает передавать ему ненужную информацию.
«Данные по теме пишутся во viewbag (инъекцией в глобальном фильтре), дальше пишется один хелпер, который достает эти данные (синтаксисом Html.Theme().Path), дальше эти данные *явно* используются в странице (т.е., для генерации пути к картинке делается Url.Content(string.Format(»{0}/Images/Logo.jpg", Html.Theme().Path))."
опять же во ViewBag писать нужно из контроллера. Контроллер не должен знать про тему.
насчет виджета да, варианты есть, не спорю. Можно вообще сделать свой server control и работать так: />.
«Возможно, дизайнер вообще отказался от использования masterpage в данном представлении и он не нужен, а контроллер продолжает передавать ему ненужную информацию.»
Тогда — переходите на razor, где layout задается императивно (и опирайтесь на тот же Html.Theme()). Ноль проблем.
(Но вообще вы только что дали дизайнеру феерическую возможность выстрелить себе в ногу: выбрать мастер-пейдж, а потом подменить за него этот мастер-пейдж, чтобы получить несочетаемое. Так делать… не принято, скажем так.)
«опять же во ViewBag писать нужно из контроллера»
Я же написал: инъекцией в глобальном фильтре. Контроллер тут не при чем.
«Можно вообще сделать свой server control»
В asp.net mvc нет серверных контролов. Ну нет. Вообще. Модель не предусматривает.
Тогда — переходите на razor, где layout задается императивно (и опирайтесь на тот же Html.Theme()). Ноль проблем.
(Но вообще вы только что дали дизайнеру феерическую возможность выстрелить себе в ногу: выбрать мастер-пейдж, а потом подменить за него этот мастер-пейдж, чтобы получить несочетаемое. Так делать… не принято, скажем так.)
«опять же во ViewBag писать нужно из контроллера»
Я же написал: инъекцией в глобальном фильтре. Контроллер тут не при чем.
«Можно вообще сделать свой server control»
В asp.net mvc нет серверных контролов. Ну нет. Вообще. Модель не предусматривает.
«Я же написал: инъекцией в глобальном фильтре. Контроллер тут не при чем»
если я правильно понимаю, ViewBag это динамическая коллекция. Если в фильтре я задам ViewBag.Test = 123
а в представлении напишу
<%=ViewBag.
то после точки IntelliSence мне ничего не подставит, т.к. ViewBag заполняется в рантайме. Так?
если я правильно понимаю, ViewBag это динамическая коллекция. Если в фильтре я задам ViewBag.Test = 123
а в представлении напишу
<%=ViewBag.
то после точки IntelliSence мне ничего не подставит, т.к. ViewBag заполняется в рантайме. Так?
«если я правильно понимаю, ViewBag это динамическая коллекция. Если в фильтре я задам ViewBag.Test = 123
а в представлении напишу
а в представлении напишу
м, а смысл цитировать часть моего сообщения?
Парсер — лох, как любят говорить на одном малоизвестном бложике.
Так вот, ViewBag — это не динамическая коллекция, а dynamic, но за исключением этого вы правы. Соответственно, чтобы у вас не было проблем с IntelliSense и ошибками времени выполнения, и пишется хелпер Html.Theme(), который внутри себя обращается к ViewBag по нужному ключу и преобразует объект в нужный тип.
(понятно, что хелпер должен быть согласован с инъектором, но это несложно)
Так вот, ViewBag — это не динамическая коллекция, а dynamic, но за исключением этого вы правы. Соответственно, чтобы у вас не было проблем с IntelliSense и ошибками времени выполнения, и пишется хелпер Html.Theme(), который внутри себя обращается к ViewBag по нужному ключу и преобразует объект в нужный тип.
(понятно, что хелпер должен быть согласован с инъектором, но это несложно)
Не сложнее чем тот же дополнительный кастомный хелпер. В общем понятно, я понял о чем вы говорите, буду копать в эту сторону, но в некоторых моментах лично для меня мой подход выгоднее и удобнее.
Вот только он нарушает паттерн, которым вы пользуетесь. В этом и зло.
От этого взорвется сервер или самопроизвольно зародится линукс? Еще в самом начале обсуждения я упомянул, что это не конкурс чистоты паттернов. Да, в MVC море стандартных возможностей, но изобретение велосипеда дело изобретателя, если мозг хочет разминки и руки чешутся, никто этого запретить не может.
Вот только вопрос — зачем? На ASP.NET, да еще и с MVC такие «костыли» для «улучшения MVC».
Хотите вы или нет, но ASP.NET MVC построена на костях классического ASP.NET
Да неужели? :)
MVC не построен на «классическом» ASP.NET. MVC — это абстракция вокруг ASP.NET-фреймворка, реализующая паттерн Front Controller, имеющая перед собой цель SoC (Separation of Concerns). Так называемый «классический ASP.NET» — это ASP.NET WebForms, и это просто другая абстракия вокруг «базового» ASP.NET Framework'а, реализующая другие принципы (включая паттерн Page Controller).
«Классическим» называют вебформс, так как он был единственной абстракцией вокруг ASP.NET. И MVC — это отнюдь не надстройка над WebForms.
Просто был один WebForms, а теперь на ASP.NET три фреймворка (от MS) — WebForms, MVC и WebMatrix.
MVC не построен на «классическом» ASP.NET. MVC — это абстракция вокруг ASP.NET-фреймворка, реализующая паттерн Front Controller, имеющая перед собой цель SoC (Separation of Concerns). Так называемый «классический ASP.NET» — это ASP.NET WebForms, и это просто другая абстракия вокруг «базового» ASP.NET Framework'а, реализующая другие принципы (включая паттерн Page Controller).
«Классическим» называют вебформс, так как он был единственной абстракцией вокруг ASP.NET. И MVC — это отнюдь не надстройка над WebForms.
Просто был один WebForms, а теперь на ASP.NET три фреймворка (от MS) — WebForms, MVC и WebMatrix.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Расширение функционала тегов Page/MasterPage/UserControl в ASP.NET MVC