How to create reusable HTML components using default html/template package

1,941 views
Skip to first unread message

pj.beta...@gmail.com

unread,
Aug 7, 2016, 5:51:05 PM8/7/16
to golang-nuts
Coming from another language, I'm starting to port a web app to Go using default packages to test-drive it but I couldn't find any standard/recommended way of using pre-defined HTML components that I could use in HTML pages and inside other components.

Specifically, is there a recommended pattern on how to use a component inside another component inside another component passing data to each one in a transparent way?
As far as I understand Go differentiates itself from other languages because you don't need to use a Web framework and rather use default API/middleware. But when it comes to reusable templates/components (and also layouts) I don't see an immediate  parallel.

Could someone point me in the correct direction?
Thanks.

Simon Ritchie

unread,
Aug 8, 2016, 2:45:41 AM8/8/16
to golang-nuts
> Specifically, is there a recommended pattern on how to use a component inside another component inside another component passing data to each one in a transparent way?

That's not awfully specific.  I'm guessing that you are thinking of a particular framework that you have used with another language.  Can you say what that is?  Your question would be clearer if you do that.

I'm using a solution based on ideas from Struts and Spring, which are Java frameworks.  However, what I'm doing may not be anything like what you are trying to do.

Paulo Janeiro

unread,
Aug 8, 2016, 11:13:57 AM8/8/16
to golang-nuts
I understand what your point, but I'm just trying to see if this is something easy to do by myself or if I should start looking into a framework
Nevertheless, I'm using Phoenixframework (Elixir).
Here we could define a layout that is made of componentes (pre-built templates). Data is passed when you insert templates (components):

<!DOCTYPE html>
<html lang="e
<head>

   
<%= render FabricaASA.ModuleView, "head.html", conn: @conn %>

</head>

<body>

   
<%= render FabricaASA.ComponentView, "clientInfo.html",

        clientInfo_isMobileiPad
: nil,
        clientInfo_smallScreenWidth
: "750"
    %>
<!-- ................................................. Flash messages .................................................... -->

               
<%= if get_flash(@conn, :info) do %>

   
<p class="comp alert info" role="alert"><%= get_flash(@conn, :info) %></p>
       
<script>
            setTimeout
(function(){
                $
(".comp.alert.info").css("opacity", "0")
           
}, 3000);
       
</script>
                           
<% end %>
               
<%= if get_flash(@conn, :error) do %>
   
<p class="comp alert danger" role="alert"><%= get_flash(@conn, :error) %></p>
                           
<% end %>
<!-- .................................................. Header ........................................................... -->

   
<%= render FabricaASA.ModuleView, "headerASA.html", conn: @conn,

        header_class
: nil,
        header_id
: nil,
        header_posit
: nil,
        header_dimen
: nil,
        header_mainStyle
: nil,
        header_effect
: "onTop"
    %>
<!-- ............................................... Page content ........................................................ -->

   
<%= render @view_module, @view_template, assigns %>


<!-- ................................................. Footer ............................................................ -->


   
<%= render FabricaASA.ModuleView, "footer.html",
        footer_class
: nil,
        footer_id
: nil,
        footer_tooltip
: nil
    %>
<!-- ....................................... Imports javascript files ..................................................... -->

    <script src="
<%= static_path(@conn, "/js/app.js") %>"></script>
   
<%= render FabricaASA.ModuleView, "importJs.html" %>

</body>
</html>


Code inside "Page content" is what is shown depend ending on the route.
Each component can be made of other componentes (templates):

<div class="mod header main <%= @header_class %>" id="header<%= @header_id %>"
                                           
<%= if @header_effect=="onTop" do %>
                            style=" position: absolute;
                                    top: 0px;
                                    left: 0px;
                                    z-index: 50;
                                    width: 100%;
                                                       
<% end %>">
   
<div class="mod header firstChild top" style="height: 70px;
                                                  flex
: 1 1 1200px;
                                                  flex
-flow: row nowrap">                                    <!-- header part that is allways visible -->
       
<div class="mod header secondChild left" style="width: 100%; height: 100%; flex-flow: row nowrap">
                                   
<%= if @conn.request_path == "/" do %>                                             <!-- If it's home page ... -->
       
<%= render FabricaASA.ComponentView, "menuButton.html",
            conn
: @conn,
            menuButton_id
: "1",
            menuButton_class
: nil,
            menuButton_posit
: nil,
            menuButton_dimen
: "flex: 1.5 0 90px",
            menuButton_mainStyle
: "visibility: hidden",                                   #...hides the element but keep it there to occupy its space
            menuButton_hiddenElement
: ".header.firstChild.bottom",
            menuButton_marginDown
: "70px",
            menuButton_marginTime
: "0.6s",
            menuButton_slideTime
: "slow",
            menuButton_slideEasing
: nil,
            menuButton_effect
: "push",
            menuButton_maniElem
: ".vpage.main"
        %>                                                                                
<!-- menuButton that controls the show/hide of the menu -->
                                                       
<%= else %>
       
<%= render FabricaASA.ComponentView, "menuButton.html",
            conn
: @conn,
            menuButton_id
: "1",
            menuButton_class
: nil,
            menuButton_posit
: nil,
            menuButton_dimen
: "flex: 1.5 0 90px; height: 100%",
            menuButton_mainStyle
: nil,
            menuButton_hiddenElement
: ".header.firstChild.bottom",
            menuButton_marginDown
: "70px",
            menuButton_marginTime
: "0.6s",
            menuButton_slideTime
: "slow",
            menuButton_slideEasing
: nil,
            menuButton_effect
: "push",
            menuButton_maniElem
: ".vpage.main"
        %>
                                                   
<% end %>
       
<%= render FabricaASA.ComponentView, "button.html",
            button_id
: "1",
            button_class
: "tooltip",
            button_posit
: nil,
            button_dimen
: "flex: 1 0 50px",
            button_mainStyle
: nil,
            button_script
: 'hopscotch.startTour(tour)',
            button_imgPath
: "/images/helpwhite.svg",
            button_width
: "30px",
            button_height
: "30px",
            button_tooltip
: "Ver tour explicativo de novo",
            button_label
: nil,
            button_linkURL
: nil,
            button_effect
: "aux"
        %>                                                                                                                    
<!-- Help icon link -->
           
<%= render FabricaASA.ComponentView, "textBlock.html",
            textBlock_id
: nil,
            textBlock_class
: nil,
            textBlock_posit
: nil,
            textBlock_dimen
: "height: 100%; flex: 2 1 15px",
            textBlock_title
: "Right Item",
            textBlock_text
: nil,
            textBlock_hType
: "5",
            textBlock_effect
: nil,
            textBlock_mainStyle
: "visibility: hidden",
            textBlock_titleStyle
: nil,
            textBlock_textStyle
: nil
        %>
       
</div>
   
</div>
   
<div class="mod header firstChild bottom" style="flex: 1 1 1200px;
                                                 display
: none">                                                     <!-- hidden part of the menu -->
                                   
<%= unless @conn.request_path == "/polPrivLight" do %>
       
<%= render FabricaASA.ComponentView, "intLinkButBack.html",
            intLinkButBack_id
: nil,
            intLinkButBack_class
: nil,
            intLinkButBack_posit
: nil,
            intLinkButBack_height
: "70px",
            intLinkButBack_effect
: "button",
            intLinkButBack_video
: nil,
            intLinkButBack_url
: "#vpageFabrica",
            intLinkButBack_maniElemId
: "page",
            intLinkButBack_maniElemClass
: "page.main",
            intLinkButBack_label
: "A FÁBRICA",
            intLinkButBack_backCol
: "#1D75CE",
            intLinkButBack_backImgURL
: "/images/specific/fabrica.jpg",
            intLinkButBack_backImgSize
: "cover",
            intLinkButBack_backImgSizeHover
: "cover",
            intLinkButBack_flexComp
: "175px",
            intLinkButBack_top
: "25%",
            intLinkButBack_buttonWidth
: "70%",
            intLinkButBack_buttonLeft
: "15%",
            intLinkButBack_opa
: "0.3",
            intLinkButBack_opaHover
: "0.9"
        %>
       
<%= render FabricaASA.ComponentView, "intLinkButBack.html",
            intLinkButBack_id
: nil,
            intLinkButBack_class
: nil,
            intLinkButBack_posit
: nil,
            intLinkButBack_height
: "70px",
            intLinkButBack_effect
: "button",
            intLinkButBack_video
: nil,
            intLinkButBack_url
: "#vpageEspacos",
            intLinkButBack_maniElemId
: "page",
            intLinkButBack_maniElemClass
: "page.main",
            intLinkButBack_label
: "ESPAÇOS",
            intLinkButBack_backCol
: "#ED754A",
            intLinkButBack_backImgURL
: "/images/specific/piso0.jpg",
            intLinkButBack_backImgSize
: "cover",
            intLinkButBack_backImgSizeHover
: "cover",
            intLinkButBack_flexComp
: "175px",
            intLinkButBack_top
: "25%",
            intLinkButBack_buttonWidth
: "70%",
            intLinkButBack_buttonLeft
: "15%",
            intLinkButBack_opa
: "0.3",
            intLinkButBack_opaHover
: "0.9"
        %>
                                               
<%= else %>
               
<%= render FabricaASA.ComponentView, "intLinkButBack.html",
            intLinkButBack_id
: nil,
            intLinkButBack_class
: nil,
            intLinkButBack_posit
: nil,
            intLinkButBack_height
: "70px",
            intLinkButBack_effect
: "button",
                    intLinkButBack_video
: nil,
            intLinkButBack_url
: "/main.html#vpageFabrica",
            intLinkButBack_maniElemId
: "page",
            intLinkButBack_maniElemClass
: "page.main",
            intLinkButBack_label
: "A FÁBRICA",
            intLinkButBack_backCol
: "#1D75CE",
            intLinkButBack_backImgURL
: "/images/specific/fabrica.jpg",
            intLinkButBack_backImgSize
: "cover",
            intLinkButBack_backImgSizeHover
: "cover",
            intLinkButBack_flexComp
: "175px",
            intLinkButBack_top
: "25%",
            intLinkButBack_buttonWidth
: "70%",
            intLinkButBack_buttonLeft
: "15%",
            intLinkButBack_opa
: "0.3",
            intLinkButBack_opaHover
: "0.9"
        %>
                                                           
<% end %>
   
</div>
</div>

Marvin Renich

unread,
Aug 8, 2016, 12:30:43 PM8/8/16
to golang-nuts
* Paulo Janeiro <pj.beta...@gmail.com> [160808 11:14]:
> I understand what your point, but I'm just trying to see if this is
> something easy to do by myself or if I should start looking into a framework
> Nevertheless, I'm using Phoenixframework (Elixir).
> Here we could define a layout that is made of componentes (pre-built
> templates). Data is passed when you insert templates (components):
>
> <!DOCTYPE html>
> <html lang="e
> <head>
>
> <%= render FabricaASA.ModuleView, "head.html", conn: @conn %>
>
> </head>

Would the html/template package do what you want?

...Marvin

parais...@gmail.com

unread,
Aug 8, 2016, 2:09:48 PM8/8/16
to golang-nuts
Use the html/template package . It's a good idea to go through the documentation of the standard lib once and to see what packages it provides. In fact, with Go the knowledge of the std lib is as important knowing any other  features of the language. 

Paulo Janeiro

unread,
Aug 8, 2016, 2:53:05 PM8/8/16
to golang-nuts, parais...@gmail.com
I'm using html/template already.
I'm able to parse my templates dynamically but my approach doesn't seems to be the best to handle dozens of different templates that can be used inside N different combinations, especially because I want to cache them and not to have to parse those combination every time I render that view.
For example, I have this code:

func extractFileNames () (templateFileNamesFull []string) {
    files
, _ := ioutil.ReadDir("./views")                                                                                        
   
for _, f := range files {                                                                                                          
       
if strings.Contains(f.Name(), ".html") {                                                                                        
            templateFileNamesFull
= append(templateFileNamesFull, "views/" + f.Name())                  
       
}
   
}
   
return templateFileNamesFull                                                                                                        
}


var Templates = template.Must(template.ParseFiles(extractFileNames()...))

This works great WITHOUT layouts/components, because using components make me have N of these combinations, instead of just one "Templates" variable with all posed templates:

t1.ParseFiles("boilerplate.html", "page1.html")
...
tN
.ParseFiles("boilerplate.html", "pageN.html")

AFAIK with GO I can't create dynamic variables like "template1",..., "templateN" thus making this road not the best one for what I want.

So,  as a newcomer to this language and with some homework done, my question is if there are some best practices for using these small components or is it something that makes the use of a Web Framework necessary?

Matt Silverlock

unread,
Aug 11, 2016, 9:56:26 AM8/11/16
to golang-nuts, parais...@gmail.com
Have you looked at template blocks? https://golang.org/pkg/html/template/#example_Template_block

My approach is to create a map[string]*template.Template that contains each template (base + layout + blocks for that template), with the map key set to the layout name. 

krde...@gmail.com

unread,
May 16, 2019, 1:04:14 PM5/16/19
to golang-nuts
How do I create template within Nextgen?

Leonel Quinteros

unread,
May 17, 2019, 9:49:16 AM5/17/19
to golang-nuts
I've created some approach for what you need, but designed to "compile" these templates and reusable components into a static site. 
Feel free to check the code for ideas to implement your own solution, it shouldn't be hard to do so: https://github.com/leonelquinteros/thtml

I guess the code you want to check deeper will be in this package: 
Reply all
Reply to author
Forward
0 new messages