ti-enxame.com

Modelo Externo no Sublinhado

Eu uso modelo Underscore . É possível anexar um arquivo externo como modelo ?

Em Backbone View eu tenho:

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

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

No meu html é:

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

Isso funciona bem. Mas eu preciso de template externo . Eu tento:

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

ou

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

ou

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

mas não funcionou.

120
Tomáš

EDIT: Esta resposta é antiga e desatualizada. Eu iria apagá-lo, mas é a resposta "aceite". Vou injetar a minha opinião em vez disso.

eu não defendo mais isso. Em vez disso, eu separaria todos os modelos em arquivos HTML individuais. Alguns sugeririam o carregamento de forma assíncrona (Require.js ou um tipo de cache de modelo). Isso funciona bem em pequenas projetos, mas em grandes projetos com muitos modelos, você se encontra fazendo uma tonelada de pequenas solicitações assíncronas no carregamento da página que eu realmente não gosto. (ugh ... ok, você pode contornar isso com o Require.js pré-compilando sua inicial dependências com r.js, mas para modelos, isso ainda parece errado para mim)

Eu gosto de usar uma tarefa grunhida (grunt-contrib-jst) para compilar todos os templates HTML em um único arquivo templates.js e incluí-lo. Você obtém o melhor de todos os mundos IMO ... templates live em um arquivo, a compilação dos modelos ocorre no momento da compilação (não no tempo de execução), e você não tem cem pequenas solicitações assíncronas quando a página é iniciada.

Tudo abaixo é lixo

Para mim, prefiro a simplicidade de incluir um arquivo JS em meu modelo. Então, eu poderia criar um arquivo chamado view_template.js, que inclui o modelo como uma variável:

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

Então, é tão simples quanto incluir o arquivo de script como um normal e usá-lo na sua opinião:

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

Dando um passo adiante, eu realmente uso o coffeescript, então meu código realmente se parece mais com isso e evita os caracteres de escape de fim de linha:

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

Usando esta abordagem evita salga em require.js onde realmente não é necessário.

51
Brian Genisio

Aqui está uma solução simples:

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

Usar "async: false" aqui não é um mau caminho, pois em qualquer caso você deve esperar até que o template seja carregado.

Então, função "render"

  1. permite que você armazene cada modelo em arquivo html separado no diretório estático
  2. é muito leve
  3. compila e armazena em cache modelos
  4. abstrai a lógica de carregamento do modelo. Por exemplo, no futuro, você pode usar modelos pré-carregados e pré-compilados.
  5. é fácil de usar

[Estou editando a resposta em vez de deixar um comentário porque acredito que isso seja importante.]

se os modelos não estão aparecendo em aplicativo nativo , e você vê HIERARCHY_REQUEST_ERROR: DOM Exception 3, veja a resposta de Dave Robinson para O que exatamente pode causar um "HIERARCHY_REQUEST_ERR: DOM Exception 3" -Error? .

Basicamente, você deve adicionar

dataType: 'html'

para o pedido $ .ajax.

107
koorchik

Este mixin permite que você renderize um template externo usando Sublinhado de maneira muito simples: _.templateFromUrl(url, [data], [settings]). API de método é quase o mesmo que Underscore _. Template () . Cache incluído.

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

Uso:

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

Eu não queria usar o require.js para essa tarefa simples, então usei a solução do koorchik modificado.

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

Por que acrescentar modelos ao documento, em vez de armazená-los em um objeto javascript? Como na versão de produção, gostaria de gerar o arquivo html com todos os modelos já incluídos, portanto, não precisarei fazer solicitações ajax adicionais. E ao mesmo tempo eu não precisarei fazer qualquer refatoração no meu código, como eu uso

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

nas minhas visualizações do Backbone.

17
Tyth

Isso pode ser um pouco fora do tópico, mas você pode usar o Grunt (http://gruntjs.com/) - que é executado em node.js (http://nodejs.org/, disponível para todas as principais plataformas) para executar tarefas do linha de comando. Há um monte de plugins para esta ferramenta, como um compilador de template, https://npmjs.org/package/grunt-contrib-jst . Veja a documentação no GitHub, https://github.com/gruntjs/grunt-contrib-jst . (Você também precisará entender como executar o gerenciador de pacotes do nó, https://npmjs.org/ . Não se preocupe, é incrivelmente fácil e versátil.)

Você pode então manter todos os seus templates em arquivos html separados, rodar a ferramenta para pré-compilar todos usando underscore (que eu acredito ser uma dependência do plugin JST, mas não se preocupe, o gerenciador de pacotes irá instalar dependências para você).

Isso compila todos os seus modelos para um script, digamos

templates.js

Carregar o script irá definir um global - "JST" por padrão - que é uma matriz de funções, e pode ser acessado da seguinte forma:

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

que seria semelhante a

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

se você colocar o conteúdo dessa tag de script em (templates /) listView.html

No entanto, o kicker real é o seguinte: O Grunt vem com essa tarefa chamada 'watch', que basicamente monitora alterações nos arquivos que você definiu no arquivo grunt.js local (que é basicamente um arquivo de configuração para o seu projeto Grunt, em javascript ). Se você tem grunhido, inicie esta tarefa para você, digitando:

grunt watch

a partir da linha de comando, o Grunt irá monitorar todas as alterações feitas nos arquivos e auto-executar todas as tarefas que você configurou para ele nesse arquivo grunt.js se detectar alterações - como o jst tarefa descrita acima. Edite e salve seus arquivos, e todos os seus modelos recompilarem em um arquivo js, ​​mesmo que estejam espalhados por vários diretórios e subdiretórios.

Tarefas semelhantes podem ser configuradas para linting seu javascript, executando testes, concatenando e diminuindo/uglifying seus arquivos de script. E tudo pode estar vinculado à tarefa de observação, de forma que as alterações nos seus arquivos automaticamente acionam uma nova 'construção' do seu projeto.

Demora algum tempo para definir as coisas e entender como configurar o arquivo grunt.js, mas bem, vale a pena investir tempo, e eu não acho que você vai voltar para uma maneira pré-grunhida de trabalhar

16
Mansiemans

Eu acho que isto é o que pode te ajudar. Tudo na solução gira em torno da biblioteca require.js, que é um arquivo JavaScript e um carregador de módulos.

O tutorial no link acima mostra muito bem como um projeto de backbone pode ser organizado. A implementação de amostra também é fornecido. Espero que isto ajude.

15
nayaab

Eu tive que definir o tipo de dados para "texto" para fazer o trabalho para mim:

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

Eu me interessei por modelos de javascript e agora estou dando os primeiros passos com o backbone. Isso é o que eu criei e parece funcionar muito bem.

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

Eu encontrei uma solução que funciona para mim com o uso de jQuery.

Eu adiciono o código de modelo de sublinhado, com o método jQuery.load (), ao arquivo html principal.

Uma vez lá, estou usando para gerar os templates. Tudo precisa acontecer de forma síncrona!

O conceito é:

Eu tenho um código de modelo de mapa de sublinhado:

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

E eu coloquei esse código em um arquivo chamado map-template.html

Depois disso, criei um wrapper para os arquivos de modelo.

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

Então eu incluo esse arquivo no meu arquivo html principal assim.

Na cabeça:

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

Felicidades.

2
Kaloyan Stamatov

Eu sei que esta pergunta é realmente antiga, mas surgiu como o primeiro resultado em uma pesquisa no google para sublinhar modelos de ajax.

Eu estava cansado de não encontrar uma boa solução para isso, então criei o meu próprio:

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

Além de carregar modelos de sublinhado usando o AJAX, ele adiciona a funcionalidade <% include%>. Espero que possa ser útil para alguém.

1
ziad-saab

Aviso para frente - aqui estão dragões:

Menciono a abordagem mostrada abaixo simplesmente para ajudar aqueles que lutam para fazer com que as pilhas ASP.NET (e estruturas similares) trabalhem harmoniosamente com o ecossistema de js-libs. Escusado será dizer que esta não é uma solução genérica. Tendo dito isto ...

/ endforwardwarning

Se você estiver usando o ASP.NET, poderá externalizar seus modelos simplesmente colocando-os dentro de uma ou mais visualizações parciais próprias. Aka dentro do seu .cshtml:

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

Dentro do seu 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>

E agora você pode usar o modelo como de costume:

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

Espero que essa abordagem elusivamente óbvia ajude alguém a economizar uma hora de raciocínio.

0
XDS

Eu estava um pouco desconfortável forçando o jQuery a funcionar de forma síncrona, então modifiquei o exemplo síncrono anterior usando promessas. É praticamente o mesmo, mas é executado de forma assíncrona. Estou usando modelos de hbs neste exemplo:

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

Então, para usar o html renderizado:

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

NOTA: Como discutido por outros, seria preferível compilar todos os modelos em um único arquivo templates.js e carregá-lo no início, em vez de ter muitas pequenas chamadas síncronas AJAX para obter modelos quando a página da Web é carregada.

0
Megatron