Внешний шаблон в Underscore

121

Я использую шаблон Underscore . Можно ли прикрепить внешний файл в качестве шаблона ?

В Backbone View у меня есть:

 textTemplate: _.template( $('#practice-text-template').html() ),

 initialize: function(){                                            
  this.words = new WordList;            
  this.index = 0;
  this.render();
 },

В моем html есть:

<script id="practice-text-template" type="text/template">
   <h3>something code</h3>
</script>

Это работает хорошо. Но мне нужен внешний шаблон . Я пытаюсь:

<script id="practice-text-template" type="text/template" src="templates/tmp.js">

или

textTemplate: _.template( $('#practice-text-template').load('templates/tmp.js') ),

или

$('#practice-text-template').load('templates/tmp.js', function(data){ this.textTemplate = _.template( data ) })

но это не сработало.

Томаш
источник

Ответы:

51

РЕДАКТИРОВАТЬ: этот ответ старый и устаревший. Я бы удалил его, но это "принятый" ответ. Вместо этого я выскажу свое мнение.

Я бы больше не выступал за это. Вместо этого я бы разделил все шаблоны на отдельные файлы HTML. Некоторые предлагают загружать их асинхронно (Require.js или своего рода кеш-шаблон). Это хорошо работает в небольших проектах, но в больших проектах с большим количеством шаблонов вы обнаруживаете, что делаете тонну небольших асинхронных запросов при загрузке страницы, что мне очень не нравится. (тьфу ... хорошо, вы можете обойти это с помощью Require.js, предварительно скомпилировав свои начальные зависимости с помощью r.js, но для шаблонов это все еще кажется мне неправильным)

Мне нравится использовать задачу grunt (grunt-contrib-jst) для компиляции всех HTML-шаблонов в один файл templates.js и включения его. Вы получаете лучшее из всех миров IMO ... шаблоны живут в файле, компиляция указанных шаблонов происходит во время сборки (а не во время выполнения), и у вас нет сотни крошечных асинхронных запросов при запуске страницы.

Все ниже - мусор

Лично я предпочитаю простоту включения JS-файла в мой шаблон. Итак, я мог бы создать файл с именем view_template.js, который включает шаблон в качестве переменной:

app.templates.view = " \
    <h3>something code</h3> \
";

Затем это так же просто, как включить файл сценария, как обычный, а затем использовать его в своем представлении:

template: _.template(app.templates.view)

Сделав еще один шаг, я на самом деле использую coffeescript, поэтому мой код на самом деле выглядит примерно так и избегает escape-символов конца строки:

app.templates.view = '''
    <h3>something code</h3>
'''

Использование этого подхода позволяет избежать добавления require.js туда, где в этом нет необходимости.

Брайан Генизио
источник
46
при таком подходе будут потеряны все функции подсветки синтаксиса, переформатирования и рефакторинга, доступные в ide. но не голосование.
Кинджал Диксит
1
Извините, но мне пришлось проголосовать против этого ответа. Это ужасно неуклюже, так как файлы шаблонов по-прежнему будут храниться в виде файлов сценариев, которые просто вынуждены выглядеть как шаблоны. Шаблоны должны быть шаблонами, поэтому, если вам нужно добавить Require.js или использовать блестящее решение koorchik ниже, я считаю, что оно того стоит.
Tommi Forsström
3
@ TommiForsström Согласен. Я отошел от этого подхода. Вот Это Да! 4 декабря 2011 г. - это очень давно в мире разработки Backbone.js :)
Брайан Генизио
На самом деле, я бы хотел удалить этот ответ, но не могу, потому что это принятый ответ. Он устарел, и есть гораздо лучшие решения, чем это. Сегодня я бы использовал их как отдельные файлы шаблонов и использовал бы задачу grunt (например, JST), чтобы объединить их в отдельный файл templates.js, чтобы избежать асинхронного характера извлечения их всех по отдельности. Это лучшее из обоих подходов ИМО.
Брайан Генизио
Что ж, если шаблонов не так много, я думаю, что первое решение действительно наиболее эффективно.
silkAdmin
107

Вот простое решение:

var rendered_html = render('mytemplate', {});

function render(tmpl_name, tmpl_data) {
    if ( !render.tmpl_cache ) { 
        render.tmpl_cache = {};
    }

    if ( ! render.tmpl_cache[tmpl_name] ) {
        var tmpl_dir = '/static/templates';
        var tmpl_url = tmpl_dir + '/' + tmpl_name + '.html';

        var tmpl_string;
        $.ajax({
            url: tmpl_url,
            method: 'GET',
            dataType: 'html', //** Must add 
            async: false,
            success: function(data) {
                tmpl_string = data;
            }
        });

        render.tmpl_cache[tmpl_name] = _.template(tmpl_string);
    }

    return render.tmpl_cache[tmpl_name](tmpl_data);
}

Использование "async: false" здесь неплохой способ, потому что в любом случае вы должны дождаться загрузки шаблона.

Итак, функция "рендеринга"

  1. позволяет хранить каждый шаблон в отдельном html файле в статическом каталоге
  2. очень легкий
  3. компилирует и кеширует шаблоны
  4. абстрагирует логику загрузки шаблона. Например, в будущем вы можете использовать предварительно загруженные и предварительно скомпилированные шаблоны.
  5. легко использовать

[Я редактирую ответ вместо того, чтобы оставлять комментарий, потому что считаю это важным.]

если шаблоны не отображаются в собственном приложении , и вы видите HIERARCHY_REQUEST_ERROR: DOM Exception 3, посмотрите ответ Дэйва Робинсона на вопрос Что именно может вызвать ошибку "HIERARCHY_REQUEST_ERR: DOM Exception 3"? ,

По сути, вы должны добавить

dataType: 'html'

на запрос $ .ajax.

koorchik
источник
3
@BinaryNights - всегда ли мы должны добавлять dataType: 'html'в наш запрос ajax, на всякий случай?
Мэтт
Работает ли это также для вложенных представлений? По-видимому, я не могу заставить его работать, если представление относится к другому представлению.
Т. Росси
1
Да, это должно работать и для вложенных шаблонов. Просто добавьте помощника по рендерингу и назовите его так: <% = render ('nested_template', data)%>
koorchik
Здравствуйте, не могли бы вы подробнее рассказать о "шаблонах компиляции и кеширования"? Когда я пытался вызвать функцию рендеринга, она не добавляла tmpl_data для возврата значения, а просто передавала его, как есть. После этого мне пришлось вызвать метод Handlebars.compile. Спасибо.
cdagli
18

Это Mixin позволяет оказывать внешний шаблон с помощью подчеркивания в очень простым способом: _.templateFromUrl(url, [data], [settings]). API метода почти такой же, как _.template () в Underscore . Кеширование включено.

_.mixin({templateFromUrl: function (url, data, settings) {
    var templateHtml = "";
    this.cache = this.cache || {};

    if (this.cache[url]) {
        templateHtml = this.cache[url];
    } else {
        $.ajax({
            url: url,
            method: "GET",
            async: false,
            success: function(data) {
                templateHtml = data;
            }
        });

        this.cache[url] = templateHtml;
    }

    return _.template(templateHtml, data, settings);
}});

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

var someHtml = _.templateFromUrl("http://example.com/template.html", {"var": "value"});
Дмитрий
источник
2
Действительно хороший маленький миксин, очень аккуратный! :) Приветствую вас за то, что поделился
Ник Уайт
Очень круто, D, это было именно то решение, которое я искал. и я думаю, что можно было бы использовать для сохранения конфиденциальности набора шаблонов.
bigmadwolf
@abhi указано в ответе. Также вам понадобится jQuery для загрузки шаблона, но вы можете переписать часть кода, загружающего шаблон через AJAX, на свой вкус, используя любую другую библиотеку.
Дмитрий
@Dmitriy async: false устарел, поэтому, если я вызываю без параметра async, он не работает, я думаю, это потому, что по умолчанию async истинно, что означает вызов syncronisilly, так что у вас есть решение для этой проблемы
abhi
@abhi, это работает для jQuery 1. * См. также этот ответ stackoverflow.com/a/11755262/541961
Дмитрий
17

Я не хотел использовать require.js для этой простой задачи, поэтому использовал модифицированное решение koorchik.

function require_template(templateName, cb) {
    var template = $('#template_' + templateName);
    if (template.length === 0) {
        var tmpl_dir = './templates';
        var tmpl_url = tmpl_dir + '/' + templateName + '.tmpl';
        var tmpl_string = '';

        $.ajax({
            url: tmpl_url,
            method: 'GET',
            contentType: 'text',
            complete: function (data, text) {
                tmpl_string = data.responseText;
                $('head').append('<script id="template_' + templateName + '" type="text/template">' + tmpl_string + '<\/script>');
                if (typeof cb === 'function')
                    cb('tmpl_added');
            }
        });
    } else {
        callback('tmpl_already_exists');
    }
}

require_template('a', function(resp) {
    if (resp == 'tmpl_added' || 'tmpl_already_exists') {
        // init your template 'a' rendering
    }
});
require_template('b', function(resp) {
    if (resp == 'tmpl_added' || 'tmpl_already_exists') {
        // init your template 'b' rendering
    }
});

Зачем добавлять шаблоны в документ, а не хранить их в объекте javascript? Поскольку в производственной версии я хотел бы создать html-файл со всеми уже включенными шаблонами, поэтому мне не нужно будет делать какие-либо дополнительные запросы ajax. И в то же время мне не нужно будет проводить рефакторинг в моем коде, поскольку я использую

this.template = _.template($('#template_name').html());

в моих представлениях Backbone.

Tyth
источник
1
Использование этого также отлично подходит для сценария, в котором я пытаюсь использовать Jasmine для TDD и хочу протестировать шаблоны до того, как я реализовал requirejs и его плагин textjs. Молодец @Tramp
Николас Мюррей
Вызов $ .ajax является асинхронным, все, что зависит от результатов, должно выполняться в рамках метода done возвращенного обещания.
JoshRoss
Спасибо за это. Я использовал это. Одно предложение: нет причин для добавления в качестве тега сценария - можно просто преобразовать его в шаблон и сохранить его в поисковом хэше. Вот пример (нефункциональной) скрипки: jsfiddle.net/PyzeF
webnesto
async: falseустарел
ProblemsOfSumit
Поскольку async: falseустарело, я улучшил ответ, добавив completeобратный вызов.
Александр
16

Это может быть немного не по теме, но вы можете использовать Grunt (http://gruntjs.com/), который работает на node.js (http://nodejs.org/, доступен для всех основных платформ), чтобы запускать задачи из командная строка. Для этого инструмента существует множество плагинов, например, компилятор шаблонов, https://npmjs.org/package/grunt-contrib-jst . См. Документацию на GitHub, https://github.com/gruntjs/grunt-contrib-jst . (Вам также необходимо знать, как запустить диспетчер пакетов узлов, https://npmjs.org/ . Не волнуйтесь, это невероятно просто и универсально.)

Затем вы можете сохранить все свои шаблоны в отдельных html-файлах, запустить инструмент, чтобы предварительно скомпилировать их все, используя подчеркивание (которое, я считаю, является зависимостью для плагина JST, но не волнуйтесь, диспетчер пакетов узлов автоматически установит зависимости для вас).

Это компилирует все ваши шаблоны в один сценарий, скажем

templates.js

При загрузке скрипта будет установлен глобальный - "JST" по умолчанию - который представляет собой массив функций, доступ к которому можно получить следующим образом:

JST['templates/listView.html']()

что было бы похоже на

_.template( $('#selector-to-your-script-template'))

если вы поместите содержимое этого тега скрипта в (templates /) listView.html

Однако настоящий кикер заключается в следующем: Grunt поставляется с этой задачей под названием `` смотреть '', которая в основном будет отслеживать изменения в файлах, которые вы определили в своем локальном файле grunt.js (который в основном является файлом конфигурации для вашего проекта Grunt в javascript ). Если у вас есть хрюканье, запустите эту задачу за вас, набрав:

grunt watch

из командной строки Grunt будет отслеживать все изменения, которые вы вносите в файлы, и автоматически выполнять все задачи, которые вы настроили для него в этом файле grunt.js, если он обнаруживает изменения - например, задача jst, описанная выше. Отредактируйте, а затем сохраните свои файлы, и все ваши шаблоны перекомпилируются в один файл js, даже если они разбросаны по нескольким каталогам и подкаталогам.

Аналогичные задачи могут быть настроены для линтинга вашего javascript, запуска тестов, объединения и уменьшения / уменьшения ваших файлов скриптов. И все это можно привязать к задаче наблюдения, поэтому изменения в ваших файлах автоматически запускают новую «сборку» вашего проекта.

Требуется некоторое время, чтобы все настроить и понять, как настроить файл grunt.js, но это хорошо, стоит потраченного времени, и я не думаю, что вы когда-нибудь вернетесь к способу работы с предварительным ворчанием.

Mansiemans
источник
Любимый ответ. Это должен быть принятый ответ. (не мое)
Брайан Генизио
Хорошая отправная точка для ворчания. Он отлично работает для простого HTML, но если у меня есть <% = price%> или аналогичный, я получаю: неожиданный токен =, не удалось скомпилировать из grunt
mcktimo
Мне нравится этот подход (с использованием JST), за исключением того, что у меня проблемы с этим: template: JST['test.html']()похоже, он не загружает данные из JST :( (см. Мой вопрос здесь: stackoverflow.com/questions/29723392/… )
timhc22
15

Думаю, это может вам помочь. Все в решении вращается вокруг require.jsбиблиотеки, которая представляет собой файл JavaScript и загрузчик модулей.

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

nayaab
источник
3
Спасибо за ссылку на мой сайт, всем, кто смотрит. Я начал проект, который пытается реализовать передовой опыт backboneboilerplate.com
Томас Дэвис
4

Я заинтересовался шаблонами javascript и теперь делаю первые шаги с магистралью. Это то, что я придумал, и, кажется, работает очень хорошо.

window.App = {

    get : function(url) {
        var data = "<h1> failed to load url : " + url + "</h1>";
        $.ajax({
            async: false,
            url: url,
            success: function(response) {
                data = response;
            }
        });
        return data;
    }
}

App.ChromeView = Backbone.View.extend({
    template: _.template( App.get("tpl/chrome.html") ),
    render: function () {
        $(this.el).html(this.template());
        return this;
    },
});

App.chromeView = new App.ChromeView({ el : document.body });
App.chromeView.render();
j040p3d20
источник
В вашей getфункции я, вероятно, верну $.ajaxсам, чтобы он возвращал объект обещания, чтобы ваш шаблон не ответил сразу.
Деннис Ронго
4

Мне пришлось установить тип данных «текст», чтобы он работал у меня:

get : function(url) {
    var data = "<h1> failed to load url : " + url + "</h1>";
    $.ajax({
        async: false,
        dataType: "text",
        url: url,
        success: function(response) {
            data = response;
        }
    });
    return data;
}
user1828189
источник
2

Я нашел решение, которое мне подходит с использованием jQuery.

Я добавляю код шаблона подчеркивания с помощью метода jQuery.load () в основной файл html.

Как только он появится, я использую его для создания шаблонов. Все должно происходить синхронно!

Концепция:

У меня есть код шаблона карты подчеркивания:

<!-- MAP TEMPLATE-->
<script type="text/template" id="game-map-template">
    <% _.each(rc, function(rowItem, index){ %>
      <ul class="map-row" data-row="<%- index %>">
        <li class="map-col <%- colItem.areaType ? 'active-area' : '' %>"></li>
        ...
</script>

И я поместил этот код в файл с именем map-template.html.

После этого я создаю оболочку для файлов шаблона.

<div id="templatesPool"></div>

Затем я включаю этот файл в свой основной файл html вот так.

В голове:

<!-- Template Loader -->
<script> 
    $(function(){
      $("#templatesPool").append($('<div>').load("map-template.html")); 
    });
</script> 

Приветствия.

Калоян Стаматов
источник
1

Я знаю, что этот вопрос действительно старый, но он появился как первый результат поиска в Google шаблонов подчеркивания ajax.

Я устал не находить для этого хорошего решения, поэтому создал свой:

https://github.com/ziad-saab/underscore-async-templates

Помимо загрузки шаблонов подчеркивания с помощью AJAX, он добавляет функциональность <% include%>. Надеюсь, это может быть кому-то полезно.

Зияд-Сааб
источник
0

Мне было немного сложно заставить jQuery работать синхронно, поэтому я изменил предыдущий синхронный пример, используя обещания. Это почти то же самое, но работает асинхронно. В этом примере я использую шаблоны hbs:

var asyncRenderHbs= function(template_name, template_data) {
    if (!asyncRenderHbs.template_cache) { 
        asyncRenderHbs.template_cache= {};
    }

    var promise= undefined;

    if (!asyncRenderHbs.template_cache[template_name]) {
        promise= new Promise(function(resolve, reject) {
            var template_url= '/templates/' + template_name;
            $.ajax({
                url: template_url,
                method: 'GET',
                success: function(data) {
                    asyncRenderHbs.template_cache[template_name]= Handlebars.compile(data);
                    resolve(asyncRenderHbs.template_cache[template_name](template_data));
                },
                error: function(err, message) {
                    reject(err);
                }           
            });
        });
    } else {
        promise= Promise.resolve(asyncRenderHbs.template_cache[template_name](template_data));
    }

    return promise;
};

Затем использовать визуализированный html:

asyncRenderHbs('some_template.hbs', context)
    .then(function(html) {
        applicationMain.append(html);
        // Do other stuff here after html is rendered...
    })
    .catch(function(err) {
        // Handle errors
    });

ПРИМЕЧАНИЕ. Как обсуждали другие, было бы предпочтительнее скомпилировать все шаблоны в один файл templates.js и загрузить его вначале, а не иметь множество небольших синхронных вызовов AJAX для получения шаблонов при загрузке веб-страницы.

Мегатрон
источник
0

Предупреждение - вот драконы:

Я упоминаю о подходе, показанном ниже, просто для того, чтобы помочь тем, кто изо всех сил пытается заставить стеки ASP.NET (и аналогичные фреймворки) гармонично работать с экосистемой js-libs. Само собой разумеется, что это не универсальное решение. Было сказано, что ...

/ endforwardwarning

Если вы используете ASP.NET, вы можете экстернализовать свои шаблоны, просто разместив их внутри одного или нескольких собственных частичных представлений. Также внутри вашего .cshtml:

  @Html.Partial("path/to/template")

Внутри вашего template.cshtml:

   // this is razorview and thusly if you ever need to use the @ character in here  
   // you will have to either escape it as @@ or use the html codepoint which is &#64
   // http://stackoverflow.com/questions/3626250/escape-character-in-razor-view-engine
   <script type="text/x-template" id="someId">
        <span class="foo"><%= name %></span>
   </script>

И теперь вы можете использовать шаблон как обычно:

  _.template($("#someId").html())({ name: "Foobar" });

Надеюсь, этот неуловимо-очевидный подход поможет кому-то сэкономить час на головокружение.

XDS
источник