Зовнішній шаблон у підкресленні


121

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

У перегляді хребта я маю:

 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

EDIT: Ця відповідь стара і застаріла. Я б її видалив, але це "прийнята" відповідь. Я натомість накладу свою думку.

Я б не рекомендував робити це більше. Натомість я розділив би всі шаблони на окремі 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, тому мій код насправді виглядає приблизно так і уникаю символів втечі в кінці рядка:

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

Використовуючи цей підхід, можна уникнути перенапруги в Requ.js там, де це справді не потрібно.


46
цей підхід втратить будь-які функції виділення синтаксису, переформатування та рефакторингу, що є у наявності. не голосуючий, хоча.
Kinjal Dixit

1
Вибачте, але мені довелося спростувати цю відповідь. Це жахливо незграбно, оскільки він як і раніше зберігатиме файли шаблонів як файли сценаріїв, просто начебто змушені виглядати як шаблони. Шаблони повинні бути шаблонами, тому якщо вам доведеться принести Require.js або використовувати геніальне рішення koorchik нижче, я вважаю, що це, безумовно, варто.
Tommi Forsström

3
@ TommiForsström Я згоден. Я відійшов від такого підходу. Оце Так! 4 грудня 2011 року насправді давно у світі розвитку Backbone.js :)
Брайан Генісіо

Насправді я хотів би видалити цю відповідь, але не можу, тому що це прийнята відповідь. Він застарів і є набагато кращі рішення, ніж це. Сьогодні я мав би їх як окремі файли шаблонів і використовую грухте завдання (наприклад, 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


3
@BinaryNights - чи слід завжди додавати dataType: 'html'до нашого запиту ajax, про всяк випадок?
Метт

Чи працює це також для вкладених подань? Мабуть, я не можу його працювати, якщо подання посилається на інший вигляд.
Т. Россі

1
Так, він повинен працювати і для вкладених шаблонів. Просто додайте помічник візуалізації та називайте його так: <% = render ('nested_template', дані)%>
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
Дійсно приємний маленький mixin там дуже акуратний! :) ура для обміну
Нік Уайт

Дуже круто D, це було таке рішення, яке я шукав. і я думаю, що це може бути використане для збереження приватного набору шаблонів.
bigmadwolf

@abhi це передбачено у відповіді. Також вам потрібен jQuery для завантаження шаблону, але ви можете переписати частину коду, який завантажує шаблон через AJAX на ваш смак, використовуючи будь-яку іншу бібліотеку.
Дмитро

@Dmitriy async: false є застарілим, тому якщо я зателефонував із параметром async, він не працює, я думаю, це тому, що за замовчуванням async є істинним, що означає виклик syncronisilly, тому у вас є рішення для цієї проблеми
abhi

@abhi, він працює для jQuery 1. * Також дивіться цю відповідь stackoverflow.com/a/11755262/541961
Дмитро

17

Я не хотів використовувати requ.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());

в моїх поглядах на хребет.


1
Використовуючи це, це чудово підходить для scenerio, де я намагаюся використовувати Jasmine для TDD і хочу протестувати шаблони, перш ніж я впроваджую Requjs та його плагін textjs. Молодці @Tramp
Ніколас Мюррей

Виклик $ .ajax є асинхронним, і все, що залежить від результатів, повинно виконуватися в межах виконаного методу повернутої обіцянки.
JoshRoss

Дякую за це Я цим користувався. Одна пропозиція: немає причин додавати як тег скрипту - можна просто перейти до конвертації до шаблону та зберегти його в пошуковому хеші. Ось (нефункціональний) приклад загадки
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 поставляється з цим завданням під назвою "watch", яке в основному буде контролювати зміни файлів, визначених у вашому локальному файлі grunt.js (що в основному є конфігураційним файлом для вашого проекту Grunt, в JavaScript ). Якщо у вас є грим, почніть це завдання для вас, ввівши:

grunt watch

з командного рядка Grunt буде контролювати всі внесені вами зміни у файли та автоматично виконувати всі завдання, які ви встановили для цього, у тому файлі grunt.js, якщо він виявить зміни - наприклад, jst завдання, описане вище. Відредагуйте, а потім збережіть свої файли, а всі ваші шаблони перекомпілюйте в один js-файл, навіть якщо вони розповсюджені на декілька каталогів та підкаталогів.

Подібні завдання можна налаштувати для зв’язування вашого JavaScript, запуску тестів, об'єднання та мінімізації / зменшення вмісту файлів сценарію. І все може бути прив’язано до завдання перегляду, так що зміни у ваших файлах автоматично спричинить нову «збірку» вашого проекту.

Щоб налаштувати речі та зрозуміти, як налаштувати файл grunt.js, потрібен певний час, але це добре, варто того, щоб вкладено витрачений час, і я не думаю, що ви коли-небудь повернетесь до попереднього способу роботи


Улюблена відповідь. Це має бути прийнятою відповіддю. (не моя)
Брайан Генісіо

Приємна точка входу до бурчання. Це добре працює для простого HTML, але якщо у мене є <% = price%> або подібне, я отримую: несподіваний маркер =, не вдалося компілювати з
grunt

Мені подобається такий підхід (використовуючи JST), за винятком проблем із цим: template: JST['test.html']()він, схоже, не завантажує дані з JST :( (див. Моє запитання тут: stackoverflow.com/questions/29723392/… )
timhc22

15

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

Навчальний посібник за посиланням вище дуже добре показує, як можна було б організувати базовий проект. Зразок реалізації також надається. Сподіваюся, це допомагає.


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();

Що стосується вашої 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;
}

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. Само собою зрозуміло, що це не загальне рішення. Сказавши, що ...

/ кінцеве попередження

Якщо ви використовуєте ASP.NET, ви можете розширити свої шаблони, просто розмістивши їх у одному або декількох власних часткових уявленнях. Aka у вашому .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" });

Сподіваюсь, що цей невловимий очевидний підхід допомагає комусь заощадити годину на голову.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.