Underscoreの外部テンプレート


121

Underscoreテンプレートを使用しています。テンプレートとして外部ファイルを添付することは可能ですか?

バックボーンビューで私は:

 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または一種のテンプレートキャッシュ)。これは小さなプロジェクトではうまく機能しますが、たくさんのテンプレートがある大きなプロジェクトでは、ページのロード時に大量の小さな非同期リクエストを作成することに気づきます。(うーん...そう、r.jsを使用して初期の依存関係をプリコンパイルすることで、Require.jsで回避できますが、テンプレートの場合、これはまだ私には間違っていると感じています)

私はgruntタスク(grunt-contrib-jst)を使用してすべてのHTMLテンプレートを1つのtemplates.jsファイルにコンパイルし、それをインクルードするのが好きです。すべての世界で最高のIMOを利用できます...テンプレートはファイル内にあり、テンプレートのコンパイルは(ランタイムではなく)ビルド時に行われ、ページの起動時に100の小さな非同期要求はありません。

以下はすべてジャンク品です

私にとっては、JSファイルをテンプレートに含めるというシンプルさを好みます。したがって、テンプレートを変数として含むview_template.jsというファイルを作成する場合があります。

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

次に、スクリプトファイルを通常のファイルのようにインクルードし、ビューで使用するだけです。

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

さらに一歩踏み込んで、実際にはcoffeescriptを使用しているので、コードは実際には次のようになり、行末のエスケープ文字を避けています。

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

このアプローチを使用すると、require.jsが実際に不要な場所へのアクセスを回避できます。


46
このアプローチでは、IDEで使用可能な構文の強調表示、再フォーマット、リファクタリング機能が失われます。投票はしません。
Kinjal Dixit 2012

1
申し訳ありませんが、この回答に反対票を投じなければなりませんでした。テンプレートファイルをスクリプトファイルとして保持するため、ひどく不格好です。テンプレートはテンプレートである必要があるため、Require.jsを組み込むか、以下のkoorchikの優れたソリューションを使用する必要がある場合は、間違いなく価値があると思います。
TommiForsström2013年

3
@TommiForsström同意する。私はこのアプローチから離れました。うわー!2011年12月4日は、Backbone.js開発の世界でかなり昔のことです:)
Brian Genisio 2013年

実はこの回答を削除したいのですが、受け入れられた回答なので削除できません。それは時代遅れであり、これよりもはるかに優れた解決策があります。今日、それらを個別のテンプレートファイルとして用意し、グラントタスク(JSTなど)を使用してそれらを個別のtemplates.jsファイルに構築し、個別にすべてをフェッチするという非同期の性質を回避します。それはIMOアプローチの両方の世界で最高です。
Brian Genisio 2014年

テンプレートがあまりない場合は、前者のソリューションが本当に最も効率的だと思います。
SilkAdmin 2014年

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、Dave Robinsonによる「HIERARCHY_REQUEST_ERR:DOM Exception 3」エラーの正確な原因とは何ですか。

基本的に、追加する必要があります

dataType: 'html'

$ .ajaxリクエストに追加します。


3
@BinaryNights-念のdataType: 'html'ため、常にajaxリクエストに追加する必要がありますか?
Matt

これはネストされたビューでも機能しますか?どうやら、ビューが別のビューを参照している場合、それを機能させることはできません。
T.ロッシ

1
はい、ネストされたテンプレートでも機能するはずです。レンダリングヘルパーを追加して、次のように呼び出します。<%= render( 'nested_template'、data)%>
koorchik

こんにちは、「コンパイルとキャッシュのテンプレート」についてもう少し説明してもらえますか?レンダー関数を呼び出そうとしたとき、戻り値にtmpl_dataが追加されず、そのまま渡されました。その後、 "Handlebars.compile"メソッドを呼び出さなければなりませんでした。ありがとうございました。
cdagli 2015

18

このミックスインを使用すると、Underscoreを使用して非常に簡単な方法で外部テンプレートをレンダリングできます_.templateFromUrl(url, [data], [settings])。メソッドAPIは、アンダースコアの _.template()とほとんど同じです。キャッシングが含まれています。

_.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があります。:)共有を応援
Nick White

とてもクールなD、これは私が探していた種類のソリューションでした。そして、私はテンプレートのセットをプライベートに保つために使用できると思います。
bigmadwolf 14年

@abhi回答で提供されています。また、テンプレートを読み込むにはjQueryが必要ですが、他のライブラリを使用して、AJAX経由でテンプレートを読み込むコードの一部を好みに合わせて書き換えることができます。
Dmitriy 2014年

@Dmitriy非同期:偽が廃止されましたので、私が働いていないそのパラメータアウト非同期で呼び出した場合、私はデフォルトの非同期で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());

私のバックボーンビューで。


1
これも使用して、requirejsとそのtextjsプラグインを実装する前に、TDDにJasmineを使用してテンプレートをテストしたい場合のシーンリオに最適です。よくやった@Tramp
ニコラスマレー

$ .ajaxの呼び出しは非同期であり、結果に応じて、返されたpromiseのdoneメソッド内で実行する必要があります。
JoshRoss 2013

これをありがとう。使った。1つの提案:スクリプトタグとして追加する理由はありません-先に進んでテンプレートに変換し、それをルックアップハッシュに保持することができます。これは(機能しない)フィドルの例です: 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を参照してください。(また、ノードパッケージマネージャーの実行方法を理解する必要があります

次に、すべてのテンプレートを別々のhtmlファイルに保存し、ツールを実行して、アンダースコアを使用してすべてをプリコンパイルします(これはJSTプラグインの依存関係だと思いますが、ノードパッケージマネージャーは依存関係を自動インストールします)。

これにより、すべてのテンプレートが1つのスクリプトにコンパイルされます。

templates.js

スクリプトをロードすると、グローバル(デフォルトでは「JST」)が設定されます。これは関数の配列であり、次のようにアクセスできます。

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

これは次のようになります

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

そのスクリプトタグのコンテンツを(templates /)listView.htmlに配置した場合

ただし、実際のキッカーは次のとおりです。Gruntには「ウォッチ」と呼ばれるこのタスクが付属しており、ローカルのgrunt.jsファイルで定義したファイルへの変更を基本的に監視します(これは、JavaScriptで基本的にGruntプロジェクトの構成ファイルです) )。次のように入力して、このタスクを開始する必要がある場合:

grunt watch

コマンドラインから、Gruntはファイルに加えたすべての変更を監視し、上記のjstタスクのように変更を検出した場合、そのgrunt.jsファイルで設定したすべてのタスクを自動実行します。ファイルを編集して保存すると、テンプレートが複数のディレクトリやサブディレクトリに分散している場合でも、すべてのテンプレートが1つのjsファイルに再コンパイルされます。

JavaScriptファイルのリンティング、テストの実行、スクリプトファイルの連結と縮小/醜悪化のために、同様のタスクを構成できます。また、すべてを監視タスクに関連付けることができるため、ファイルを変更すると、プロジェクトの新しい「ビルド」が自動的にトリガーされます。

設定してgrunt.jsファイルを構成する方法を理解するには少し時間がかかりますが、これは投資する時間に見合うだけの価値があり、あなたが作業前の無作法な方法に戻ることはないと思います。


好きな答え。これは受け入れられる答えになるはずです。(私のものではない)
Brian Genisio 2014年

うなり声を上げるための素敵なエントリポイント。プレーンHTMLでは問題なく動作しますが、<%= price%>または同様のものを取得すると、予期しないトークン=が発生し、
うなり声

私は(JSTを使用して)このアプローチが好きなのです、私はこれをやって問題を抱えている以外: template: JST['test.html']()、それはJSTからのデータをロードしていないようです:((ここで私の質問を参照してください。stackoverflow.com/questions/29723392/...を
timhc22 2015

15

これがあなたの役に立つかもしれないと思います。ソリューションのすべてrequire.jsは、JavaScriptファイルおよびモジュールローダーであるライブラリを中心に展開します。

上記のリンクにあるチュートリアルは、バックボーンプロジェクトの編成方法を非常にうまく示しています。サンプル実装をも提供します。お役に立てれば。


3
私のサイトへの参照をありがとう、探している人のために私はベストプラクティスbackboneboilerplate.com
Thomas Davis

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それ自体を返すので、テンプレートがすぐに応答しない場合に備えてpromiseオブジェクトを返します。
デニスロンゴ2014

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

私はこの質問が本当に古いことを知っていますが、下線のajaxテンプレートのグーグル検索の最初の結果として出てきました。

私はこれに対する良い解決策を見つけるのに疲れていたので、自分で作成しました:

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

AJAXを使用してアンダースコアテンプレートをロードすることに加えて、<%include%>機能を追加します。誰かのお役に立てば幸いです。


0

jQueryを同期的に機能させるのが少し不安だったので、Promiseを使用して前の同期の例を変更しました。ほぼ同じですが、非同期で実行されます。この例では、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を使用している場合は、テンプレートを1つ以上の部分ビューの中に配置するだけで、テンプレートを外部化できます。.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" });

このとらえどころのない明白なアプローチが、誰かが1時間分の頭を悩ませる手助けになることを願っています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.