Modello esterno in trattino basso


121

Uso il modello di sottolineatura . È possibile allegare un file esterno come modello ?

In Backbone View ho:

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

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

Nel mio html c'è:

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

Funziona bene. Ma ho bisogno di un modello esterno . Provo:

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

o

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

o

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

ma non ha funzionato.

Risposte:


51

EDIT: questa risposta è vecchia e obsoleta. Lo cancellerei, ma è la risposta "accettata". Inietterò invece la mia opinione.

Non avrei più sostenuto di farlo. Invece, separerei tutti i modelli in singoli file HTML. Alcuni suggeriscono di caricarli in modo asincrono (Require.js o una sorta di cache dei modelli). Funziona bene su piccoli progetti ma su progetti di grandi dimensioni con molti modelli, ti ritrovi a fare un sacco di piccole richieste asincrone al caricamento della pagina che non mi piacciono davvero. (ugh ... ok, puoi aggirare il problema con Require.js precompilando le tue dipendenze iniziali con r.js, ma per i modelli, questo mi sembra ancora sbagliato)

Mi piace usare un'attività grunt (grunt-contrib-jst) per compilare tutti i modelli HTML in un unico file templates.js e includerlo. Ottieni il meglio di tutti i mondi IMO ... i modelli vivono in un file, la compilazione di tali modelli avviene in fase di compilazione (non in runtime) e non hai cento minuscole richieste asincrone all'avvio della pagina.

Tutto quello che c'è sotto è spazzatura

Per me, preferisco la semplicità di includere un file JS con il mio modello. Quindi, potrei creare un file chiamato view_template.js che include il modello come variabile:

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

Quindi, è semplice come includere il file di script come uno normale e quindi utilizzarlo nella tua vista:

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

Facendo un ulteriore passo avanti, in realtà uso coffeescript, quindi il mio codice sembra effettivamente più simile a questo ed evito i caratteri di escape di fine riga:

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

L'utilizzo di questo approccio evita il brining in require.js dove non è realmente necessario.


46
questo approccio perderebbe qualsiasi funzione di evidenziazione della sintassi, riformattazione e refactoring disponibile con ide. non voto però.
Kinjal Dixit

1
Mi dispiace, ma ho dovuto votare negativamente questa risposta. È orribilmente goffo in quanto manterrà i file modello come file di script, solo un po 'costretti a sembrare modelli. I modelli devono essere modelli, quindi se devi portare Require.js o utilizzare la brillante soluzione di koorchik di seguito, penso che ne valga decisamente la pena.
Tommi Forsström

3
@ TommiForsström Sono d'accordo. Mi sono allontanato da questo approccio. Wow! Il 4 dicembre 2011 è davvero tanto tempo fa nel mondo dello sviluppo di Backbone.js :)
Brian Genisio

In realtà, vorrei eliminare questa risposta ma non posso perché è la risposta accettata. È obsoleto e ci sono soluzioni molto migliori di questa. Oggi, li avrei come file modello separati e utilizzerei un'attività grugnita (JST, ad esempio) per crearli in un file templates.js separato per evitare la natura asincrona di recuperarli tutti individualmente. È il migliore di entrambi i mondi approccio IMO.
Brian Genisio

beh, se non ci sono molti modelli penso che la prima soluzione sia davvero la più efficiente.
silkAdmin

107

Ecco una semplice soluzione:

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

Usare "async: false" qui non è un brutto modo perché in ogni caso devi aspettare che il template venga caricato.

Quindi, funzione "render"

  1. consente di memorizzare ogni modello in un file html separato nella directory statica
  2. è molto leggero
  3. compila e memorizza nella cache i modelli
  4. logica di caricamento del modello abstract. Ad esempio, in futuro potrai utilizzare modelli precaricati e precompilati.
  5. è facile da usare

[Sto modificando la risposta invece di lasciare un commento perché credo che sia importante.]

se i modelli non vengono visualizzati nell'app nativa e vedi HIERARCHY_REQUEST_ERROR: DOM Exception 3, guarda la risposta di Dave Robinson a Cosa può causare esattamente un errore "HIERARCHY_REQUEST_ERR: DOM Exception 3"? .

Fondamentalmente, devi aggiungere

dataType: 'html'

alla richiesta $ .ajax.


3
@BinaryNights - dovremmo sempre aggiungere dataType: 'html'alla nostra richiesta ajax, per ogni evenienza?
Matt

Funziona anche per le viste nidificate? Apparentemente non posso farlo funzionare se una vista fa riferimento a un'altra vista.
T. Rossi

1
Sì, dovrebbe funzionare anche per i modelli nidificati. Basta aggiungere l'helper di rendering e chiamarlo come: <% = render ('nested_template', data)%>
koorchik

Ciao, potresti spiegare un po 'di più su "modelli di compilazione e cache"? Quando ho provato a chiamare la funzione di rendering, non ha aggiunto tmpl_data per restituire il valore, lo ha semplicemente passato così com'è. Dopodiché ho dovuto chiamare il metodo "Handlebars.compile". Grazie.
cdagli

18

Questo mixin consente di effettuare il rendering modello esterno utilizzando sottolineatura in modo molto semplice: _.templateFromUrl(url, [data], [settings]). L'API del metodo è quasi la stessa di _.template () di Underscore . Memorizzazione nella cache inclusa.

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

2
Piccolo mixin davvero carino lì molto pulito! :) applausi per la condivisione
Nick White

Molto interessante D, questo era il tipo di soluzione che stavo cercando. e penso che potrebbe essere utilizzato per mantenere privato un set di modelli.
bigmadwolf

@abhi è fornito nella risposta. Inoltre, è necessario jQuery per caricare il modello, ma è possibile riscrivere parte del codice che carica il modello tramite AJAX secondo i propri gusti utilizzando qualsiasi altra libreria.
Dmitriy

@Dmitriy async: false è deprecato, quindi se chiamo senza il parametro async non funziona, penso che ciò sia perché per impostazione predefinita async è vero che significa chiamare syncronisilly, quindi hai una soluzione per questo problema
abhi

@abhi, funziona per jQuery 1. * Vedi anche questa risposta stackoverflow.com/a/11755262/541961
Dmitriy

17

Non volevo usare require.js per questa semplice operazione, quindi ho usato la soluzione modificata di 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
    }
});

Perché aggiungere modelli al documento, invece di memorizzarli nell'oggetto javascript? Perché nella versione di produzione vorrei generare file html con tutti i modelli già inclusi, quindi non avrò bisogno di fare ulteriori richieste ajax. E allo stesso tempo non avrò bisogno di fare alcun refactoring nel mio codice, come uso

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

nelle mie visualizzazioni Backbone.


1
Usando anche questo, è ottimo per lo scenario in cui cerco di usare Jasmine per TDD e desidero testare i modelli prima di aver implementato requirejs e il suo plugin textjs. Ben fatto @Tramp
Nicholas Murray,

La chiamata a $ .ajax è asincrona, qualsiasi cosa a seconda dei risultati, dovrebbe essere eseguita all'interno del metodo done della promessa restituita.
JoshRoss

Grazie per questo. L'ho usato. Un suggerimento: nessun motivo per aggiungere come tag di script: potrebbe semplicemente andare avanti e convertire in un modello e tenerlo in un hash di ricerca. Ecco un esempio di violino (non funzionante): jsfiddle.net/PyzeF
webnesto

async: falseè deprecato ora
ProblemsOfSumit

Poiché async: falseè deprecato, ho migliorato la risposta aggiungendo la completerichiamata.
Alexander

16

Questo potrebbe essere leggermente fuori tema, ma potresti usare Grunt (http://gruntjs.com/), che gira su node.js (http://nodejs.org/, disponibile per tutte le principali piattaforme) per eseguire attività dal riga di comando. Ci sono un sacco di plugin per questo strumento, come un compilatore di modelli, https://npmjs.org/package/grunt-contrib-jst . Consulta la documentazione su GitHub, https://github.com/gruntjs/grunt-contrib-jst . (Dovrai anche capire come eseguire il gestore di pacchetti del nodo, https://npmjs.org/ . Non preoccuparti, è incredibilmente facile e versatile.)

Puoi quindi mantenere tutti i tuoi modelli in file html separati, eseguire lo strumento per precompilarli tutti usando il carattere di sottolineatura (che credo sia una dipendenza per il plugin JST, ma non preoccuparti, il gestore di pacchetti del nodo installerà automaticamente le dipendenze per te).

Questo compila tutti i tuoi modelli in uno script, diciamo

templates.js

Il caricamento dello script imposterà un globale - "JST" per impostazione predefinita - che è un array di funzioni, ed è accessibile in questo modo:

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

che sarebbe simile a

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

se metti il ​​contenuto di quel tag script in (templates /) listView.html

Tuttavia, il vero kicker è questo: Grunt viene fornito con questa attività chiamata 'watch', che fondamentalmente monitorerà le modifiche ai file che hai definito nel tuo file grunt.js locale (che è fondamentalmente un file di configurazione per il tuo progetto Grunt, in javascript ). Se hai grugnito avvia questa attività per te, digitando:

grunt watch

dalla riga di comando, Grunt monitorerà tutte le modifiche apportate ai file ed eseguirà automaticamente tutte le attività che hai configurato in quel file grunt.js se rileva modifiche, come l' attività jst descritta sopra. Modifica e salva i tuoi file e tutti i tuoi modelli si ricompilano in un unico file js, anche se sono distribuiti su un certo numero di directory e sottodirectory.

Attività simili possono essere configurate per lintare il tuo javascript, eseguire test, concatenare e minimizzare / umiliare i tuoi file di script. E tutto può essere collegato all'attività di controllo, quindi le modifiche ai file attiveranno automaticamente una nuova "build" del progetto.

Ci vuole un po 'di tempo per impostare le cose e capire come configurare il file grunt.js, ma ne è valsa la pena, e non credo che tornerai mai a un modo di lavorare pre-grunt


Risposta preferita. Questa dovrebbe essere la risposta accettata. (non mio)
Brian Genisio

Bel punto di ingresso per grugnire. Funziona bene per HTML semplice ma se ho <% = price%> o simile ottengo: token imprevisto =,
impossibile

Mi piace questo approccio (usando JST), tranne che ho problemi a farlo template: JST['test.html']():, non sembra caricare i dati da JST :( (vedi la mia domanda qui: stackoverflow.com/questions/29723392/… )
timhc22

15

Penso che questo sia ciò che potrebbe aiutarti. Tutto nella soluzione ruota attorno alla require.jslibreria che è un file JavaScript e un caricatore di moduli.

Il tutorial al link sopra mostra molto bene come potrebbe essere organizzato un progetto backbone. Viene fornita anche un'implementazione di esempio . Spero che questo ti aiuti.


3
Grazie per il riferimento al mio sito, per chi cerca ho avviato un progetto che cerca di implementare le migliori pratiche backboneboilerplate.com
Thomas Davis

4

Mi sono interessato ai modelli javascript e ora sto facendo i primi passi con backbone. Questo è quello che mi è venuto in mente e sembra funzionare abbastanza bene.

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

Sulla tua getfunzione, probabilmente restituirei lo $.ajaxstesso in modo che restituisca un oggetto di promessa, quindi nel caso in cui il tuo modello non risponda immediatamente.
Dennis Rongo

4

Ho dovuto impostare il tipo di dati su "testo" per farlo funzionare per me:

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

Ho trovato una soluzione che funziona per me utilizzando jQuery.

Aggiungo il codice del modello di sottolineatura, con il metodo jQuery.load (), al file html principale.

Una volta che è lì, lo sto usando per generare i modelli. Tutto deve avvenire in modo sincrono!

Il concetto è:

Ho un codice modello di mappa con trattino basso:

<!-- 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 ho inserito quel codice in un file chiamato map-template.html

Successivamente creo un wrapper per i file modello.

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

Quindi includo quel file nel mio file html principale in questo modo.

In testa:

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

Saluti.


1

So che questa domanda è davvero vecchia, ma è emersa come il primo risultato di una ricerca su Google per i modelli ajax di sottolineatura.

Ero stanco di non trovare una buona soluzione per questo, quindi ho creato il mio:

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

Oltre a caricare i modelli di sottolineatura utilizzando AJAX, aggiunge la funzionalità <% include%>. Spero possa essere utile a qualcuno.


0

Ero un po 'a disagio nel forzare jQuery a funzionare in modo sincrono, quindi ho modificato il precedente esempio sincrono usando le promesse. È più o meno lo stesso, ma funziona in modo asincrono. Sto usando i modelli hbs in questo esempio:

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

Quindi per utilizzare l'html renderizzato:

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: come discusso da altri, sarebbe preferibile compilare tutti i modelli in un unico file templates.js e caricarlo all'inizio piuttosto che avere molte piccole chiamate AJAX sincrone per ottenere i modelli quando viene caricata la pagina web.


0

Avviso in avanti: ecco i draghi:

Cito l'approccio mostrato di seguito semplicemente per aiutare coloro che lottano per far funzionare gli stack ASP.NET (e framework simili) in modo armonioso con l'ecosistema di js-libs. Inutile dire che questa non è una soluzione generica. Avendolo detto ...

/ endforwardwarning

Se utilizzi ASP.NET puoi esternalizzare i tuoi template semplicemente inserendoli all'interno di una o più viste parziali proprie. Alias ​​all'interno del tuo .cshtml:

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

All'interno del tuo 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 ora puoi usare il modello come al solito:

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

Spero che questo approccio elusivamente ovvio aiuti qualcuno a risparmiare un'ora di grattacapi.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.