Наследование HTML файлов в xslt-стиле

    Проблема


    Однажды, в ходе переписывания большого проекта, возникла необходимость улучшить механизм кастомизации html шаблонов под разные версии нашего web-приложения. В старой версии кастомизация выглядела подобным образом:

    {{if app.version==versions.main}}
    <!--один html код-->
    {{else if app.version==versions.custom1}}
    <!--другой html код-->
    {{else if app.version==versions.custom2}}
    <!--и ещё html код-->
    {{endif}}
    

    И подобной лапшой был пронизан весь проект. Такой код сложно поддерживать и изменять, при активном использовании шаблоны превращаются в непонятную кашу, где бизнес логика отображения отдельных блоков переплетается с кастомизацией под разные версии.

    Привыкнув к удобным методам управления версиями приложения с помощью инъекции зависимостей, когда в зависимости от версии используются разные реализации интерфейсов, я решил изобрести свой велосипед для XSLT-подобного управления версиями html файлов: grunt-html-inheritance. Он позволяет подменять кусочки базового html-файла с помощью маленьких патчей.

    При переписывании проекта в качестве фреймворка был выбран AngularJS, поэтому вёрстка в приложении хранится в виде множества html файлов, подгружаемых при необходимости, что позволило легко внедрить систему сборки этих файлов с кастомизацией под разные версии приложения.

    Пример


    Давайте представим, что у нас имеется следующий кусок вёрстки в HTML файле «myfile.html»:
    <div>
        Blah blah blah
        <div>Main version</div>
    </div>
    


    И, как часто бывает в разных версиях приложения, нам понадобилось, чтобы в версии «myversion» вместо одного текста «Main version» отображался другой: «My version». Чтобы сделать это с помощью html-inheritance, нужно сделать 2 простых шага:

    1. Добавить к кастомизируемому тегу аттрибут, начинающийся на «bl-»:

    <div>
        Blah blah blah
        <div bl-mytag>Main version</div>
    </div>
    


    2. Создать файл «myfile.myversion.html» с патчем к родительскому файлу:

        <div bl-mytag="replace">Custom version</div>
    


    Всё! При билде проекта все html файлы будут аккуратно сложены в указанную папку с разбивкой по версиям:

    dist
      |
      main/
         myfile.html
      myversion/
         myfile.html
    

    Теперь достаточно указать вашему приложению, откуда грузить html файлы в зависимости от версии, и всё будет работать само по себе!

    Установка


    Чтобы добавить grunt-html-inheritance в ваш проект, нужно установить npm модуль командой
    npm install grunt-html-inheritance --save-dev

    или добавить зависимость в package.json, загрузить задачу в Gruntfile.js командой
    grunt.loadNpmTasks('grunt-html-inheritance');

    и настроить задачу следующим образом:
    
    grunt.initConfig({
      html_inheritance: {
          main: {
              files: { 
                src: "**.html" //селектор для файлов
              },
              options: {
                  modules: ["version1", "version2"], //список модулей
                  dstDir: "../dist", //папка, в которую будут складываться скомпиленные файлы
              },
          },
      },
    });
    

    В AngularJS приложении важным условием легкого переключения между версиями HTML файлов является использование хелпера для построения путей к файлам шаблонов вместо жёсткого прописывания этих путей в директивах и роутингах.

    Преимущества


    Самое интересное преимущество такой системы кастомизации — основную версию проекта можно использовать без компиляции html файлов, так как bl-аттрибуты не мешают браузеру отображать исходный файл. Так же для использования такого метода не нужно изучать новый синтаксис какого-либо шаблонизатора, вся логика реализуется с помощью обычных html-аттрибутов, знакомых каждому разработчику.

    Использование


    Кроме замены базового тега в патче, описанного в примере выше, так же доступны следующие режимы:
    1. Удаление — удаление в патче родительского элемента
    2. Вставка — вставка в патче элемента, тогда как в основной версии элемента не будет
    3. Модификация аттрибутов — удаление и добавление аттрибутов к родительскому элементу, удаление и добавление классов.

    Полная документация доступна на странице пакета или в репозитории на github. Модуль покрыт тестами и нуждается в дальнейшем совершенстовании. Приглашаю сообщество помочь в развитии, присылайте pull-реквесты!

    Комментарии 2

      0
      Интересно, сами решаем подобную проблему. У вас не возникает трудностей с тем, что заранее не известно, какие именно элементы шаблона прийдется изменять в той или иной версии продукта, а на всех тегах расставлять «bl-» атрибуты может быть не удобно. Видите какие то пути решения этой проблемы с вашим подходом?
        0
        Можно извратиться, прописывая в файлах-патчах сложные селекторы к изменяемым блокам, но так, кажется, только запутаннее.

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

        Так что мы смирились с необходимостью проставлять bl-теги)

      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

      Самое читаемое