Pull to refresh

Comments 21

Вопрос скорее в том, что возможно вы делаете что-то не так, если используете для генерации хтмл в мвц приложении не razor. Я понимаю, что бывают ситуации, когда дешевле просто нарисовать тегбилдером что-то, но чтобы это были такие большие блоки кода, как в вашем случае…
Вы же в курсе, что 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 для этого тоже особо и не нужен.
Идея создать подобный «внутренний декларативный DSL» красивая, но вообще надо идти конца: не вижу смысла держаться за строки «div». В предельной форме надо выводить на синтаксис

var body = Body(e=>e.Width(100).Css("background", "whyte").Children(e2=>e2.Div(e3=>e3.P ...)));


В таком виде за много что будет ответственен компилиятор, выражения можно будет парсить и модифицировать при помощи Expression Trees и обязаталеьно найдутся интересные применения.

Думаю даже мог бы помочь вам в этом.

Интересная идея. Подумаю сам над этим. Ну и, если что: Fork — он же вполне доступный =).

Наличие DSL создающей DOM в виде Expression Tree даст две традиционные вещи — скорость (нужна не многим) и возможность создать новый DSL поверх (синтаксическая корректность выражений которого опять будет поддерживаться компилятором С#). Очевидно это может быть какой-то DSL манипуляций над DOM.

Вообще я думал о ситуации скорее что-то по типу определения html по вводному слову и отсечению "лишних" функций в Intellisense.
Т.е. если задать скажем первой строкой "button" — то во-первых добавлять .AddType — уже нет необходимости ведь и так ясно, что это кнопка, а значит система должна распознавать и подставлять это сама, а во-вторых — скрыть в Intellisense функции вида .AddRef() — потому что ссылочный параметр становится бесполезен.
Но тогда сильно страдает универсальность — на основе такой логики хорошо бы смотрелся отдельный набор классов под конкретный набор элементов, но который бы использовал мои классы как базу.

Что вы называете «Intellisense» это и есть контроль компилятора «вид сбоку». Но основные выгоды получаются не от intellisense самого по себе, а от того что с его помощью можно взять еще один уровень сложности/обобщения, поскольку корректность кирпичиков предыдущего уровня поддерживается не только «я верю», но и «comilation succeeded». До некоторой степени конечно.

P.S. я в основном с бутстрапом, как layout работаю — отсюда и постоянные "дивы".

Вся красота и мощь fluent-интерфейсов. Я только удивлен: неужели до сих пор нет ни одной подобной библиотеки во всем NuGet'e?

Я нашёл какое-то решение, когда начал уже своё ваять. Но там было что-то, что мне не очень понравилось.


Под "своё ваять" я подразумеваю то, что как бы до 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")
    )
);

Пример реализации хелпера H:
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);
}
Мой опыт говори что в C# больше смысла делать internal DSL не минималистичный, а наиболее полно включающий компилятор C# (т.е. лексер/паресер языка) в проверку корректности его выражений dev time. В случае html это должен быть internal dsl не позволяющий вставить в ul ничего кроме li.

Вообще конечно надо сгенерировать код такого Internal DSL из формальной грамматики HTML (той что построже). «Надо» — в смысле абициозных целей. С точки зрения практики возможны варианты.

наиболее полно включающий компилятор C# (т.е. лексер/паресер языка) в проверку корректности его выражений dev time. В случае html это должен быть internal dsl не позволяющий вставить в ul ничего кроме li.

С этой точки зрения Razor полностью удовлетворяет условиям. Разве что не является подмножеством C#. Но так ли это важно?


Если категорически не нравятся строковые пути к PartialView — есть T4MVC.
Если хочется оформить PartialView как TagHelper — есть RazorEngine (компилятор Razor отвязанный от ASP.NET)

проверил и поправляю себя. точно контроллирует дев тайм.
тогда да, ваши аргументы очень стройны, можно докапываться но не хочется. тут надо программировать и смотреть что получается. я плохой аналитик — наперед вижу код плохо.
П.С. кончено не так важно что Razor не подмножество C#.

Sign up to leave a comment.

Articles