Gin: Menggunakan setHTMLTemplate di setiap penangan?

Dibuat pada 31 Mei 2015  ·  30Komentar  ·  Sumber: gin-gonic/gin

Hai,

Saya ingin tahu apakah menggunakan gin.Context.Engine.setHTMLTemplate di setiap penangan adalah cara yang benar / aman untuk menyiapkan template? (_nb_: templat yang mencoba menggunakan kembali elemen tata letak dan karenanya menggunakan define tidak dapat menimpa blok define sehingga untuk setiap penangan seseorang hanya perlu memuat templat tertentu).

question

Komentar yang paling membantu

@ al3d

package main

import (
    "html/template"

    "github.com/gin-gonic/gin"
    "github.com/gin-gonic/gin/render"
)

func main() {
    router := gin.Default()
    router.HTMLRender = createMyRender()
    router.GET("/", func(c *gin.Context) {
        c.HTML(200, "index", data)
    })
    router.Run(":8080")
}

type MyHTMLRender struct {
    templates map[string]*template.Template
}

func (r *MyHTMLRender) Add(name string, tmpl *template.Template) {
    if r.templates == nil {
        r.templates = make(map[string]*template.Template)
    }
    r.templates[name] = tmpl
}

func (r *MyHTMLRender) Instance(name string, data interface{}) render.Render {
    return render.HTML{
        Template: r.templates[name],
        Data:     data,
    }
}

func createMyRender() render.HTMLRender {
    r := &MyHTMLRender{}
    r.Add("index", template.Must(template.ParseGlob(pattern)))
    r.Add("login", template.Must(template.ParseGlob(pattern1)))
    r.Add("something", template.Must(template.ParseGlob(pattern2)))
    r.Add("another", template.Must(template.ParseGlob(pattern3)))

    return r
}

Apakah kamu mengerti? Anda dapat membuat render Anda sendiri dan masih menggunakan sintaks c.HTML () yang sangat bagus ...

Semua 30 komentar

@ al3xandru Context.Engine telah dihapus di Gin v1.0, saya sarankan Anda untuk memperbarui secepat mungkin. Ini menampilkan banyak peningkatan kinerja, keamanan dan stabilitas.

Memanggil setHTMLTemplate dari sebuah permintaan adalah ide yang sangat buruk karena SetHTMLTemplate tidak aman untuk thread.

Anda harus menginisialisasi semua template Anda di awal:

    tmpl := template.Must(template.ParseFiles(r.Files...))
    tmpl.ParseFiles("template1.html", "template2.html"...)
    router.SetHTMLTemplate(tmpl)

atau menggunakan:

//router.LoadHTMLFiles("template1.html", "template2.html")
router.LoadHTMLGlob("resources/*)

lalu gunakan saja:

func handler(c *gin.Context) {
    c.HTML(200, "template1.html", data)
}

gunakan apa pun yang Anda inginkan, tetapi jangan pernah mengubah konfigurasi router setelah memulai server.

@tokopedia

  1. terima kasih telah menyebutkan bahwa 1.0 sudah keluar. Saya tidak menyadarinya.
  2. Saya mungkin melewatkan sesuatu atau melakukan sesuatu yang sangat salah, tetapi saya tidak begitu mengerti bagaimana router.setHTMLTemplate akan bekerja.

Diberikan:

  • base.html : berisi tata letak (menggunakan define dan template )
  • page1.html : berisi "komponen" untuk halaman tertentu (pada dasarnya mengisi defines yang digunakan dalam base.html untuk template )
  • page2.html : sama seperti di atas

Anda tidak dapat memuat semua template ini sekaligus karena berisi duplikat define blok.

Apa yang saya lakukan salah?

_Update_: Saya lupa merujuk # 219 dan SHA: bee03fa7b0c9bf88ac211f

@ al3d
router.SetHTMLTemplate () menghubungkan template Anda dengan c.HTML ()

jadi, Anda hanya perlu menginisialisasi template Anda dan memberikannya ke gin.

template yang mencoba menggunakan kembali elemen tata letak dan karenanya menggunakan define tidak dapat menimpa blok define sehingga untuk setiap penangan seseorang hanya perlu memuat template tertentu).

Anda tidak harus menggunakan SetHTMLTemplate (), Anda dapat template.Execute(c.Writer, data) .
tetapi saya tidak merekomendasikannya, Anda harus merancang situs web Anda untuk menggunakan satu objek template.

https://golang.org/doc/articles/wiki/

Ada inefisiensi dalam kode ini: renderTemplate memanggil ParseFiles setiap kali halaman dirender. Pendekatan yang lebih baik adalah memanggil ParseFiles satu kali saat inisialisasi program, mengurai semua template menjadi satu * Template. Kemudian kita dapat menggunakan metode ExecuteTemplate untuk membuat template tertentu.

mungkin saya belum mengerti kamu.

@manucorporat jika saya membaca jawaban Anda dengan benar:

  1. Saya juga harus menggunakan solusi yang kurang optimal
  2. berhenti menggunakan templat yang dapat diperluas dan duplikat bagian umum di semua halaman berbeda untuk mengatasi batasan html/template

(_Catatan_: Saya tahu ini dapat menyebabkan perdebatan, tetapi saya benar-benar tidak dapat melihat bagaimana proyek berukuran layak sebenarnya dapat menggunakan satu template untuk semua halaman. Anggap saja sebagai contoh satu set halaman untuk manajemen akun, daftar konten, konten tunggal halaman, pembuatan konten, dll.)

Meskipun saya belum menggali ini terlalu jauh, memiliki addHTMLTemplate(name string, t *template.Template) akan menjadi solusi yang jauh lebih elegan (dan lebih mudah dirawat).

@ al3xandru no, Anda tidak perlu menduplikasinya. Template One Go dapat memiliki banyak template secara internal. itu seperti pohon.

{{ $title := "Page Title" }}
{{ template "head" $title }}
{{ template "checkout" }}
{{ template "top" }}

// I have ~3 different sidebar layouts that change according to the page
// front page, listing creation, payment page - either with some marketing
// fluff or a quick "FAQ" for the user. This is just one of the combinations.
{{ template "sidebar_details" . }}
{{ template "sidebar_payments" }}
{{ template "sidebar_bottom" }}

<div class="bordered-content">
    ...
    {{ template "listing_content" . }}
     ...
</div>

{{ template "footer"}}
{{ template "bottom" }}
router.LoadHTMLGlob("resources/templates/*")

resource / templates / *

  • common.tmpl.html
  • header.tmpl.html
  • index.tmpl.html
  • login.tmpl.html
  • dll.
func index(c *gin.Context) {
    c.HTML(200, "index.tmpl.html", data)
}

func index(c *gin.Context) {
    c.HTML(200, "login.tmpl.html", data)
}
//....

cepat, bersih, dan idiomatis.

one *template.Template artinya: simpan semua template Anda di bawah namespace yang sama

Meskipun saya belum menggali terlalu jauh, memiliki addHTMLTemplate (string nama, t * template.Template) akan menjadi solusi yang jauh lebih elegan (dan lebih mudah dirawat).

yang sudah ada di * template.Template

@manucorporat ini hanya bekerja jika ada satu dan hanya satu 1 definisi untuk setiap "template"

Silakan lihat di https://elithrar.github.io/article/approximating-html-template-inheritance/ dan http://www.reddit.com/r/golang/comments/27ls5a/includes_htmltemplate_snippets_is_there_a_better/

Sementara itu saya akan mencoba untuk melihat apakah solusi di atas itu adalah sesuatu yang benar-benar dapat saya lakukan di pihak saya.

@ al3xandru ok, saya tidak pernah punya masalah dengan desain saat ini.
Halaman mengimpor header, footer ...
dan Anda ingin:
Impor dasar Halaman

Anda dapat membuat render HTML Anda sendiri menggunakan template peta [string] *. Template.

Izinkan saya 10 menit untuk menulis sesuatu untuk Anda ...

@ al3d

package main

import (
    "html/template"

    "github.com/gin-gonic/gin"
    "github.com/gin-gonic/gin/render"
)

func main() {
    router := gin.Default()
    router.HTMLRender = createMyRender()
    router.GET("/", func(c *gin.Context) {
        c.HTML(200, "index", data)
    })
    router.Run(":8080")
}

type MyHTMLRender struct {
    templates map[string]*template.Template
}

func (r *MyHTMLRender) Add(name string, tmpl *template.Template) {
    if r.templates == nil {
        r.templates = make(map[string]*template.Template)
    }
    r.templates[name] = tmpl
}

func (r *MyHTMLRender) Instance(name string, data interface{}) render.Render {
    return render.HTML{
        Template: r.templates[name],
        Data:     data,
    }
}

func createMyRender() render.HTMLRender {
    r := &MyHTMLRender{}
    r.Add("index", template.Must(template.ParseGlob(pattern)))
    r.Add("login", template.Must(template.ParseGlob(pattern1)))
    r.Add("something", template.Must(template.ParseGlob(pattern2)))
    r.Add("another", template.Must(template.ParseGlob(pattern3)))

    return r
}

Apakah kamu mengerti? Anda dapat membuat render Anda sendiri dan masih menggunakan sintaks c.HTML () yang sangat bagus ...

Terima kasih telah menjelaskan pendekatan saat ini dan juga untuk opsi baru. Pada percakapan ini saya mencoba untuk mendapatkan opsi default untuk bekerja.

@ al3xandru dalam pengalaman singkat saya dengan "templates" Saya telah melihat bahwa sebagian besar orang mencoba menggunakan satu contoh * template.Template dengan beberapa template di dalamnya dan kemudian mereka memanggil:

template.ExecuteTemplate(w, "template3", data)

jadi, saya mencoba mengakomodasi API untuk kasus penggunaan yang paling umum, tetapi sekali lagi, saya mendorong Anda untuk mempertahankan desain Anda saat ini jika lebih sesuai dengan kebutuhan Anda.

Seperti yang saya katakan: Anda memiliki dua opsi:

  1. Jangan gunakan fasilitas HTML bawaan dan lakukan persis seperti jika Anda menggunakan http.HandleFunc("/route", handler) alih-alih gin:

go template.Execute(c.Writer, data)

  1. Atau buat render Gin HTML Anda sendiri sehingga Anda tetap dapat membuatnya mudah dibaca menggunakan c.HTML ()

@ al3xandru Saya baru saja menambahkan render ke repo contrib:
https://github.com/gin-gonic/contrib/blob/master/renders/multitemplate/multitemplate.go

package main

import (
    "html/template"

    "github.com/gin-gonic/gin"
    "github.com/gin-gonic/contrib/renders/multitemplate"
)

func main() {
    router := gin.Default()
    router.HTMLRender = createMyRender()
    router.GET("/", func(c *gin.Context) {
        c.HTML(200, "index", data)
    })
    router.Run(":8080")
}

func createMyRender() multitemplate.Render {
    r := multitemplate.New()
    r.Add("index", template.Must(template.ParseGlob(pattern)))
    r.AddFromGlob("login", "/resources/login/*")
    r.AddFromFiles("something", "template1.html", "template2.html", "template3.html")

    return r
}

Hei, baru memulai tetapi saya pikir mengapa tidak membuat solusi middleware, pikiran?

package layout

import (
    "fmt"
    "github.com/gin-gonic/gin"
    "html/template"
    "net/http"
)

type GinLayout struct {
        Status  int64
    Base     string
    Template string
    Data     gin.H
}

func Layout() gin.HandlerFunc {
    return func(c *gin.Context) {
        c.Next()
        layout := c.MustGet("layout").(GinLayout)
        var baseTemplate = fmt.Sprint("views/layouts/", layout.Base)
        c.Engine.SetHTMLTemplate(template.Must(template.ParseFiles(baseTemplate, layout.Template)))
        c.HTML(http.StatusOK, layout.Base, layout.Data)
    }
}

dan kemudian di panggilan pengontrol

func main() {
    router := gin.Default()
    router.Use(layout.Layout())
    router.GET("/", func(c *gin.Context) {
       c.Set("layout", layout.GinLayout{
                200,
        "base.html",
        "views/dashboard.html",
        gin.H{
            "hello": "world",
        },
    })
    })
    router.Run(":8080")
}

@bayu_joo

  1. c.Engine.SetHTMLTemplate tidak aman untuk thread
  2. c. Mesin telah dilepas
  3. Gin sudah menyediakan cara non-hacky untuk melakukannya.
  4. Mengurai template di setiap permintaan sangat tidak efisien.

Jika Anda tidak ingin menggunakan sistem templat yang disediakan oleh Gin: buatlah sesuatu seperti:

func Layout() gin.HandlerFunc {
    return func(c *gin.Context) {
        c.Next()
        layout := c.MustGet("layout").(GinLayout)
        var baseTemplate = fmt.Sprint("views/layouts/", layout.Base)
        tmpl := template.Must(template.ParseFiles(baseTemplate, layout.Template)))
        tmpl.ExecuteTemplate(c.Writer, layout.Base, layout.Data)
    }
}

^^ solusi itu lambat dan hacky tetapi setidaknya itu bebas kondisi balapan

Jika Anda membutuhkan banyak template, saya sarankan Anda membuat multitemplate:

https://github.com/gin-gonic/contrib/blob/master/renders/multitemplate/multitemplate.go

tetapi saran saya adalah: coba sesuaikan desain Anda hanya dalam satu template. Template.
Jadi, alih-alih memiliki base.html dan halaman.html, Anda memiliki: header, footer, halaman1, halaman2 ...
halaman1, halaman2 menyertakan header, footer ... alih-alih templat dasar yang menyertakan konten.

Ini adalah contoh multitemplate, mungkin sangat cocok dengan pengaturan multi-template Anda saat ini:

package main

import (
    "html/template"

    "github.com/gin-gonic/gin"
    "github.com/gin-gonic/contrib/renders/multitemplate"
)

func main() {
    router := gin.Default()
    router.HTMLRender = createMyRender()
    router.GET("/", func(c *gin.Context) {
        c.HTML(200, "index", data)
    })
    router.Run(":8080")
}

func createMyRender() multitemplate.Render {
    r := multitemplate.New()
    r.AddFromFiles("index", "base.html", "base.html")
    r.AddFromFiles("article", "base.html", "article.html")
    r.AddFromFiles("login", "base.html", "login.html")
    r.AddFromFiles("dashboard", "base.html", "dashboard.html")

    return r
}

@manucorporat , saya menggunakan pongo2 , dan bagaimana kinerja solusi ini:

func Render(c *gin.Context, pth ...string) {
    template := pongo2.Must(pongo2.FromCache("tpl/" + strings.Join(pth, "/") + ".html"))
    if err := template.ExecuteWriter(c.Keys, c.Writer); err != nil {
        http.Error(c.Writer, err.Error(), http.StatusInternalServerError)
    }
}

Kemudian panggil seperti ini Render(c, "path", "to", "template") di penangan

@leedstyh , render juga bisa dibuat untuk pongo2. jadi Anda masih menggunakan c.HTML ()

Jadi Anda bisa melakukan:

router := gin.New()
router.HTMLRender = newPongo2Render("filesglob/*")
router.GET("/", func(c *gin.Context) {
     c.HTML(200, "index", data) // c.HTML uses pongo2
})

Terima kasih @manucorporat , saya hanya butuh satu template dasar. Jadi yang akan saya lakukan adalah beralih ke menyertakan template header dan footer seperti saran Anda.

@chonthu jika Anda menggunakan satu desain template:

Anda sudah siap dengan ini:

router := gin.New()
router.LoadHTMLGlob("templates/*") // this will load all the template files inside templates

Saya mengubah semua file untuk menggunakan {{template "header.html"}} dll. Dan ya, itu berfungsi dengan baik.

Saya terbiasa dengan pendekatan dasar dari kerangka kerja lain dalam bahasa lain. Saya pikir alasan untuk pergi ke arah itu adalah untuk mengubah struktur template dasar satu file dan tidak harus masuk ke setiap file yang memiliki header dan footer.

Ini adalah kerugian kecil dalam hal pembersihan atau perubahan besar tetapi tidak mematikan.

@chonthu ingat! jika Anda masih ingin menggunakan pengaturan base.html, maka paket multitemplate adalah titik awal yang baik!

Senang bisa membantu Anda

@manucorporat Oke, saya akan mencoba menulis yang baru!

@manucorporat Berdasarkan ini, jika saya menginginkan kemampuan untuk memuat berbagai tema (templat) dari penyimpanan objek seperti Cloudfiles berdasarkan pengguna tertentu. Saya perlu menimpa HTMLRender dengan logika untuk menarik file dari penyimpanan objek. Apakah hal seperti ini mungkin atau saya mencari masalah?

Hai @manucorporat , Saya mencoba melakukan ini (di tangkapan layar) dan berfungsi dengan sangat baik tetapi saya tidak tahu apakah ini solusi yang baik atau tidak?
Screenshot from 2020-09-04 06-46-36

Apakah halaman ini membantu?
0 / 5 - 0 peringkat