Alt Çizgide harici şablon


121

Kullandığım Underscore şablonu . Şablon olarak harici bir dosya eklemek mümkün mü?

Omurga Görünümünde şunlara sahibim:

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

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

Benim html'mde:

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

İyi çalışıyor. Ama harici şablona ihtiyacım var . Denerim:

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

veya

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

veya

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

Ama çalışmadı.

Yanıtlar:


51

DÜZENLEME: Bu cevap eski ve modası geçmiş. Onu silerdim ama bu "kabul edilen" cevaptır. Onun yerine fikrimi enjekte edeceğim.

Artık bunu yapmayı savunmuyorum. Bunun yerine, tüm şablonları ayrı HTML dosyalarına ayırırdım. Bazıları bunların eşzamansız olarak yüklenmesini önerir (Require.js veya bir tür şablon önbelleği). Bu, küçük projelerde iyi çalışıyor ancak çok sayıda şablon içeren büyük projelerde, kendinizi gerçekten sevmediğim sayfa yüklemesinde bir sürü küçük eşzamansız istek yaparken buluyorsunuz. (ugh ... tamam, r.js ile ilk bağımlılıklarınızı önceden derleyerek Require.js ile bunun üstesinden gelebilirsiniz, ancak şablonlar için bu hala bana yanlış geliyor)

Tüm HTML şablonlarını tek bir templates.js dosyasında derlemek ve bunu eklemek için bir grunt görevi (grunt-Contrib-jst) kullanmayı seviyorum. Tüm dünyaların en iyi IMO'sunu elde edersiniz ... şablonlar bir dosyada yaşar, söz konusu şablonların derlenmesi derleme zamanında gerçekleşir (çalışma zamanında değil) ve sayfa başladığında yüz minik eşzamansız isteğiniz olmaz.

Aşağıdaki her şey önemsiz

Benim için şablonuma bir JS dosyası eklemenin basitliğini tercih ediyorum. Bu nedenle, şablonu bir değişken olarak içeren view_template.js adlı bir dosya oluşturabilirim:

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

Ardından, komut dosyasını normal bir dosya gibi eklemek ve ardından kendi görünümünüzde kullanmak kadar basittir:

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

Bir adım daha alarak, ben aslında sonu hattı çıkış karakterleri benim kod aslında daha şuna benzer şekilde, CoffeeScript kullanmak ve kaçının:

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

Bu yaklaşımı kullanmak, gerçekten gerekli olmadığı durumlarda, require.js'de bir araya gelmeyi önler.


46
bu yaklaşım ide ile mevcut olan sözdizimi vurgulama, yeniden biçimlendirme ve yeniden düzenleme işlevlerini kaybedecektir. oy vermiyor.
Kinjal Dixit

1
Üzgünüm ama bu yanıta olumsuz oy vermem gerekti. Şablon dosyalarını betik dosyaları olarak tutmaya devam edeceği için korkunç derecede kullanışsız, sadece şablon gibi görünmeye zorlanıyor. Şablonların şablon olması gerekir, bu nedenle Require.js'yi getirmeniz veya koorchik'in aşağıdaki parlak çözümünü kullanmanız gerekirse, kesinlikle buna değer olduğunu düşünüyorum.
Tommi Forsström

3
@ TommiForsström Katılıyorum. Bu yaklaşımdan uzaklaştım. Vaov! 4 Aralık 2011, Backbone.js geliştirme dünyasında gerçekten çok uzun zaman önce :)
Brian Genisio

Aslında bu cevabı silmek istiyorum ama yapamıyorum çünkü kabul edilen cevap bu. Modası geçmiş ve bundan çok daha iyi çözümler var. Bugün, onları ayrı şablon dosyaları olarak alacak ve hepsini ayrı ayrı getirmenin zaman uyumsuz doğasından kaçınmak için bunları ayrı bir templates.js dosyası halinde oluşturmak için bir grunt görevi (örneğin JST) kullanacaktım. Her iki dünyanın da en iyisi IMO yaklaşımıdır.
Brian Genisio

pek çok şablon yoksa eski çözümün gerçekten en verimli olduğunu düşünüyorum.
silkAdmin

107

İşte basit bir çözüm:

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

Burada "async: false" kullanmak kötü bir yol değildir çünkü her durumda şablon yüklenene kadar beklemeniz gerekir.

Yani, "render" işlevi

  1. her şablonu ayrı bir html dosyasında statik dizinde saklamanıza izin verir
  2. çok hafif
  3. şablonları derler ve önbelleğe alır
  4. özetler şablon yükleme mantığı. Örneğin, gelecekte önceden yüklenmiş ve önceden derlenmiş şablonları kullanabilirsiniz.
  5. kullanımı kolay

[Yorum bırakmak yerine yanıtı düzenliyorum çünkü bunun önemli olduğuna inanıyorum.]

Şablonlar yerel uygulamadaHIERARCHY_REQUEST_ERROR: DOM Exception 3 görünmüyorsa ve görüyorsanız , Dave Robinson'ın "HIERARCHY_REQUEST_ERR: DOM İstisnası 3" -Hatasına tam olarak neden olabilir? sorusuna verilen cevaba bakın. .

Temel olarak eklemelisiniz

dataType: 'html'

$ .ajax isteğine.


3
@BinaryNights - her ihtimale dataType: 'html'karşı ajax isteğimize her zaman eklemeli miyiz ?
Matt

Bu, iç içe görünümler için de çalışır mı? Görünüşe göre bir görünüm başka bir görünüme atıfta bulunursa çalışmasını sağlayamam.
T. Rossi

1
Evet, iç içe geçmiş şablonlar için de çalışmalıdır. Oluşturma yardımcısını ekleyin ve şöyle çağırın: <% = render ('nested_template', data)%>
koorchik

Merhaba, "şablonları derler ve önbelleğe alır" hakkında biraz daha açıklayabilir misiniz? Render işlevini çağırmaya çalıştığımda, değeri döndürmek için tmpl_data eklemedi, olduğu gibi geçti. Bundan sonra "Handlebars.compile" yöntemini çağırmak zorunda kaldım. Teşekkür ederim.
cdagli

18

Bu mixin kullandığınız harici şablon oluşturma olanağı tanır alt çizgi çok basit bir şekilde: _.templateFromUrl(url, [data], [settings]). Yöntem API'si Alt Çizgi'nin _.template () ile hemen hemen aynıdır . Önbelleğe alma dahildir.

_.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);
}});

Kullanımı:

var someHtml = _.templateFromUrl("http://example.com/template.html", {"var": "value"});

2
Orada gerçekten güzel küçük bir karışım çok temiz! :) paylaşım için alkış
Nick White

Çok havalı D, aradığım türden bir çözüm buydu. ve bir dizi şablonu gizli tutmak için kullanılabileceğini düşünüyorum.
bigmadwolf

@abhi cevapta verilmektedir. Ayrıca, şablonu yüklemek için jQuery'ye ihtiyacınız vardır, ancak AJAX aracılığıyla şablonu yükleyen kodun bir bölümünü başka bir kitaplığı kullanarak zevkinize göre yeniden yazabilirsiniz.
Dmitriy

@Dmitriy async: false kullanımdan kaldırıldı, bu yüzden async parametresiyle ararsam çalışmıyor, bunun nedeni varsayılan olarak senkronizasyonun çağrılması anlamına geldiği için doğru olduğunu düşünüyorum, bu nedenle bu sorun için bir çözümünüz var mı
abhi

@abhi, jQuery 1 için çalışıyor. * Ayrıca bu yanıta bakın stackoverflow.com/a/11755262/541961
Dmitriy

17

Bu basit görev için require.js'yi kullanmak istemedim, bu yüzden değiştirilmiş koorchik'in çözümünü kullandım.

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

Şablonları javascript nesnesinde depolamak yerine neden belgeye eklemelisiniz? Çünkü üretim sürümünde, zaten dahil edilmiş tüm şablonlarla html dosyası oluşturmak istiyorum, bu nedenle herhangi bir ek ajax isteğinde bulunmam gerekmeyecek. Aynı zamanda, kullandığım gibi kodumda herhangi bir yeniden düzenleme yapmam gerekmeyecek

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

Omurga görünümlerimde.


1
Bunu da kullanmak, Jasmine'i TDD için kullanmaya çalıştığım ve requirejs ve textjs eklentisini uygulamadan önce şablonları test etmek istediğim senaryo için harika. Tebrikler @Tramp
Nicholas Murray

$ .Ajax çağrısı eşzamansızdır, sonuçlara bağlı olarak herhangi bir şey, döndürülen sözün done yöntemi içinde yürütülmelidir.
JoshRoss

Bunun için teşekkürler. Onu kullandım. Bir öneri: komut dosyası etiketi olarak eklemek için bir neden yok - devam edip bir şablona dönüştürebilir ve onu bir arama karmasında tutabilir. İşte (işlevsiz) bir keman örneği: jsfiddle.net/PyzeF
webnesto

async: falseşimdi kullanımdan kaldırıldı
ProblemsOfSumit

Kullanımdan async: falsekaldırıldığı için completegeri aramayı ekleyerek cevabı iyileştirdim .
İskender

16

Bu biraz konu dışı olabilir, ancak Grunt'ı (http://gruntjs.com/) - node.js üzerinde çalışan (http://nodejs.org/, tüm büyük platformlar için mevcuttur) Komut satırı. Bu araç için bir şablon derleyici gibi bir dizi eklenti vardır, https://npmjs.org/package/grunt-contrib-jst . GitHub'daki belgelere bakın, https://github.com/gruntjs/grunt-contrib-jst . (Ayrıca düğüm paketi yöneticisini nasıl çalıştıracağınızı da anlamanız gerekecek, https://npmjs.org/ . Endişelenmeyin, inanılmaz derecede kolay ve çok yönlü.)

Daha sonra tüm şablonlarınızı ayrı html dosyalarında tutabilir, altçizgi kullanarak hepsini önceden derlemek için aracı çalıştırabilirsiniz (ki bunun JST eklentisi için bir bağımlılık olduğuna inanıyorum, ancak endişelenmeyin, düğüm paketi yöneticisi sizin için bağımlılıkları otomatik olarak yükleyecektir).

Bu, tüm şablonlarınızı tek bir komut dosyasında derler, örneğin

templates.js

Komut dosyasını yüklemek, bir işlev dizisi olan ve şu şekilde erişilebilen global - "JST" varsayılan olarak ayarlayacaktır:

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

hangisine benzer

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

bu komut dosyası etiketinin içeriğini (templates /) listView.html içine koyarsanız

Ancak asıl önemli olan şudur: Grunt, yerel grunt.js dosyanızda tanımladığınız dosyalardaki değişiklikleri temelde izleyen (temelde Grunt projeniz için javascript'te bir yapılandırma dosyasıdır) 'izle' adlı bu görevle birlikte gelir. ). Eğer homurdanırsanız, bu göreve şunu yazarak başlayın:

grunt watch

Komut satırından Grunt, dosyalarda yaptığınız tüm değişiklikleri izler ve yukarıda açıklanan jst görevi gibi değişiklikleri algılarsa, o grunt.js dosyasında onun için ayarladığınız tüm görevleri otomatik olarak yürütür . Dosyalarınızı düzenleyin ve ardından kaydedin ve tüm şablonlarınız, birkaç dizin ve alt dizine yayılmış olsalar bile, tek bir js dosyasında yeniden derlenir.

Benzer görevler, javascript'inizi linting yapmak, testleri çalıştırmak, betik dosyalarınızı birleştirmek ve küçültmek / küçültmek için yapılandırılabilir. Ve hepsi izleme görevine bağlanabilir, böylece dosyalarınızdaki değişiklikler otomatik olarak projenizin yeni bir 'yapısını' tetikler.

Bir şeyleri ayarlamak ve grunt.js dosyasının nasıl yapılandırılacağını anlamak biraz zaman alıyor, ancak bu iyi, harcanan zamana değer ve bir daha homurtu öncesi çalışma yöntemine geri döneceğinizi sanmıyorum


Favori cevap. Kabul edilen cevap bu olmalıdır. (benim değil)
Brian Genisio

Homurdanmak için güzel bir giriş noktası. Düz HTML için iyi çalışıyor ancak <% = price%> veya benzerine
sahipsem

Bu yaklaşımı beğeniyorum (JST kullanarak), ancak bunu yaparken sorun yaşıyorum:, template: JST['test.html']()verileri JST'den yüklüyor gibi görünmüyor :( (soruma buradan bakın: stackoverflow.com/questions/29723392/… )
timhc22

15

Sanırım bu size yardımcı olabilir. Çözümdeki her şey require.js, bir JavaScript dosyası ve modül yükleyici olan kitaplık etrafında dönüyor .

Yukarıdaki bağlantıdaki eğitim, bir omurga projesinin nasıl organize edilebileceğini çok güzel bir şekilde göstermektedir. Bir örnek uygulama da sağlanır. Bu yardımcı olur umarım.


3
Siteme referans için teşekkürler, arayan herkes için en iyi uygulamaları uygulamaya çalışan bir projeye başladım backboneboilerplate.com
Thomas Davis

4

Javascript şablonuyla ilgilenmeye başladım ve şimdi omurga ile ilk adımları atıyorum. Bulduğum şey bu ve oldukça iyi çalışıyor gibi görünüyor.

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

Senin üzerinde getfonksiyonu, muhtemelen döneceğini $.ajaxsizin şablon hemen yanıt vermez durumda böylece bir söz nesnesi döndüren bu yüzden kendisini.
Dennis Rongo

4

Benim için çalışmasını sağlamak için veri türünü "metin" olarak ayarlamam gerekiyordu:

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 kullanarak benim için çalışan bir çözüm buldum.

Alt çizgi şablon kodunu jQuery.load () yöntemi ile ana html dosyasına ekliyorum.

Oraya vardığında, şablonları oluşturmak için kullanıyorum. Her şeyin eşzamanlı olması gerekir!

Kavram şudur:

Alt çizgi harita şablon kodum var:

<!-- 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>

Ve bu kodu map-template.html adlı bir dosyaya koydum

Bundan sonra şablon dosyaları için bir sarmalayıcı oluşturuyorum.

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

Sonra bu dosyayı ana html dosyama böyle ekliyorum.

Kafada:

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

Şerefe.


1

Bu sorunun gerçekten eski olduğunu biliyorum ama alt çizgi ajax şablonları için yapılan bir google aramasının ilk sonucu olarak ortaya çıktı.

Bunun için iyi bir çözüm bulamamaktan yoruldum, bu yüzden kendi çözümümü yarattım:

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

AJAX kullanarak alt çizgi şablonlarını yüklemeye ek olarak, <% include%> işlevselliği ekler. Umarım birisi için faydalı olabilir.


0

JQuery'yi eşzamanlı olarak çalışmaya zorlamak biraz tedirgindi, bu yüzden önceki eşzamanlı örneği sözler kullanarak değiştirdim. Hemen hemen aynıdır, ancak eşzamansız olarak çalışır. Bu örnekte hbs şablonları kullanıyorum:

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;
};

Ardından işlenmiş html'yi kullanmak için:

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

NOT: Başkaları tarafından tartışıldığı gibi, tüm şablonları tek bir templates.js dosyasında derlemek ve web sayfası yüklendiğinde şablonları almak için çok sayıda küçük senkronize AJAX çağrısı yapmak yerine başlangıçta yüklemek tercih edilir.


0

İleri uyarı - İşte ejderhalar:

Aşağıda gösterilen yaklaşımı, ASP.NET yığınlarını (ve benzer çerçeveleri) js-libs ekosistemiyle uyumlu bir şekilde çalıştırmaya çalışanlara yardımcı olmak için söylüyorum. Bunun genel bir çözüm olmadığını söylememize gerek yok. Bunu söyledikten sonra ...

/ endforwardwarning

ASP.NET kullanıyorsanız, şablonlarınızı kendilerine ait bir veya daha fazla kısmi görünümün içine yerleştirerek dışa aktarabilirsiniz. .Cshtml dosyanızın içindeki Aka:

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

Template.cshtml dosyanızın içinde:

   // 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>

Ve şimdi şablonu her zamanki gibi kullanabilirsiniz:

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

Umarım bu anlaşılması zor olan yaklaşım, birinin bir saatlik kafa kaşınmadan kurtulmasına yardımcı olur.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.