Comments 21
Вы же в курсе, что HtmlHelper умеет рендерить partial view? И что razor templates компилируются, если вас беспокоит производительность.
Когда рисуешь свой элемент управления для Razor — обычно хочется это компактно упихать в один namespace/static class, вместо того, чтобы создавать на каждый контрол 2-3-4-...partial view и соединять их ещё в одном "общем" PartialView.
Лично у меня на данных классах обычно висят небольшие функциональные элементы навигации/взаимодействия, которые выглядят "чуть-чуть по другому" нежели стандартный Bootstrap 3/4, но это вот "чуть-чуть" достаточно для того, что и уже их внутренние элементы надо изменять и получаемое в PartialView решение кажется мне недостаточно элегантным.
Кроме того — важный момент — я не очень люблю применять PartialView из-за того, что если не установлен Resharper — большинство partial элементов являются стринговыми и навигация по ним превращается в реальную мУку. Но это скорее личное предпочтение.
Регулярное использование этого класса показалось мне слишком муторным.
Вам не кажется, что с архитектурой что-то не так?
Долбанный TagBuilder удалить вообще надо. Пришлось как-то делать редизайн на сайте, большая часть которого генерировалась посредством TagBuilder. В итоге я занимался 2 месяца этим, так закончить и не осилил. Я ещё бы понял идею Data-Driven Document, но TagBuilder для этого тоже особо и не нужен.
var body = Body(e=>e.Width(100).Css("background", "whyte").Children(e2=>e2.Div(e3=>e3.P ...)));
В таком виде за много что будет ответственен компилиятор, выражения можно будет парсить и модифицировать при помощи Expression Trees и обязаталеьно найдутся интересные применения.
Думаю даже мог бы помочь вам в этом.
Интересная идея. Подумаю сам над этим. Ну и, если что: Fork — он же вполне доступный =).
Вообще я думал о ситуации скорее что-то по типу определения html по вводному слову и отсечению "лишних" функций в Intellisense.
Т.е. если задать скажем первой строкой "button" — то во-первых добавлять .AddType — уже нет необходимости ведь и так ясно, что это кнопка, а значит система должна распознавать и подставлять это сама, а во-вторых — скрыть в Intellisense функции вида .AddRef() — потому что ссылочный параметр становится бесполезен.
Но тогда сильно страдает универсальность — на основе такой логики хорошо бы смотрелся отдельный набор классов под конкретный набор элементов, но который бы использовал мои классы как базу.
P.S. я в основном с бутстрапом, как layout работаю — отсюда и постоянные "дивы".
Я нашёл какое-то решение, когда начал уже своё ваять. Но там было что-то, что мне не очень понравилось.
Под "своё ваять" я подразумеваю то, что как бы до pushа в GitHub — у меня уже сформировались некоторые классы. Просто в какой-то момент я понял, что использую куски нечто подобного уже в 3-4 проектах и просто взялся и собрал это всё в одно solution ну и запушил.
Это же ужасно тратить столько времени на написание подобных штук.
Почему нельзя использовать шаблонизаторы ?
Никто не сказал, что нельзя. Это просто ещё одно решение, который возможно кому-то будет полезно. Ни больше-ни меньше.
Потому что если брать _вообще_ — иметь внутреннее представление html в виде DOM это может понадобится. Почему броузер строит DOM? Почему react строит virtual DOM.
Есть конечно HtmlAgilityPack, но почему не попробовать идею — описывать DOM используя fluent, Internal DSL — потенциал у нее хороший.
Браузер и браузерные фреймворки строят DOM, чтобы его было удобнее обновлять, а здесь речь идет о серверной стороне, где данные рендерятся один раз и уходят на клиент.
Или я что-то не так понимаю?
То что HTML структуры похожие на DOM на сервере используются это просто факт. Я HTMLAgilityPack использую при автоматическом тестировании. Теперь если мне надо проверить содержит ли HTML то что мне нужно (отрывок другого html), мне нужно будет забить то что мне нужно в код — написать отрывок html. Можно строкой. А можно через Internal DSL.
Если уж нужен минималистичный DSL для HTML, то почему бы не воспользоваться опытом наших друзей фронтендеров: hyperscript?
В этом синтаксисе Ваш пример мог бы выглядеть так:
H("div.card", ("style", "width: 18rem;"),
H("img", ("src", "..."), ("alt", "Card image cap")),
H("div.card-body",
H("h5.card-title", "Card title"),
H("h5.card-text",
"Some quick example text to build on the card title and make up the bulk of the card's content."
),
H("a.btn.btn-primary", ("href", "#"), "Go somewhere")
)
);
public static string H(string name, params object[] props)
{
string[] classes = name.Split('.');
var tb = new TagBuilder(classes[0]);
for (int i = 1; i < classes.Length; i++)
{
tb.AddCssClass(classes[i]);
}
var sb = new StringBuilder();
foreach (object obj in props)
{
if (obj is string html)
{
sb.Append(html);
}
else if (obj is ValueTuple<string, string> attribute)
{
tb.MergeAttribute(attribute.Item1, attribute.Item2);
}
else
{
throw new NotSupportedException();
}
}
tb.InnerHtml = sb.ToString();
return tb.ToString(TagRenderMode.Normal);
}
Вообще конечно надо сгенерировать код такого Internal DSL из формальной грамматики HTML (той что построже). «Надо» — в смысле абициозных целей. С точки зрения практики возможны варианты.
наиболее полно включающий компилятор C# (т.е. лексер/паресер языка) в проверку корректности его выражений dev time. В случае html это должен быть internal dsl не позволяющий вставить в ul ничего кроме li.
С этой точки зрения Razor полностью удовлетворяет условиям. Разве что не является подмножеством C#. Но так ли это важно?
Если категорически не нравятся строковые пути к PartialView — есть T4MVC.
Если хочется оформить PartialView как TagHelper — есть RazorEngine (компилятор Razor отвязанный от ASP.NET)
Каскадная генерация HTML-тегов посредством C#