Mẫu bên ngoài trong Dấu gạch dưới


121

Tôi sử dụng mẫu gạch dưới . Có thể đính kèm tệp bên ngoài làm mẫu không?

Trong Backbone View, tôi có:

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

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

Trong html của tôi là:

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

Nó hoạt động tốt. Nhưng tôi cần mẫu bên ngoài . Tôi thử:

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

hoặc là

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

hoặc là

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

nhưng nó đã không hoạt động.

Câu trả lời:


51

CHỈNH SỬA: Câu trả lời này đã cũ và lỗi thời. Tôi muốn xóa nó, nhưng đó là câu trả lời "được chấp nhận". Thay vào đó, tôi sẽ đưa ra ý kiến ​​của mình.

Tôi sẽ không ủng hộ việc này nữa. Thay vào đó, tôi sẽ tách tất cả các mẫu thành các tệp HTML riêng lẻ. Một số đề xuất tải các tệp này một cách không đồng bộ (Requi.js hoặc một bộ đệm ẩn mẫu). Điều đó hoạt động tốt trên các dự án nhỏ nhưng trên các dự án lớn với nhiều mẫu, bạn thấy mình thực hiện rất nhiều yêu cầu không đồng bộ nhỏ khi tải trang mà tôi thực sự không thích. (ugh ... được rồi, bạn có thể giải quyết vấn đề này với Requi.js bằng cách biên dịch trước các phụ thuộc ban đầu của bạn với r.js, nhưng đối với các mẫu, tôi vẫn cảm thấy điều này sai)

Tôi thích sử dụng tác vụ grunt (grunt-Contrib-jst) để biên dịch tất cả các mẫu HTML thành một tệp template.js duy nhất và bao gồm nó. Bạn nhận được những gì tốt nhất của tất cả các thế giới IMO ... các mẫu trực tiếp trong một tệp, việc biên dịch các mẫu nói trên diễn ra tại thời điểm xây dựng (không phải thời gian chạy) và bạn không có một trăm yêu cầu không đồng bộ nhỏ khi trang khởi động.

Mọi thứ bên dưới đều là rác

Đối với tôi, tôi thích sự đơn giản của việc bao gồm một tệp JS với mẫu của tôi. Vì vậy, tôi có thể tạo một tệp có tên view_template.js bao gồm mẫu dưới dạng một biến:

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

Sau đó, nó đơn giản như bao gồm tệp script như một tệp bình thường và sau đó sử dụng nó trong chế độ xem của bạn:

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

Tiến thêm một bước nữa, tôi thực sự sử dụng coffeescript, vì vậy mã của tôi thực sự trông giống như thế này hơn và tránh các ký tự thoát cuối dòng:

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

Việc sử dụng cách tiếp cận này sẽ tránh được việc làm sáng màu trong request.js khi nó thực sự không cần thiết.


46
cách tiếp cận này sẽ làm mất đi bất kỳ chức năng tô sáng cú pháp, định dạng lại và tái cấu trúc nào có sẵn với Ide. không biểu quyết mặc dù.
Kinjal Dixit

1
Tôi xin lỗi, nhưng tôi đã phải từ chối câu trả lời này. Nó rất khó hiểu vì nó sẽ vẫn giữ các tệp mẫu dưới dạng tệp kịch bản, chỉ hơi bị buộc phải trông giống như các mẫu. Các mẫu cần phải là các mẫu vì vậy nếu bạn phải đưa vào Request.js hoặc sử dụng giải pháp tuyệt vời của koorchik bên dưới, tôi cảm thấy nó chắc chắn đáng giá.
Tommi Forsström

3
@ TommiForsström Tôi đồng ý. Tôi đã tránh xa cách tiếp cận này. Chà! 04 Tháng Mười Hai 2011 là một thời gian rất dài trước trong thế giới phát triển Backbone.js :)
Brian Genisio

Trên thực tế, tôi muốn xóa câu trả lời này nhưng tôi không thể vì nó là câu trả lời được chấp nhận. Nó đã lỗi thời và có nhiều giải pháp tốt hơn thế này. Hôm nay, tôi sẽ có chúng dưới dạng các tệp mẫu riêng biệt và sử dụng tác vụ grunt (JST, chẳng hạn) để xây dựng chúng thành một tệp template.js riêng biệt để tránh tính chất không đồng bộ của việc tìm nạp tất cả chúng riêng lẻ. Đó là cách tốt nhất của cả hai phương pháp IMO.
Brian Genisio

tốt nếu không có nhiều mẫu, tôi nghĩ giải pháp cũ thực sự là hiệu quả nhất.
silkAdmin

107

Đây là một giải pháp đơn giản:

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

Sử dụng "async: false" ở đây không phải là một cách tồi vì trong mọi trường hợp, bạn phải đợi cho đến khi mẫu được tải.

Vì vậy, hàm "kết xuất"

  1. cho phép bạn lưu trữ từng mẫu trong tệp html riêng biệt trong dir tĩnh
  2. rất nhẹ
  3. biên dịch và lưu trữ mẫu
  4. tóm tắt logic tải mẫu. Ví dụ: trong tương lai, bạn có thể sử dụng các mẫu được tải trước và biên dịch trước.
  5. rất dễ sử dụng

[Tôi đang chỉnh sửa câu trả lời thay vì để lại nhận xét vì tôi tin rằng điều này là quan trọng.]

nếu các mẫu không hiển thị trong ứng dụng gốc và bạn thấy HIERARCHY_REQUEST_ERROR: DOM Exception 3, hãy xem câu trả lời của Dave Robinson cho Điều gì chính xác có thể gây ra "HIERARCHY_REQUEST_ERR: DOM Exception 3" -Error?.

Về cơ bản, bạn phải thêm

dataType: 'html'

với yêu cầu $ .ajax.


3
@BinaryNights - chúng ta có nên luôn thêm dataType: 'html'vào yêu cầu ajax của mình không?
Matt

Điều này cũng hoạt động cho các chế độ xem lồng nhau? Rõ ràng là tôi không thể làm cho nó hoạt động nếu một chế độ xem đề cập đến một chế độ xem khác.
T. Rossi

1
Có, nó cũng sẽ hoạt động cho các mẫu lồng nhau. Chỉ cần thêm làm helper và gọi nó là như sau: <% = render ( 'nested_template', dữ liệu)%>
koorchik

Xin chào, bạn có thể giải thích thêm một chút về "mẫu biên dịch và bộ nhớ đệm" không? Khi tôi cố gắng gọi hàm kết xuất, nó không thêm tmpl_data để trả về giá trị, nó chỉ truyền nó giống như vậy. Tôi đã phải gọi phương thức "Handlebars.compile" sau đó. Cảm ơn bạn.
cdagli

18

Mixin này cho phép bạn làm mẫu bên ngoài bằng gạch theo cách rất đơn giản: _.templateFromUrl(url, [data], [settings]). API phương thức gần giống như _.template () của Underscore . Đã bao gồm bộ nhớ đệm.

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

Sử dụng:

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

2
Thực sự tốt đẹp mixin nhỏ ở đó rất gọn gàng! :) cổ vũ cho việc chia sẻ
Nick White

D rất tuyệt, đây là loại giải pháp tôi đang tìm kiếm. và tôi nghĩ có thể được sử dụng để giữ một tập hợp các mẫu riêng tư.
bigmadwolf

@abhi nó được cung cấp trong câu trả lời. Ngoài ra, bạn cần jQuery để tải mẫu, nhưng bạn có thể viết lại một phần mã tải mẫu qua AJAX theo sở thích của mình bằng bất kỳ thư viện nào khác.
Dmitriy

@Dmitriy async: false không được dùng nữa, vì vậy nếu tôi gọi với ngoài tham số async thì nó không hoạt động, tôi nghĩ điều này là do theo mặc định async là true, có nghĩa là gọi syncronisilly, vậy bạn có giải pháp cho vấn đề này không
abhi

@abhi, nó hoạt động cho jQuery 1. * Cũng xem câu trả lời này stackoverflow.com/a/11755262/541961
Dmitriy

17

Tôi không muốn sử dụng request.js cho tác vụ đơn giản này, vì vậy tôi đã sử dụng giải pháp của koorchik đã sửa đổi.

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

Tại sao phải nối các mẫu vào tài liệu, thay vì lưu trữ chúng trong đối tượng javascript? Bởi vì trong phiên bản sản xuất, tôi muốn tạo tệp html với tất cả các mẫu đã được bao gồm, vì vậy tôi sẽ không cần thực hiện bất kỳ yêu cầu ajax nào khác. Đồng thời, tôi sẽ không cần phải tái cấu trúc lại mã của mình, khi tôi sử dụng

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

trong quan điểm Backbone của tôi.


1
Sử dụng điều này cũng rất tốt cho bối cảnh mà tôi đang cố gắng sử dụng Jasmine cho TDD và muốn thử nghiệm các mẫu trước khi tôi triển khai requestjs và plugin textjs của nó. Well done @Tramp
Nicholas Murray

Lệnh gọi đến $ .ajax là không đồng bộ, bất kỳ thứ gì tùy thuộc vào kết quả, sẽ được thực thi trong phương thức done của lời hứa được trả về.
JoshRoss

Cảm ơn vì điều đó. Tôi đã sử dụng nó. Một gợi ý: không có lý do gì để nối thêm dưới dạng thẻ tập lệnh - chỉ có thể tiếp tục và chuyển đổi sang một mẫu và giữ nó ở dạng băm tra cứu. Đây là một ví dụ fiddle (không chức năng): jsfiddle.net/PyzeF
webnesto

async: falsebị phản đối tại
ProblemsOfSumit

async: falsekhông được dùng nữa nên tôi đã cải thiện câu trả lời bằng cách thêm lệnh completegọi lại.
Alexander

16

Điều này có thể hơi lạc đề, nhưng bạn có thể sử dụng Grunt (http://gruntjs.com/) - chạy trên node.js (http://nodejs.org/, có sẵn cho tất cả các nền tảng chính) để chạy các tác vụ từ dòng lệnh. Có rất nhiều plugin cho công cụ này, chẳng hạn như trình biên dịch mẫu, https://npmjs.org/package/grunt-contrib-jst . Xem tài liệu trên GitHub, https://github.com/gruntjs/grunt-contrib-jst . (Bạn cũng sẽ cần hiểu cách chạy trình quản lý gói nút, https://npmjs.org/ . Đừng lo, nó cực kỳ dễ dàng và linh hoạt.)

Sau đó, bạn có thể giữ tất cả các mẫu của mình trong các tệp html riêng biệt, chạy công cụ để biên dịch trước tất cả chúng bằng cách sử dụng dấu gạch dưới (tôi tin rằng đây là phần phụ thuộc cho plugin JST, nhưng đừng lo, trình quản lý gói nút sẽ tự động cài đặt các phần phụ thuộc cho bạn).

Điều này biên dịch tất cả các mẫu của bạn thành một tập lệnh, giả sử

templates.js

Việc tải tập lệnh sẽ đặt toàn cục - "JST" theo mặc định - là một mảng các hàm và có thể được truy cập như vậy:

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

tương tự như

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

nếu bạn đặt nội dung của thẻ script đó trong (template /) listView.html

Tuy nhiên, người khởi xướng thực sự là thế này: Grunt đi kèm với tác vụ này được gọi là 'watch', về cơ bản sẽ theo dõi các thay đổi đối với các tệp mà bạn đã xác định trong tệp grunt.js cục bộ của mình (về cơ bản là tệp cấu hình cho dự án Grunt của bạn, trong javascript ). Nếu bạn khó chịu, hãy bắt đầu nhiệm vụ này cho bạn, bằng cách nhập:

grunt watch

từ dòng lệnh, Grunt sẽ theo dõi tất cả các thay đổi bạn thực hiện đối với tệp và tự động thực hiện tất cả các tác vụ mà bạn đã thiết lập cho nó trong tệp grunt.js đó nếu nó phát hiện thay đổi - như tác vụ jst được mô tả ở trên. Chỉnh sửa và sau đó lưu các tệp của bạn và tất cả các mẫu của bạn được biên dịch lại thành một tệp js, ngay cả khi chúng được dàn trải trên một số thư mục và thư mục con.

Các tác vụ tương tự có thể được định cấu hình để vẽ javascript của bạn, chạy các bài kiểm tra, nối và giảm thiểu / làm mờ các tệp script của bạn. Và tất cả đều có thể được gắn với nhiệm vụ xem, vì vậy các thay đổi đối với tệp của bạn sẽ tự động kích hoạt 'bản dựng' mới của dự án của bạn.

Cần một chút thời gian để thiết lập mọi thứ và hiểu cách định cấu hình tệp grunt.js, nhưng nó tốt, rất đáng để đầu tư thời gian và tôi không nghĩ rằng bạn sẽ quay lại cách làm việc như trước


Câu trả lời yêu thích. Đây phải là câu trả lời được chấp nhận. (không phải của tôi)
Brian Genisio

Điểm vào tốt để càu nhàu. Nó hoạt động tốt đối với HTML thuần túy nhưng nếu tôi có <% = price%> hoặc tương tự thì tôi nhận được: bất ngờ mã thông báo =, không thể biên dịch từ grunt
mcktimo

Tôi thích cách tiếp cận này (sử dụng JST), ngoại trừ tôi đang gặp sự cố khi thực hiện điều này template: JST['test.html']():, có vẻ như nó không tải dữ liệu từ JST :( (xem câu hỏi của tôi tại đây: stackoverflow.com/questions/29723392/… )
timhc22

15

Tôi nghĩ rằng đây là những gì có thể giúp bạn. Mọi thứ trong giải pháp đều xoay quanh require.jsthư viện là một tệp JavaScript và bộ tải mô-đun.

Hướng dẫn tại liên kết trên cho thấy rất độc đáo về cách một dự án xương sống có thể được tổ chức. Một triển khai mẫu cũng được cung cấp. Hi vọng điêu nay co ich.


3
Cảm ơn vì đã tham khảo trang web của tôi, đối với bất kỳ ai đang tìm hiểu, tôi đã bắt đầu một dự án cố gắng triển khai các phương pháp hay nhất backboneboilerplate.com
Thomas Davis

4

Tôi quan tâm đến javascript templating và bây giờ tôi đang thực hiện những bước đầu tiên với backbone. Đây là những gì tôi đã nghĩ ra và có vẻ hoạt động khá tốt.

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

Trên gethàm của bạn , tôi có thể sẽ trả về $.ajaxchính nó để nó trả về một đối tượng hứa hẹn, trong trường hợp mẫu của bạn không phản hồi ngay lập tức.
Dennis Rongo

4

Tôi đã phải đặt kiểu dữ liệu thành "văn bản" để làm cho nó hoạt động với tôi:

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

Tôi đã tìm thấy một giải pháp phù hợp với mình bằng cách sử dụng jQuery.

Tôi thêm mã mẫu dấu gạch dưới, với phương thức jQuery.load (), vào tệp html chính.

Khi nó ở đó, tôi đang sử dụng nó để tạo các mẫu. Tất cả cần phải diễn ra đồng bộ!

Khái niệm là:

Tôi có mã mẫu bản đồ gạch dưới:

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

Và tôi đặt mã đó trong một tệp có tên là map-template.html

Sau đó, tôi tạo aa wrapper cho các tệp mẫu.

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

Sau đó, tôi đưa tệp đó vào tệp html chính của mình như vậy.

Trong đầu:

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

Chúc mừng.


1

Tôi biết câu hỏi này thực sự cũ nhưng nó được đưa ra như là kết quả đầu tiên trên google tìm kiếm các mẫu ajax gạch dưới.

Tôi cảm thấy mệt mỏi vì không tìm ra giải pháp tốt cho việc này nên tôi đã tạo ra của riêng mình:

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

Ngoài việc tải các mẫu dấu gạch dưới bằng AJAX, nó còn thêm chức năng <% include%>. Tôi hy vọng nó có thể hữu ích cho ai đó.


0

Tôi hơi khó chịu khi buộc jQuery hoạt động đồng bộ, vì vậy tôi đã sửa đổi ví dụ đồng bộ trước đó bằng cách sử dụng hứa hẹn. Nó khá giống nhau, nhưng chạy không đồng bộ. Tôi đang sử dụng các mẫu hbs trong ví dụ này:

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

Sau đó, để sử dụng html được kết xuất:

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

LƯU Ý: Như những người khác đã thảo luận, tốt hơn là nên biên dịch tất cả các mẫu thành một tệp template.js duy nhất và tải tệp đó ngay từ đầu hơn là có nhiều lệnh gọi AJAX đồng bộ nhỏ để nhận các mẫu khi tải trang web.


0

Cảnh báo phía trước - Đây là những con rồng:

Tôi đề cập đến cách tiếp cận được hiển thị bên dưới chỉ đơn giản là để giúp những người đang gặp khó khăn trong việc làm cho các ngăn xếp ASP.NET (và các khuôn khổ tương tự) hoạt động hài hòa với hệ sinh thái của js-libs. Không cần phải nói rằng đây không phải là một giải pháp chung chung. Có nói rằng ...

/ endendwarning

Nếu bạn đang sử dụng ASP.NET, bạn có thể ngoại hóa các mẫu của mình chỉ bằng cách đặt chúng vào bên trong một hoặc nhiều chế độ xem một phần của chúng. Aka bên trong .cshtml của bạn:

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

Bên trong template.cshtml của bạn:

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

Và bây giờ bạn có thể sử dụng mẫu như bình thường:

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

Hy vọng cách tiếp cận rõ ràng và rõ ràng này sẽ giúp ai đó tiết kiệm được một giờ vò đầu.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.