
This text was written by an exterior contributor.
Go templates are a strong function used to generate textual content or HTML outputs primarily based on information in a Go program. You may customise how the information is displayed by passing an object to a template. Templates are sometimes used to generate internet pages, emails, and different text-based outputs. A very talked-about real-life use of a Go template is within the kubectl command line instrument, the place you possibly can move a template to the --template
flag to customise the output to your wants.
Templates overview
In Go, there are two packages that present the templating performance: the textual content/template and html/template packages. Each supply the very same set of interfaces; the one distinction is that the latter robotically secures the HTML output in opposition to numerous assaults. This makes html/template
a more sensible choice for producing HTML output, and is why this text will use the html/template
package deal.
A template is just a string with some particular instructions referred to as actions, that are enclosed in double curly braces. The actions are used to entry or consider information, or to regulate the template’s construction.
Right here’s an instance template:
tmpl := "Good day {{.Title}}!"
The above template has a single motion that prints the worth of the Title
discipline of the information object handed to the template. The .
character within the motion refers back to the information object handed to the template, and .Title
accesses the Title
discipline of the thing.
To render the template, you have to parse the template with the Parse
perform and use the Execute
perform, which takes a author and the information object as arguments and writes the output to the author.
// Defining the information to move kind Consumer struct { Title string } consumer := Consumer{"James"} t, err := template.New("take a look at").Parse(tmpl) if err != nil { panic(err) } err = t.Execute(os.Stdout, consumer) if err != nil { panic(err) }
The above outputs Good day James!
to the console.
Aside from accessing information, you should use actions like if
to conditionally render content material, and vary
to iterate over a group. You can too outline your individual features and use them within the template. An entire overview of templates might be discovered right here.
Constructing a Weblog With Templates
To comply with together with the tutorial, it’s essential to have Go put in and arrange in your system. You’ll additionally wish to set up GoLand.
You will discover the code for this tutorial on GitHub. Be at liberty to clone and discover the code, or to comply with together with the tutorial to create the appliance from scratch.
Creating a brand new mission
Begin GoLand and click on on the New mission button. Select the Go choice and supply a reputation, equivalent to “GoBlog”.

Click on on Create to create the mission. Create a file major.go
on the root of the mission with the next content material:
package deal major import ( "database/sql" "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" _ "github.com/mattn/go-sqlite3" )
You’ll discover the modules are highlighted in purple. It’s because these modules haven’t been downloaded but.

GoLand can robotically obtain them for you. Merely hover over any highlighted entries and click on on Sync dependencies of GoBlog within the popup that seems.

After a couple of seconds, the modules shall be downloaded, and the purple highlighting will disappear.
Please word that when you’ve got Auto-format on save enabled, as quickly because the file is saved (for instance, when the window loses focus), GoLand will format the code and take away the unused dependencies. You may disable autosaving when switching to a special utility or a built-in terminal in Choice / Settings | Instruments | Actions on Save | Configure autosave choices… | Autosave.
You can too add imports manually. The Sync Dependencies motion downloads the packages to cache reminiscence, and once you add imports manually, you get autocomplete ideas.

As step one of coding the server, declare the next two international variables in major.go
:
var router *chi.Mux var db *sql.DB
Whereas you should use the usual library to arrange the REST server, this text will use the chi router for this function – chi is an easy-to-use, light-weight, and superfast router that comes bundled with many options.
router
will retailer the chi router occasion, and db
will retailer the database object.
Outline the Article
struct, which can have a title, content material, and ID:
kind Article struct { ID int `json:"id"` Title string `json:"title"` Content material template.HTML `json:"content material"` }
Observe that for Content material
, you’re utilizing template.HTML
as an alternative of string
. As a result of the content material shall be wealthy textual content and rendered as HTML, it’s essential to use template.HTML
to stop the HTML from being escaped when the templates are rendered.
Organising the database
Create the file db.go
, which is able to home the functionalities associated to the database. Begin by importing the required module:
package deal major import ( "database/sql" )
On this article, you’ll use the database/sql
package deal to work together with the database. Nonetheless, the templating system is unaffected by your alternative of database package deal; you should use it with some other database package deal that you simply like.
This tutorial will use the SQLite database to retailer the information, however you should use some other SQL database, equivalent to MySQL or PostgreSQL. An entire checklist of databases supported by database/sql
might be discovered right here.
Outline the join
perform, which is able to create the preliminary connection to the database. You can be utilizing an area file named information.sqlite
to retailer the information. If the file doesn’t exist, it is going to be created robotically.
func join() (*sql.DB, error) { var err error db, err = sql.Open("sqlite3", "./information.sqlite") if err != nil { return nil, err } sqlStmt := ` create desk if not exists articles (id integer not null main key autoincrement, title textual content, content material textual content); ` _, err = db.Exec(sqlStmt) if err != nil { return nil, err } return db, nil }
You’ll discover the SQL assertion is highlighted in GoLand.

GoLand has a built-in database plugin that may hook up with completely different databases and allows you to question, create, and handle tables, all with out leaving the IDE. To make use of the plugin, it’s essential to configure a knowledge supply. Merely click on on the highlighted SQL assertion, and within the context actions menu (the yellow bulb icon), click on Configure information supply. Alternatively, you possibly can click on in highlighted space and press Alt+Enter (⌥ ↩) to see obtainable intention actions.
Within the dialog that seems, click on on +
so as to add a brand new information supply, and choose SQLite because the database kind. Present a reputation for the database, equivalent to “Database”, and use information.sqlite
because the file title. If the suitable database driver will not be put in, you’ll be prompted to put in it. As soon as prepared, click on on OK to save lots of the information supply.

GoLand will create the file if it doesn’t exist, hook up with it, and open a database console. That is the place you possibly can write the SQL queries. You can too see the newly created database within the Database instrument window on the suitable aspect.

Return to the code, place the cursor on the create desk
SQL assertion, and press Ctrl+Enter (⌘↩). Choose the console from the popup menu, and GoLand will run the question within the console.
You will note from the logs that the desk has been efficiently created; this may even be mirrored within the database instrument window.

Now let’s write the remainder of the database features. The dbCreateArticle()
perform will create a brand new article within the database from an Article
struct:
func dbCreateArticle(article *Article) error { question, err := db.Put together("insert into articles(title,content material) values (?,?)") defer question.Shut() if err != nil { return err } _, err = question.Exec(article.Title, article.Content material) if err != nil { return err } return nil }
Right here, you’ve gotten a ready assertion that shall be used to insert the article into the database. The ?
placeholders shall be changed by the values of the Title
and Content material
fields of the Article
struct. You’ll discover GoLand appropriately identifies the embedded SQL assertion and highlights it.

Like earlier than, you possibly can run the question by inserting the cursor on the SQL assertion and urgent Ctrl+Enter (⌘↩). This time, you’ll be prompted to supply values for the placeholder. Enter the values and click on on Execute.
Observe that it’s essential to add quotes to the values, since these shall be substituted verbatim.

You may double-click the desk title within the database instrument window to see all of the rows within the database. The newly created article must also present up right here.

The dbGetAllArticles()
perform will return all of the articles within the database as a slice of Article
structs:
func dbGetAllArticles() ([]*Article, error) { question, err := db.Put together("choose id, title, content material from articles") defer question.Shut() if err != nil { return nil, err } end result, err := question.Question() if err != nil { return nil, err } articles := make([]*Article, 0) for end result.Subsequent() { information := new(Article) err := end result.Scan( &information.ID, &information.Title, &information.Content material, ) if err != nil { return nil, err } articles = append(articles, information) } return articles, nil }
The dbGetArticle()
perform will return a single article from the database primarily based on the ID:
func dbGetArticle(articleID string) (*Article, error) { question, err := db.Put together("choose id, title, content material from articles the place id = ?") defer question.Shut() if err != nil { return nil, err } end result := question.QueryRow(articleID) information := new(Article) err = end result.Scan(&information.ID, &information.Title, &information.Content material) if err != nil { return nil, err } return information, nil }
The ultimate items of the puzzle are the dbUpdateArticle()
and dbDeleteArticle()
features, which is able to replace and delete an article from the database, respectively:
func dbUpdateArticle(id string, article *Article) error { question, err := db.Put together("replace articles set (title, content material) = (?,?) the place id=?") defer question.Shut() if err != nil { return err } _, err = question.Exec(article.Title, article.Content material, id) if err != nil { return err } return nil } func dbDeleteArticle(id string) error { question, err := db.Put together("delete from articles the place id=?") defer question.Shut() if err != nil { return err } _, err = question.Exec(id) if err != nil { return err } return nil }
Creating the routes
With the database features full, return to major.go
to jot down the remainder of the server. Begin by writing the catch()
perform, which merely panics in case of an error:
func catch(err error) { if err != nil { fmt.Println(err) panic(err) } }
The major()
perform is the place you’ll arrange the routes and the middlewares. Add the next code in major.go
:
func major() { router = chi.NewRouter() router.Use(middleware.Recoverer) var err error db, err = join() catch(err) router.Use(ChangeMethod) router.Get("/", GetAllArticles) router.Route("/articles", func(r chi.Router) { r.Get("/", NewArticle) r.Publish("/", CreateArticle) r.Route("/{articleID}", func(r chi.Router) { r.Use(ArticleCtx) r.Get("/", GetArticle) // GET /articles/1234 r.Put("/", UpdateArticle) // PUT /articles/1234 r.Delete("/", DeleteArticle) // DELETE /articles/1234 r.Get("/edit", EditArticle) // GET /articles/1234/edit }) }) err = http.ListenAndServe(":8005", router) catch(err) }
Observe the usage of the Recoverer middleware. When the catch()
perform panics, this middleware will get well the server, log the error with a stack hint, and ship a 500 Inside Server Error response to the shopper.
The above code units up the next routes:
GET /
: Shows all of the articles within the database.GET /articles
: Shows the shape to create a brand new article.POST /articles
: Creates a brand new article within the database.GET /articles/{articleID}
: Shows a single article.PUT /articles/{articleID}
: Updates an article within the database.DELETE /articles/{articleID}
: Deletes an article from the database.GET /articles/{articleID}/edit
: Shows the shape to edit an article.
Along with the routes, the code units up two middlewares:
ChangeMethod
: This middleware will change the request methodology toPUT
orDELETE
if the request methodology isPOST
and the shape discipline_method
is about toPUT
orDELETE
, respectively. That is required as a result of HTML kinds solely assistGET
andPOST
strategies.ArticleCtx
: This middleware will fetch the article from the database and retailer it within the request context. It will likely be utilized by the routes below the/articles/{articleID}
path.
Let’s write the ChangeMethod
perform first. As talked about earlier than, it should search for the _method
type factor and alter the request methodology accordingly:
func ChangeMethod(subsequent http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Methodology == http.MethodPost { change methodology := r.PostFormValue("_method"); methodology { case http.MethodPut: fallthrough case http.MethodPatch: fallthrough case http.MethodDelete: r.Methodology = methodology default: } } subsequent.ServeHTTP(w, r) }) }
The ArticleCtx
middleware will entry the article ID from the URL parameters and fetch the article from the database. If the article is discovered, it is going to be saved within the request context. If the article will not be discovered, the middleware will return a 404 standing code:
func ArticleCtx(subsequent http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { articleID := chi.URLParam(r, "articleID") article, err := dbGetArticle(articleID) if err != nil { fmt.Println(err) http.Error(w, http.StatusText(404), 404) return } ctx := context.WithValue(r.Context(), "article", article) subsequent.ServeHTTP(w, r.WithContext(ctx)) }) }
The GetAllArticles()
perform will use the dbGetAllArticles()
perform to fetch all of the articles from the database, and show them on a webpage by rendering a template. For now, let’s depart the rendering half empty:
func GetAllArticles(w http.ResponseWriter, r *http.Request) { articles, err := dbGetAllArticles() catch(err) fmt.Println(articles) //TODO: Render template }
The NewArticle()
perform will merely show the shape to create a brand new article. It doesn’t have to work together with the database, and as such, it’s fairly naked bones:
func NewArticle(w http.ResponseWriter, r *http.Request) { //TODO: Render template }
The CreateArticle()
perform shall be referred to as when the shape to create a brand new article has been submitted. It can extract the Title
and Content material
fields from the shape, and use the dbCreateArticle()
perform to create a brand new article within the database. After that, it should redirect the consumer to the /
web page:
func CreateArticle(w http.ResponseWriter, r *http.Request) { title := r.FormValue("title") content material := r.FormValue("content material") article := &Article{ Title: title, Content material: template.HTML(content material), } err := dbCreateArticle(article) catch(err) http.Redirect(w, r, "/", http.StatusFound) }
The GetArticle()
perform will show a single article. Because of the ArticleCtx
middleware, you don’t have to fetch the article right here – you possibly can merely get it from the request context:
func GetArticle(w http.ResponseWriter, r *http.Request) { article := r.Context().Worth("article").(*Article) fmt.Println(article) //TODO: Render template }
The EditArticle
perform will show the shape to edit an article:
func EditArticle(w http.ResponseWriter, r *http.Request) { article := r.Context().Worth("article").(*Article) fmt.Println(article) // TODO: Render template }
The UpdateArticle()
perform shall be referred to as when the shape to edit an article has been submitted. It can extract the Title
and Content material
fields from the shape, and use the dbUpdateArticle()
perform to replace the article within the database. After that, it should redirect the consumer to the /articles/{articleID}
web page:
func UpdateArticle(w http.ResponseWriter, r *http.Request) { article := r.Context().Worth("article").(*Article) title := r.FormValue("title") content material := r.FormValue("content material") newArticle := &Article{ Title: title, Content material: template.HTML(content material), } err := dbUpdateArticle(strconv.Itoa(article.ID), newArticle) catch(err) http.Redirect(w, r, fmt.Sprintf("/articles/%d", article.ID), http.StatusFound) }
Lastly, the DeleteArticle()
perform will delete the article from the database and redirect the consumer to the /
web page:
func DeleteArticle(w http.ResponseWriter, r *http.Request) { article := r.Context().Worth("article").(*Article) err := dbDeleteArticle(strconv.Itoa(article.ID)) catch(err) http.Redirect(w, r, "/", http.StatusFound) }
Rendering templates
The routes at the moment are prepared; the following step is to render the templates. Create a templates
listing with an index.html
file inside it, which shall be rendered within the GetAllArticles
() perform. Add the next code to the file:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>All articles</title> </head> <physique> {{if eq (len .) 0}} Nothing to see right here {{finish}} {{vary .}} <div> <a href="/articles/{{.ID}}">{{.Title}}</a> </div> {{finish}} <p> <a href="/articles">Create new article</a> </p> </physique> </html>
This template makes use of the if
and vary
template features. The if
perform will verify if the variety of articles equals zero. Whether it is, it should show the Nothing to see right here
message; in any other case, it should show the checklist of articles. The vary
perform will loop by way of the checklist of articles and show them. The .
variable refers back to the slice of all articles handed to the template. Inside the physique of the vary
perform, the .
variable refers to a single article. The ID
and Title
fields of the article are accessed utilizing the dot notation.
Let’s replace the GetAllArticles()
perform to render the template:
func GetAllArticles(w http.ResponseWriter, r *http.Request) { articles, err := dbGetAllArticles() catch(err) t, _ := template.ParseFiles("templates/index.html") err = t.Execute(w, articles) catch(err) }
Let’s see how the app appears thus far. Proper click on on the mission title within the Challenge sidebar, then go to the Run menu merchandise and click on go construct GoBlog. It will construct the mission and run it.

Open your browser and navigate to http://localhost:8005
. You must see the article that you simply created beforehand.

Let’s now create the shape to create a brand new article. For the reason that Content material
discipline is a wealthy textual content, you’ll want a wealthy textual content editor. This text will use the TinyMCE editor. You’ll have to join for a free account to get an API key. After getting the API key, create a new.html
file within the templates
listing and add the next code to it:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Create a brand new article</title> <script src="https://cdn.tiny.cloud/1/no-api-key/tinymce/6/tinymce.min.js" referrerpolicy="origin"></script> <script> tinymce.init({ selector: '#mytextarea', }); </script> </head> <physique> <h1>Create a brand new article</h1> <type methodology="publish" motion="/articles"> <enter id="title" kind="textual content" title="title" placeholder="Enter the title"> <textarea id="mytextarea" title="content material"></textarea> <button id="submit" kind="submit">Create</button> </type> </physique> </html>
You’ll want to switch no-api-key
within the above code together with your TinyMCE API key.
Observe that this file merely shows a type. It’s not strictly a template, however somewhat a easy HTML file, so that you don’t have to parse it utilizing the template.ParseFiles()
perform. You may merely use the http.ServeFile()
perform to serve it. Add the next line to the NewArticle
() perform:
http.ServeFile(w, r, "templates/new.html")
You may go to http://localhost:8005/articles
to see the shape in motion.

Be sure you restart the server whether it is working. To do that, use the restart button subsequent to the configurations menu.

Everytime you change Go information, it’s essential to restart the server.This isn’t explicitly talked about within the tutorial.
Nested templates
You will have seen that the 2 templates you’ve created thus far share a big chunk of code. The essential construction of the HTML web page is identical in each templates – the one distinction is the title, content material, and scripts of the web page. You should use nested templates to keep away from repeating the identical code in a number of templates. Create a base.html
file within the templates
listing and add the next code to it:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>{{ template "title" . }}</title> {{ template "scripts" }} </head> <physique> {{ template "physique" . }} </physique> </html>
This template makes use of three different templates: title
, scripts
, and physique
. You’ll outline completely different variations of those templates, which shall be substituted in base.html
. These templates shall be used to show the title, scripts, and the physique of the web page. Additionally, word that the title
and physique
templates move the present object utilizing the .
variable, enabling the nested templates to entry it if wanted.
Substitute the contents of index.html
with the next code:
{{outline "title"}}All articles{{finish}} {{outline "scripts"}}{{finish}} {{outline "physique"}} {{if eq (len .) 0}} Nothing to see right here {{finish}} {{vary .}} <div> <a href="/articles/{{.ID}}">{{.Title}}</a> </div> {{finish}} <p> <a href="/articles">Create new article</a> </p> {{finish}}
Utilizing the outline
perform, you possibly can outline the nested templates. Right here, you’ve merely extracted the title, physique, and scripts (that are empty as a result of this web page didn’t load any scripts) into their very own templates.
To make this work, it’s essential to amend the GetAllArticles()
perform to load the base.html
template along with index.html
:
t, _ := template.ParseFiles("templates/base.html", "templates/index.html")
Observe the order of the templates. base.html
should come earlier than index.html
.
Substitute new.html
with the next code:
{{outline "title"}}Create new article{{finish}} {{outline "scripts"}} <script src="https://cdn.tiny.cloud/1/no-api-key/tinymce/6/tinymce.min.js" referrerpolicy="origin"></script> <script> tinymce.init({ selector: '#mytextarea', }); </script> {{finish}} {{outline "physique"}} <type methodology="publish" motion="/articles"> <enter kind="textual content" title="title" placeholder="Enter the title"> <textarea id="mytextarea" title="content material"></textarea> <button id="submit" kind="submit">Create</button> </type> {{finish}}
Since new.html
is now a template, http.ServeFile
received’t work anymore. You could parse the template and execute it:
func NewArticle(w http.ResponseWriter, r *http.Request) { t, _ := template.ParseFiles("templates/base.html", "templates/new.html") err := t.Execute(w, nil) catch(err) }
At this level, you possibly can create a brand new article utilizing the shape.

After clicking on Create, you’ll be redirected to the basis URL, and the newly created article will seem within the checklist.

Observe that the wealthy textual content is saved as HTML within the database.

Create a file named article.html
, which is able to show a single article:
{{outline "title"}}{{.Title}}{{finish}} {{outline "scripts"}}{{finish}} {{outline "physique"}} <h1>{{.Title}} </h1> <div> {{.Content material}} </div> <div> <a href="/articles/{{.ID}}/edit">Edit</a> <type motion="/articles/{{.ID}}" methodology="publish"> <enter kind="hidden" title="_method" worth="DELETE"> <button kind="submit">Delete</button> </type> </div> {{finish}}
This web page has a hyperlink to the edit web page and a type to delete the article. Observe the hidden _method
factor. As mentioned earlier than, the presence of this factor will convert the request to a DELETE
request courtesy of the ChangeMethod
middleware.
Modify the GetArticle
perform to render the template:
func GetArticle(w http.ResponseWriter, r *http.Request) { article := r.Context().Worth("article").(*Article) t, _ := template.ParseFiles("templates/base.html", "templates/article.html") err := t.Execute(w, article) catch(err) }
Clicking on the title of an article on the house web page will now show the article.

Picture importing
TinyMCE helps picture importing by default. You may drag and drop photographs into the editor, and so they’ll be transformed into base64 strings and saved as a part of the Content material
discipline.

The picture will present up on the article web page, as properly.

Nonetheless, it’s not thought of good observe to retailer photographs as base64 strings, so let’s add assist for picture importing.
Add two new routes to the router:
func major() { ... router.Use(ChangeMethod) router.Get("/", GetAllArticles) router.Publish("/add", UploadHandler) // Add this router.Get("/photographs/*", ServeImages) // Add this router.Route("/articles", func(r chi.Router) { ... }) ... }
The /add
route will deal with the picture add and retailer them as information within the photographs listing, and the /photographs/*
route will serve the photographs.
Let’s write the UploadHandler
perform:
func UploadHandler(w http.ResponseWriter, r *http.Request) { const MAX_UPLOAD_SIZE = 10 << 20 // Set the max add measurement to 10 MB r.Physique = http.MaxBytesReader(w, r.Physique, MAX_UPLOAD_SIZE) if err := r.ParseMultipartForm(MAX_UPLOAD_SIZE); err != nil { http.Error(w, "The uploaded file is just too massive. Please select a file that is lower than 10MB in measurement", http.StatusBadRequest) return } file, fileHeader, err := r.FormFile("file") if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } defer file.Shut() // Create the uploads folder if it does not exist already err = os.MkdirAll("./photographs", os.ModePerm) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } // Create a brand new file within the uploads listing filename := fmt.Sprintf("/photographs/%dpercents", time.Now().UnixNano(), filepath.Ext(fileHeader.Filename)) dst, err := os.Create("." + filename) if err != nil { fmt.Println(err) http.Error(w, err.Error(), http.StatusInternalServerError) return } defer dst.Shut() // Copy the uploaded file to the required vacation spot _, err = io.Copy(dst, file) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } fmt.Println(filename) response, _ := json.Marshal(map[string]string{"location": filename}) w.Header().Set("Content material-Kind", "utility/json") w.WriteHeader(http.StatusCreated) w.Write(response) }
This perform creates a brand new file within the photographs
listing and copies the incoming file to this new file. It then returns the file’s location as a JSON response, which TinyMCE makes use of to hyperlink the picture.
The ServeImages
perform is fairly easy:
func ServeImages(w http.ResponseWriter, r *http.Request) { fmt.Println(r.URL) fs := http.StripPrefix("/photographs/", http.FileServer(http.Dir("./photographs"))) fs.ServeHTTP(w, r) }
It merely serves the information within the photographs
listing as static information.
The ultimate step is to let TinyMCE know in regards to the /add
route. In new.html
, modify the tinymce.init
name with the next:
tinymce.init( alignleft aligncenter ' + 'alignright alignjustify );
The plugins
choice hundreds the picture add plugin, and the toolbar
choice provides the picture add button to the toolbar. The images_upload_url
choice specifies the path to which the picture shall be uploaded. The relative_urls
, remove_script_host
, and convert_urls
choices are used to transform the relative URLs returned by the /add
path to absolute URLs.
The brand new article web page will now present the picture add button within the toolbar.

Click on on the Add tab and add any picture you need.

It will likely be uploaded and linked to the article.

Lastly, create the template edit.html
with the next code:
{{outline "title"}}Create new article{{finish}} {{outline "scripts"}} <script src="https://cdn.tiny.cloud/1/no-api-key/tinymce/6/tinymce.min.js" referrerpolicy="origin"></script> <script> tinymce.init( ' + 'removeformat ); </script> {{finish}} {{outline "physique"}} <type methodology="publish" motion="/articles/{{.ID}}"> <enter kind="textual content" title="title" worth="{{.Title}}"> <textarea id="mytextarea" title="content material">{{.Content material}}</textarea> <enter kind="hidden" title="_method" worth="PUT"> <button id="submit" kind="submit" onclick="submitForm()">Edit</button> </type> {{finish}}
It’s similar to the brand new article type, besides it populates the shape with the article’s title and content material. The motion
attribute of the shape is about to /articles/{id}
, and the _method
discipline is about to PUT
to point that the shape is used to edit an article.
Render this template within the EditArticle
perform:
func EditArticle(w http.ResponseWriter, r *http.Request) { article := r.Context().Worth("article").(*Article) t, _ := template.ParseFiles("templates/base.html", "templates/edit.html") err := t.Execute(w, article) catch(err) }
Now you can click on on the Edit button on the article web page to edit the article.

After modifying, you’ll be redirected to the identical web page, now displaying the up to date article.

You can too click on on the Delete button to delete the article.
Conclusion
When you’d wish to assessment all the code for this mission in a single place, you are able to do so right here. Templates in Go supply strong functionalities associated to customizing output codecs. The html/template
package deal specifically sees intensive use in internet growth attributable to its capacity to output safe HTML. On this article, you realized tips on how to use the html/template
package deal to create a easy weblog utility.