How to pass data into an HTML template effectively? An idea.

xuanbao · · 441 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>Hey Gophers! In the last couple of days I&#39;ve been struggling how to pass data into an HTML template effectively. Let me explain my thoughts:</p> <p>Say you have a simple search functionality on your website. There is a handler responsible for displaying search results for a given query. Surely there is a lot more to display than just the search results. In the header you can see some information of the currently logged in user account. In the footer you can see the last five testimonials from people on twitter. The header and footer are present on all pages of the website. You would have to query and pass the data for the user and testimonials into every handler which executes a template.</p> <pre><code>func search(w http.ResponseWriter, r *http.Request) { results := querySearch() searchTemplate.Execute(w, map[string]interface{}{ &#34;Results&#34;: results, &#34;User&#34;: currentUser(), &#34;Testimonials&#34;: currentTestimonials(), }) } </code></pre> <p>In my opinion the handler displaying the search results is not responsible for anything other than the results. The data for the header and footer should come from some abstraction level above. As an example, we do similar abstractions with middleware/adapters. You have one middleware for checking if a user is logged in or not and wrap all handlers which depend on a logged in user state. You don&#39;t check for authentication in all handlers manually.</p> <p>I&#39;ve come up with an idea which may be a (good or not so good) solution to this problem: <a href="https://play.golang.org/p/ys64OyXAjg">https://play.golang.org/p/ys64OyXAjg</a> This idea is inspired by the http.RoundTripper inferface. (Thanks <a href="/u/campoy">/u/campoy</a> for your talk about interfaces)</p> <p>There is a Renderer interface (apologize the bad naming) which can writes <code>data</code> to an <code>io.Writer</code>, it&#39;s unspecified how.</p> <pre><code>type Renderer interface { Render(w io.Writer, data map[string]interface{}) } </code></pre> <p>If you go and extend an html/template.Template to satisfy this interface, you can apply some sort of middleware to it.</p> <p>Create your own type which embeds a Renderer and implements the Renderer interface:</p> <pre><code>type titleRenderer struct { title string renderer Renderer } func (r *titleRenderer) Render(w io.Writer, data map[string]interface{}) { data[&#34;Title&#34;] = r.title r.renderer.Render(w, data) } </code></pre> <p>Then you can wrap an original renderer into a titleRenderer, which adds <code>Title</code> to the data map whenever the original Render function is called. With that, the additional data like the current logged in user or the testimonials can be passed in, in an abstraction level above.</p> <p>Thoughts and alternative ideas how to solve this issue are very welcome. Thanks!</p> <hr/>**评论:**<br/><br/>shovelpost: <pre><blockquote> <p>There is a Renderer interface (apologize the bad naming)</p> </blockquote> <p>Renderer is the <a href="https://golang.org/doc/effective_go.html#interface-names" rel="nofollow">idiomatic name</a> in this case.</p> <blockquote> <p>In my opinion the handler displaying the search results is not responsible for anything other than the results.</p> </blockquote> <p>I agree and that&#39;s why <a href="https://golang.org/pkg/context/" rel="nofollow">context</a> exists. The current user should be passed down in the handler request context from the middlewhere that is responsible for that.</p> <p>Also If you want to inject dependencies to your handlers it is a good pattern to do something like this:</p> <pre><code>type MyApp struct { // dependencies go here } func (app *MyApp) search(w http.ResponseWriter, r *http.Request) { // Grab user from context // app.SearchService.Search(query) // Render search results and other stuff based on the current user context. } </code></pre> <p>Other than that, the Renderer interface seems like a good idea to me which can complement the above techniques.</p></pre>egonelbre: <pre><p>You can also use <a href="https://golang.org/pkg/html/template/#Template.Funcs" rel="nofollow">https://golang.org/pkg/html/template/#Template.Funcs</a> to return common types.</p></pre>

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

441 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传