2013-08-16 35 views
19

Bir Go şablonunda, bazen doğru verileri doğru şablona aktarma yolu bana garip geliyor. Bir pipeline parametresiyle bir şablonu çağırmak, yalnızca bir parametreyle bir işlevi çağırmak gibi görünüyor.Çeşitli boru hattı parametreleriyle şablon çağırma

Gophers hakkında Gophers için bir sitem var diyelim. Bir ana sayfa ana şablonu ve Gophers listesi yazdırmak için bir yardımcı program şablonu vardır.

http://play.golang.org/p/Jivy_WPh16

Çıktı:

*The great GopherBook* (logged in as Dewey) 

    [Most popular] 
     >> Huey 
     >> Dewey 
     >> Louie 

    [Most active] 
     >> Huey 
     >> Louie 

    [Most recent] 
     >> Louie 

Şimdi subtemplate bağlam biraz eklemek istiyorum: Şu anda oturum açmış kullanıcının adıdır çünkü farklı liste içinde adı "Dewey" biçimlendirmek . Ama ismini doğrudan iletemiyorum çünkü only one olası "nokta" argüman hattı var! Ne yapabilirim?

  • Açıkçası, alt şablon kodunu ana şablona kopyalayıp yapıştırabilirim (bunu yapmak istemiyorum çünkü bir alt tabakanın tüm ilgisini azalttığı için).
  • Ya da bir çeşit global değişkenle erişim sağlayıcıları ile juggle yapabilirim (Ben de istemiyorum). Veya her şablon parametre listesi için yeni bir özel yapı türü oluşturabilirim (büyük değil).

cevap

42

Şablonlarınızda şablon çağrısına birden çok değer aktarmak için kullanabileceğiniz bir "dict" işlevi kaydedebilirsiniz.Çağrı kendisi o şekilde görünecektir:

{{template "userlist" dict "Users" .MostPopular "Current" .CurrentUser}} 

bir şablon fonk olarak kaydederek dahil küçük "dict" yardımcı için kod, burada:

var tmpl = template.Must(template.New("").Funcs(template.FuncMap{ 
    "dict": func(values ...interface{}) (map[string]interface{}, error) { 
     if len(values)%2 != 0 { 
      return nil, errors.New("invalid dict call") 
     } 
     dict := make(map[string]interface{}, len(values)/2) 
     for i := 0; i < len(values); i+=2 { 
      key, ok := values[i].(string) 
      if !ok { 
       return nil, errors.New("dict keys must be strings") 
      } 
      dict[key] = values[i+1] 
     } 
     return dict, nil 
    }, 
}).ParseGlob("templates/*.html") 
+0

Bu güzel, teşekkürler. Bu yüzden PipelineDecorator gibi bir çoklu anahtar/değer muxer (diğer cevaba bakınız), ancak tek bir aramayla daha olası değerler. – Deleplace

+0

Bu kodu yazmaya başlamadan önce bunun çok çirkin olabileceğini düşündüm, ama şimdi kendim gibi hissediyorum ve muhtemelen bazı projelerimde kullanacağım. – tux21b

+0

Kabul edildi olarak işaretlerim, çünkü bu, keyfi bir veriyi aktarmak için "sanatın durumu" nu (ama yine de, hararetli tasarım seçimleriyle ilgili bir geçici çözüm) ifade eder. İşte benim bütün örneğim dict() ': http: //play.golang.org/p/oWdPlyWfvG – Deleplace

1

i şimdiye kadar bulduğum en iyi (ve bunu hazzetmiyorum) muxing ve "genel" çifti konteyner ile parametreleri demuxing edilir:

http://play.golang.org/p/ri3wMAubPX

type PipelineDecorator struct { 
    // The actual pipeline 
    Data interface{} 
    // Some helper data passed as "second pipeline" 
    Deco interface{} 
} 

func decorate(data interface{}, deco interface{}) *PipelineDecorator { 
    return &PipelineDecorator{ 
     Data: data, 
     Deco: deco, 
    } 
} 

ben Bu hileyi web sitemi oluşturmak için çok kullanıyorum ve aynı şeyi elde etmek için biraz daha idiomatik bir yol olup olmadığını merak ediyorum.

3

Sen şablonunda fonksiyonlar tespit basitçe şablonunda bu işlevi çağırabilir,

template.FuncMap{"isUser": func(g Gopher) bool { return string(g) == string(data.User);},} 

Sonra: ve bu işlevler kapanışları bu gibi verilere tanımlanan sahip

oyun çıkışları üzerinde
{{define "sub"}} 

    {{range $y := .}}>> {{if isUser $y}}!!{{$y}}!!{{else}}{{$y}}{{end}} 
    {{end}} 
{{end}} 

This updated version geçerli kullanıcı çevresinde oldukça !!:

*The great GopherBook* (logged in as Dewey) 

[Most popular] 

>> Huey 
>> !!Dewey!! 
>> Louie 



[Most active] 

>> Huey 
>> Louie 



[Most recent] 

>> Louie 

DÜZENLEME

Funcs arama yaparken işlevlerini geçersiz kılabilirsiniz yana, aslında şablon işlevleri önceden doldurabilirsiniz şablonunuzu derlerken, aşağıdaki gibi gerçek kapatma ile güncelleyin:

01 Birkaç goroutines aynı şablonu erişmeye çalıştığınızda o nasıl oynadığını emin değilim rağmen

...

The working example

+0

Tamam, bu nedenle, geçerli şablon yürütme kapsamında bazı "küresel erişimcileri" ilginç verilere tanımlarız. Ana dezavantaj, derlenmiş şablonu yeniden kullanamadığımdan, kapatılmış FuncMap'ı oluşturmaya, sonra derlemeye, sonra da her istek için şablonu yürütmeye zorlandığım. – Deleplace

+0

Evet, sizin durumunuza göre, tux21b'nin çözümü daha esnek olabilir. Sonunda, tüm değerlerinizi bir şekilde ya da başka bir şekilde toplamanız yeterlidir ... – val

+0

Şablon derlemesinden sonra yeni kapanışların gerçekten bağlanabileceğini bilmek güzel, bunun farkında değildim. Potansiyel yarış durumu için, onu denedim ve “runtime.GoSched()” yöntemini kullanarak, goroutine serpiştirmeyi zorlamak ve “panik” (http://play.golang.org/p/b5WVlUlGjS – Deleplace

0

Reklam "... tek bir parametresi olan bir işlevini çağırarak gibi görünüyor.":

Bir anlamda, her işlev, çok değerli bir çağırma kaydı olan tek paramater alır. Şablonları ile aynıdır, "çağırma" kaydı ilkel bir değer veya çok değerli bir {map, struct, array, slice} olabilir. Şablon, hangi alanda olursa olsun "tek" boru hattı parametresinden hangi {anahtar, alan, dizin} kullanacağını seçebilir. Bu durumda, IOW, bir tek bir yeterlidir.

+1

Kesinlikle: Benim ilgim fizibilite ile ilgili değil, temiz tasarımla ilgili. Birden çok dönüş değerine sahip olmayan diğer dillere (C, Java) bakın: bir bölüm ve geri kalan bir değer veren bir öklid bölünmesi tanımlamak. Fonksiyonların sadece bir parametreye sahip olduğu bir dil düşünün ve her parametre listesinden önce yeni bir veri yapısını bildirmek için 4 veya 5 satır kullanmalısınız: Kod yazmak iyi olmaz. – Deleplace

0

Bazen haritalar, diğer yanıtların bir çiftinde belirtildiği gibi, bu gibi durumlara hızlı ve kolay bir çözümdür. İşte

type Gopher struct { 
    Name string 
    IsCurrent bool 
    IsAdmin bool 
} 

sizin Oyun bir güncelleme var: Eğer sincaplardan çok kullandığınız (ve o zamandan beri, diğer soruya dayalı, geçerli Gopher bir yönetici ise bakım) beri, kendi yapı hak ediyor kod: http://play.golang.org/p/NAyZMn9Pep

Açıkçası alır biraz hantal el kodlama derinliği ekstra bir seviye ile örnek yapılar, ama pratikte bunlar makine tarafından oluşturulmuş olacak, çünkü o gerektiği gibi IsCurrent ve IsAdmin işaretlemek için basit bu.

0

Bunu genel boru hattını süslemek için yaklaşım yolu: Bir bağlama özel boru hattını oluşturarak

type HomeData struct { 
    User Gopher 
    Popular []Gopher 
    Active []Gopher 
    Recent []Gopher 
} 

:

type HomeDataContext struct { 
    *HomeData 
    I interface{} 
} 

bağlama özel bir boru hattı ayrılması çok ucuz. Bunu işaretçiyi kopyalayarak potansiyel büyük HomeData erişebilirsiniz: HomeData yana

t.ExecuteTemplate(os.Stdout, "home", &HomeDataContext{ 
    HomeData: data, 
}) 

HomeDataContext yılında, sizin şablon doğrudan erişecek gömülü olduğu (örneğin hala .Popular değil .HomeData.Popular yapabilirsiniz). Artı, şimdi serbest biçimli bir alana (.I) erişiminiz var.

Son olarak, HomeDataContext için bir Using işlevi yapıyorum.

func (ctx *HomeDataContext) Using(data interface{}) *HomeDataContext { 
    c := *ctx // make a copy, so we don't actually alter the original 
    c.I = data 
    return &c 
} 

Bu bana bir devlet (HomeData) tutmak ancak alt şablona keyfi bir değer geçmesine izin verir.

Bkz. http://play.golang.org/p/8tJz2qYHbZ.

+0

Dekorasyon ve yerleştirme umut verici fikirlerdir, ancak özellikle içeriğin alt plakalar tarafından erişilebilir olmasını istiyorum. Orijinal örneği düzenleyerek bu sorunu çözebilir misiniz? Teşekkürler! – Deleplace

+0

Maalesef ilk yorumunuz tamamen doğrudur: "Bir pipeline parametresiyle bir şablon çağırma, yalnızca bir parametreyle bir işlevi çağırmak gibi görünüyor." Genel "Bağlam" nesnesini tutup onun üzerinde bir alanı mutasyona sokarak en iyi başarıya sahibim. – chowey

+0

Tamam, bu yüzden PipelineDecorator'a benziyor (diğer cevaba bakınız). Ve 'işlevinin 'bir işlev yerine bir yöntem olarak kullanılması iyi bir fikirdir. – Deleplace

0

& kontrolünden geçen boru benzeri argümanları destekleyen bu sorun için bir kitaplık uyguladık.

Demo

{{define "foo"}} 
    {{if $args := . | require "arg1" | require "arg2" "int" | args }} 
     {{with .Origin }} // Original dot 
      {{.Bar}} 
      {{$args.arg1}} 
     {{ end }} 
    {{ end }} 
{{ end }} 

{{ template "foo" . | arg "arg1" "Arg1" | arg "arg2" 42 }} 
{{ template "foo" . | arg "arg1" "Arg1" | arg "arg2" "42" }} // will raise an error 

Github repo

2

Ben dizinleri belirtmeden bile kullanılabilecek şekilde işlevini geliştirdik

tux21b @ dayalı (sadece yol gitmek için değişkenleri ekler tutmaya şablon)

Şimdi bunu şu şekilde yapabilirsiniz:

{{template "userlist" dict "Users" .MostPopular "Current" .CurrentUser}} 

veya

{{template "userlist" dict .MostPopular .CurrentUser}} 

veya

{{template "userlist" dict .MostPopular "Current" .CurrentUser}} 

ama

parametre (.CurrentUser.name) kesinlikle bu değeri geçmek için bir dizin koymak gerekir bir dizi değil ise şablon

{{template "userlist" dict .MostPopular "Name" .CurrentUser.name}} 

kodumu bkz:

var tmpl = template.Must(template.New("").Funcs(template.FuncMap{ 
    "dict": func(values ...interface{}) (map[string]interface{}, error) { 
     if len(values) == 0 { 
      return nil, errors.New("invalid dict call") 
     } 

     dict := make(map[string]interface{}) 

     for i := 0; i < len(values); i ++ { 
      key, isset := values[i].(string) 
      if !isset { 
       if reflect.TypeOf(values[i]).Kind() == reflect.Map { 
        m := values[i].(map[string]interface{}) 
        for i, v := range m { 
         dict[i] = v 
        } 
       }else{ 
        return nil, errors.New("dict values must be maps") 
       } 
      }else{ 
       i++ 
       if i == len(values) { 
        return nil, errors.New("specify the key for non array values") 
       } 
       dict[key] = values[i] 
      } 

     } 
     return dict, nil 
    }, 
}).ParseGlob("templates/*.html") 
İlgili konular