8 html 常用标签与模板

8 html 常用标签与模板

web 开发就是动态的生成响应结果

比如有时候我们需要给用户响应一个 html 格式,html 是一个超文本标记语言。

HTML 是用来描述网页的一种语言。

  • HTML 指的是超文本标记语言 (Hyper Text Markup Language)

  • HTML 不是一种编程语言,而是一种标记语言 (markup language)

  • 标记语言是一套标记标签 (markup tag)

  • HTML 使用标记标签来描述网页

HTML 标签

HTML 标记标签通常被称为 HTML 标签 (HTML tag)。

  • HTML 标签是由尖括号包围的关键词,比如 <html>

  • HTML 标签通常是成对出现的,比如  和 

  • 标签对中的第一个标签是开始标签,第二个标签是结束标签

  • 开始和结束标签也被称为开放标签闭合标签

HTML 文档 = 网页

  • HTML 文档描述网页

  • HTML 文档包含 HTML 标签和纯文本

  • HTML 文档也被称为网页

  • Web 浏览器的作用是读取 HTML 文档,并以网页的形式显示出它们。浏览器不会显示 HTML 标签,而是使用标签来解释页面的内容:

如上图:

  • <html> 与 <html> 之间的文本描述网页

  • <body> 与 <body> 之间的文本是可见的页面内容

  • <h1> 与 <h1> 之间的文本被显示为标题

  • <p> 与 <p> 之间的文本被显示为段落

8.1 HTML 模板第一个范例

我们一般更多的时候实在 BODY 中进行编写,body 里面的话就是告诉用户我要显示的内容

第一个范例:

package main

import (
    "fmt"
    "net/http"
)

// 定义 html 变量
var html = `
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>我的第一个页面</title>
    </head>
    <body>
        我叫zz
    </body>
</html>
`

func main() {
    addr := ":8888"

    // 动态的生成响应结果
    http.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
        fmt.Fprint(rw, html)
    })

    http.ListenAndServe(addr, nil)
}

浏览器访问,就会得到我的第一个页面,并且还会将 body 中的 我叫zz 的数据渲染到浏览器上

8.2 HTML 中常用标签

常用标签

  • 注释 <!-- 注释内容 -->

  • 标题 h1~h6

  • 段落 p

  • 超链接 a

  • 图片img

  • 表单form

    • input: text/password/radio/checkbox/file/date/datetime/url/submit/hidden

    • textarea

    • select/option

  • 按钮 button

  • 表格table/thead/tbody/tr/td/th

  • 列表 ol/ul/li 有序列别和无序列表

  • div/span

标签范例演示

package main

import (
    "fmt"
    "net/http"
)

// 定义 html 变量
var html = `
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>我的第一个页面</title>
    </head>
    <body>
        <!-- 我是注释 -->
        我叫zz

        <!-- 标题范例 -->
        <h1> 1级标题 </h1>
        <h2> 2级标题 </h2>
        <h3> 3级标题 </h3>
        <h4> 4级标题 </h4>
        <h5> 5级标题 </h5>
        <h5> 6级标题 </h6>

        <!-- 段落 -->
        <p> 第1段 </p>
        <p> 第2段 </p>
        <p> 第3段 </p>

        <!-- 超链接 -->
        <!-- href="http://www.baidu.com" 可以跳转百度 -->
        <!-- target="_blank" 打开新的页面不会关闭旧页面-->
        <a href="http://www.baidu.com" target="_blank">百度</a>

        <!-- 在页面展示图片,src 直接可以写对应图片的 url -->
        <img src="http://39.105.137.222:8089/wp-content/uploads/2021/07/image-20210723114444753.png" />

        <!-- 按照表格的方式显示数据 -->
        <table>
            <thead>
                <tr>
                    <!-- 标头 -->
                    <th>ID</th>
                    <th>姓名</th>
                    <th>年龄</th>
                    <th>性别</th>
                </tr>
            </thead>

            <!-- 显示表格数据 -->
            <tbody>
                <tr>
                    <td>01</td>
                    <td>zzz</td>
                    <td>北京</td>
                </tr>
                <tr>
                    <td>02</td>
                    <td>ggg</td>
                    <td>重庆</td>
                </tr>
            </tbody>
        </table>

        <!-- 列表 -->
        <ol>
            <li>有序列表</li>
            <li>洗衣</li>
            <li>做饭</li>
            <li>睡觉</li>
        </ol>

        <ul>
            <li>无序列表</li>
            <li>上班</li>
            <li>学习</li>
            <li>养老</li>
        </ul>
    </body>

    <!-- 块标签 -->
    <div>块1</div>
    <div>块2</div>
    <span>SPAN1</span>
    <span>SPAN2</span>

    <!-- 提交数据 action提交位置 method提交方式,默认为 GET -->
    <form action="" method="GET">
        <!-- label显示输入信息,这个输入信息名为 用户名 类型是 text, br为换行-->
        <label>用户名</label><input name="用户名" type="text" /> <br/>
        
        <!-- label显示输入信息,这个输入信息名为 用户密码 类型是 password 输入的时候是隐式的 -->
        <label>用户密码</label><input name="用户密码" type="password" /><br/>

        <!-- 选择标签,在 web 页面提供选择功能 -->
        <label>性别</label><br/>
            <!-- radio 中 name 一样的话表示为一组 --> 
            <input type="radio" name="sex" value="1"/><label>男</label><br/>
            <input type="radio" name="sex" value="0"/><label>女</label><br/>

        <!-- 多选框,也就是说可以同时选择多个属性 -->
        <label>爱好</label><br/>
            <!-- checkbox 中 hobby 一样的话表示为一组 --> 
            <input type="checkbox" name="hobby" value="1"/><label>足球</label><br/>
            <input type="checkbox" name="hobby" value="2"/><label>篮球</label><br/>
            <input type="checkbox" name="hobby" value="3"/><label>曲棍球</label><br/>

        <!-- 下拉框 -->
        <label>部门下拉框</label><br/>
            <select name="department">
                <option value="dev">开发</option>
                <option value="test">测试</option>
                <option value="ops">运维</option>
            </select><br/>

        <!-- 有些时候我们需要输入很大一块信息就使用下面这个标签 -->  
        <label>备注</label>
            <textarea name="remark">我叫...</textarea><br/>

        <!-- 登录按钮 类型为 submit,默认显示为提交按钮,通过 url 进行提交当我们想修改他的默认显示,通过 value-->
        <input type="submit" value="Q我"/><br/>
    </form>
</html>
`

func main() {
    addr := ":8888"

    // 动态的生成响应结果
    http.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
        fmt.Fprint(rw, html)
    })

    http.ListenAndServe(addr, nil)
}

浏览器渲染

接下来我们如何将 HTML 应用到我们的项目中去

8.3 将 HTML 应用到项目中

上面案例中只要修改了 html 文本我们就需要重启程序,这个时候我们就需要将 html 放入到一个文件中去,我们在请求的时候只需要在文件中拿去就可以了。

这里我们创建一个 template 目录,将我们的 html 文本内容复制到这个 index.html 文件中,然后再通过 go 将这个文件读取出来

package main

import (
    "fmt"
    "io"
    "net/http"
    "os"
)

func main() {
    addr := ":8888"

    filePath := `./template/index.html`

    // 动态的生成响应结果
    http.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
        // 打开 index.html 文件
        f, err := os.Open(filePath)
        if err != nil {
            fmt.Println(err)
            return
        }

        // 客户端获取到服务器端 body
        io.Copy(rw, f)
    })

    http.ListenAndServe(addr, nil)
}

这样做的好处就是我们去修改这个 index.html 文件的内容也不需要重启程序,而是每次程序自动加载,这里我修改了 我叫zz1111111 的字段,浏览器中的加载

现在虽然解决了 html 文件修改后自动加载了,但是依旧没有解决动态生成页面的问题。下面我们就需要了解 template 模板技术

8.4 template 模板技术

在 go 中提供了以下两种模板技术

  1. text/template:实现数据驱动模板以生成文本输出,可以理解为一组文字按照特定格式动态嵌入另一组文字中。

  2. html/template:用于 web 开发,防止注入的功能,模板 + 数据 + 引擎(用于组装为一个字符串)

func template.New(name string) *template.Template
[10:26:46 root@go data]#go doc template.template
package template // import "html/template"

type Template struct {

        // The underlying template's parse tree, updated to be HTML-safe.
        Tree *parse.Tree
        // Has unexported fields.
}
    Template is a specialized Template from "text/template" that produces a safe
    HTML document fragment.

func Must(t *Template, err error) *Template
func New(name string) *Template
func ParseFS(fs fs.FS, patterns ...string) (*Template, error)
func ParseFiles(filenames ...string) (*Template, error)
func ParseGlob(pattern string) (*Template, error)
func (t *Template) AddParseTree(name string, tree *parse.Tree) (*Template, error)
func (t *Template) Clone() (*Template, error)
func (t *Template) DefinedTemplates() string
func (t *Template) Delims(left, right string) *Template
func (t *Template) Execute(wr io.Writer, data interface{}) error    # 将数据获取
func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) error
func (t *Template) Funcs(funcMap FuncMap) *Template
func (t *Template) Lookup(name string) *Template
func (t *Template) Name() string
func (t *Template) New(name string) *Template
func (t *Template) Option(opt ...string) *Template
func (t *Template) Parse(text string) (*Template, error)    # 用于解析
func (t *Template) ParseFS(fs fs.FS, patterns ...string) (*Template, error)
func (t *Template) ParseFiles(filenames ...string) (*Template, error)
func (t *Template) ParseGlob(pattern string) (*Template, error)
func (t *Template) Templates() []*Template

8.4.1 template 模板语法入门

8.4.1.1 template 基础语法

范例如下:

package main

import (
    "fmt"
    "os"
    "text/template"
)

func main() {
    // 在使用模板之前需要先解析模板

    // 定义 tpl 模板,在模板中占位通过两个{} 中间一个 .
    text := "我的名字加{{.}}\n"

    // 解析模板:模板是一个字符串
    tpl, err := template.New("test").Parse(text)
    if err != nil {
        fmt.Println(err)
        return
    }

    // 通过 Execute 传递 zz ,这里的 zz 就会替代上面text 中的{{.}}
    // 由于 Execute 中第二个参数是一个空接口所有能够接受任何数据类型
    // 下面是基础语法演示范例
    tpl.Execute(os.Stdout, "zz")
    tpl.Execute(os.Stdout, 1)
    tpl.Execute(os.Stdout, true)
    tpl.Execute(os.Stdout, []int{1, 2, 3})
    tpl.Execute(os.Stdout, map[string]string{"1": "2"})
}
[10:40:27 root@go template]#go run main.go 
我的名字加zz
我的名字加1
我的名字加true
我的名字加[1 2 3]
我的名字加map[1:2]

8.4.1.2 template 流程判断语法

在 template 中也可以实现对语句的流程判断

package main

import (
    "os"
    "text/template"
)

func main() {
    // 还能用于判断,必须通过 end 结尾
    // if . 判断 . 是否为真,真就执行输出男, else 输出女
    text := "性别: {{ if .}} 男 {{ else }} 女 {{ end }}\n"
    tpl, _ := template.New("test").Parse(text)

    // 输入男
    tpl.Execute(os.Stdout, true)

    // 输出女
    tpl.Execute(os.Stdout, false)

    // if 大于小于判断语法
    // gt >
    // lt <
    // gte >=
    // lte <=
    // eq =
    // neq !=
    text = "年龄是否超过 18 : {{ if eq 18 .}} 等于 18 {{else }} 不等于 18 {{end}}\n"
    tpl, _ = template.New("test").Parse(text)
    tpl.Execute(os.Stdout, 1)

}
[10:55:30 root@go template]#go run main.go 
性别:  男 
性别:  女 
年龄是否超过 18 :  不等于 18 

8.4.1.3 template 循环

在 template 中遍历数据

package main

import (
    "os"
    "text/template"
)

func main() {
    // 遍历使用 range , 结束使用 end
    // range括号里面的 . 是每次需要遍历的元素。外部 . 是每次遍历后的结果
    // 这里竖线为分输出的时候用于隔符
    text := "学生列表:{{ range . }} {{.}}| {{end}}\n"
    tpl, _ := template.New("test").Parse(text)
    tpl.Execute(os.Stdout, []string{"aa", "bb", "cc", "dd"})

}
[11:03:19 root@go template]#go run main.go 
学生列表: aa|  bb|  cc|  dd| 

8.4.2 template 复制数据结构

8.4.2.1 获取切片中的元素

package main

import (
    "os"
    "text/template"
)

func main() {
    // . 为我们传递到模板中的数据,如切片
    // index . 0 就是获取索引为 0 的元素
    text := "第一个元素:{{ index . 0}}\n第二个元素:{{index . 1}}\n"
    tpl, _ := template.New("test").Parse(text)
    tpl.Execute(os.Stdout, []string{"1", "2"})

}
[11:08:31 root@go template]#go run main.go 
第一个元素:1
第二个元素:2

8.4.2.2 获取 map 中的元素

package main

import (
    "os"
    "text/template"
)

func main() {
    // . 为我们传递到模板中的数据,如 map
    // {{ .name }} 获取 map 中 name 的值 zzz
    // {{ .addr }} 获取 map 中 addr 的值 111
    text := "name:{{ .name }} addr:{{ .addr }}\n"
    tpl, _ := template.New("test").Parse(text)
    tpl.Execute(os.Stdout, map[string]string{"name": "zzz", "addr": "111"})

    // 如果传递的 map 中没有 addr 的值,输出 no value
    tpl.Execute(os.Stdout, map[string]string{"name": "zzz"})

}
[11:12:15 root@go template]#go run main.go 
name:zzz addr:111
name:zzz addr:<no value>

8.4.2.3 获取结构体数据

package main

import (
    "os"
    "text/template"
)

func main() {
    // . 为我们传递到模板中的数据,如结构体
    // . Name 获取结构体中 Name 属性
    // . Addr 获取结构体中 Addr 属性
    text := "name:{{ .Name}} addr:{{ .Addr}}\n"
    tpl, _ := template.New("test").Parse(text)

    // 这里传入我们的 结构体
    tpl.Execute(os.Stdout, struct {
        Name string
        Addr string
    }{"zzz", "bbb"})

}


/*
    下面这种写法也是支持的,就是在结构体中多个属性的数据类型一样可以通过这种方式缩写:
    tpl.Execute(os.Stdout, struct{ Name, Addr string }{"zzz", "bbb"})

*/
[11:20:20 root@go template]#go run main.go 
name:zzz addr:bbb

8.4.3 从文件中解析模板

当然也可以将模板的数据写到文件中,然后通过 template.ParseFiles 方法进行文件解析

func (*template.Template).ParseFiles(filenames ...string) (*template.Template, error)

创建一个 user.html 文件

package main

import (
    "os"
    "text/template"
)

func main() {
    // 从 user.html 文件中解析数据
    tpl, _ := template.ParseFiles("user.html")

    // 通过 ExecuteTemplate 执行 user.html
    // 当我们有多个 模板文件的时候,需要在第二个参数的地方指定我们要执行的模板文件
    tpl.ExecuteTemplate(os.Stdout, "user.html", struct{ Name, Addr string }{"zz", "bb\n"})
}
[12:34:14 root@go template]#go run main.go 
我的第一个模板
name:zz addr:bb

8.4.4 模板结合web 页面使用(用户管理列表)

两层遍历,第一次遍历切片,然后再遍历 map,如下范例,我们将用户信息输出再浏览器上

1.编写程序文件

package main

import (
    "log"
    "net/http"
    "text/template"
)

// 创建用户的结构体
type User struct {
    Id   int
    Name string
    Sex  bool
    Addr string
}

func main() {
    // 多个用户信息使用到结构体切片
    users := []*User{
        {1, "aa", true, "xxxxx1"},
        {2, "bb", false, "xxxxx2"},
        {3, "cc", true, "xxxxx3"},
    }

    addr := ":8888"

    // 创建一个 http 的页面
    http.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
        // 将 template/user.html 进行解析
        tpl, err := template.ParseFiles("template/user.html")
        if err != nil {
            log.Println(err)
            return
        }

        // 输出到 rw 也就是网页上,指定使用 user.html 文件为模板,传递数据是 users 结构体
        tpl.ExecuteTemplate(rw, "user.html", users)
    })

    // 由于在 html 中新建页面跳转到 create 的 URI 所以我们需要在写一个 create 的处理函数
    http.HandleFunc("/create/", func(rw http.ResponseWriter, r *http.Request) {
        tpl, err := template.ParseFiles("template/create.html")
        if err != nil {
            log.Printf("[create err ]: %v", err)
            return
        }

        // 没有传递数据第三个参数我们写 nil 即可
        tpl.ExecuteTemplate(rw, "create.html", nil)
    })

    http.ListenAndServe(addr, nil)
}

2.创建 user.html 模板文件

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>用户管理</title>
    </head>
    <body>
        <!-- 新建页面跳转到 create URI -->
        <a href="/create/">新建</a>
        <table>
            <thead>
                <tr>
                    <th>用户Id</th>
                    <th>用户名</th>
                    <th>性别</th>
                    <th>地址</th>
                    <th>操作</th>
                </tr>
            </thead>
            <tbody>
                <!-- 定义模板循环遍历传递过来的 users []结构体 -->
                {{ range . }}
                    <!-- range 每个元素就是一行,开始遍历传递过来的切片中的单个属性,以表格数据显示 -->
                    <tr>
                        <td>{{.Id}}</td>
                        <td>{{.Name}}</td>
                        <td>{{if .Sex}} 男 {{ else }}女 {{ end }}</td>
                        <td>{{.Addr}}</td>
                        <td>
                            <a>编辑</a>
                            <a>删除</a>
                        </td>
                    </tr>
                    {{ . }}
                {{ end }}
            </tbody>
        </table>
    </body>
</html>

3.编写 create.html 文件

由于我们实现了跳转所以需要在编写一个 create.html 文件

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8"/>
        <title>新建页面</title>
    </head>
    <body>
        <!-- 提交到 create 路径下,提交方式为 post -->
        <form action="/create/" method="POST">
            <label>姓名</label><input name="name" value=""/> <br/>
            <label>性别</label>
                <input name="sex" type="radio" value="1"/>
                <input name="sex" type="radio" value="0"/><br/>
            <label>住址</label><textarea name="addr" value=""></textarea> <br/>
            <input type="submit" value="创建" />
        </form>
    </body>
</html>

浏览器访问 / 路径

点击跳转之后访问的 create 路径

但是当我们输入了用户姓名和性别点击创建之后,并且请求方式为 POST,处理器函数又把 create 页面加载出来等于说我们什么事都没有干,那我们可以通过请求方式进行判断

那这个时候我们就又个问题,我们该如何判断 form 是通过 A 标签来的呢?所以这个时候我们就可以在开发的时候标识他们来自于那个标签。

这个时候我们 F12 打开该页面的 headers ,可以观察到他是一个 GET 请求,但是我们在 create.html 文件中已将 method 改为了 POST ,所以这个时候我们可以在 主程序中通过 POST 来进行判断,如果是 GET 请求的话我们加载页面,如果是 POST 请求的话我们需要添加数据,并且添加完了数据之后我们还需要跳转到用户列表页面

8.4.4.1 判断请求方法来进行处理(用户添加)

package main

import (
    "log"
    "net/http"
    "text/template"
)

// 创建用户的结构体
type User struct {
    Id   int64
    Name string
    Sex  bool
    Addr string
}

func main() {
    // 多个用户信息使用到结构体切片
    users := []*User{
        {1, "aa", true, "xxxxx1"},
        {2, "bb", false, "xxxxx2"},
        {3, "cc", true, "xxxxx3"},
    }

    addr := ":8888"

    // 创建一个 http 的页面
    http.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
        // 将 template/user.html 进行解析
        tpl, err := template.ParseFiles("template/user.html")
        if err != nil {
            log.Println(err)
            return
        }

        // 输出到 rw 也就是网页上,指定使用 user.html 文件为模板,传递数据是 users 结构体
        tpl.ExecuteTemplate(rw, "user.html", users)
    })

    // 由于在 html 中新建页面跳转到 create 的 URI 所以我们需要在写一个 create 的处理函数
    http.HandleFunc("/create/", func(rw http.ResponseWriter, r *http.Request) {

        // Method == "GET" 直接加载页面
        if r.Method == "GET" {
            tpl, err := template.ParseFiles("template/create.html")
            if err != nil {
                log.Printf("[create err ]: %v", err)
                return
            }
            // 没有传递数据第三个参数我们写 nil 即可
            tpl.ExecuteTemplate(rw, "create.html", nil)
        } else {
            // 否则添加数据,通过 creat.html 中的表名字段来获取值
            users = append(users, &User{

                // 每次 id 加1
                int64(len(users) + 1),
                r.FormValue("name"),
                r.FormValue("sex") == "1",
                r.FormValue("addr"),
            })

            // 现在数据已经添加了我们就需要跳转到 用户页面,重定向让后端告诉浏览器重新请求地址
            // Redirect 第一个参数是 http.ResponseWriter
            // Redirect 第二个参数是 *http.Request
            // Redirect 第三个参数是 重定向的 URL
            // Redirect 第四个参数是 状态码,302 临时重定向
            http.Redirect(rw, r, "/", 302)
        }

    })

    http.ListenAndServe(addr, nil)
}

启动程序

点击新建

跳转到 create URI,创建数据点击提交

完成之后跳转到了 / 页面,并有了 id 为 4 的用户信息

8.4.4.2 判断请求方法来进行处理(用户删除)

我们一般都是说删除哪一个数据,删除的话肯定需要给后端传递想要删除的数据。

我们可以通过用户 ID 进行删除,并且一定会有一个想要删除的函数,所以就的有一个 delete 页面

package main

import (
    "log"
    "net/http"
    "strconv"
    "text/template"
)

// 创建用户的结构体
type User struct {
    Id   int64
    Name string
    Sex  bool
    Addr string
}

func main() {
    // 多个用户信息使用到结构体切片
    users := []*User{
        {1, "aa", true, "xxxxx1"},
        {2, "bb", false, "xxxxx2"},
        {3, "cc", true, "xxxxx3"},
    }

    addr := ":8888"

    // 显示用户列表
    // 创建一个 http 的页面
    http.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
        // 将 template/user.html 进行解析
        tpl, err := template.ParseFiles("template/user.html")
        if err != nil {
            log.Println(err)
            return
        }

        // 输出到 rw 也就是网页上,指定使用 user.html 文件为模板,传递数据是 users 结构体
        tpl.ExecuteTemplate(rw, "user.html", users)
    })

    // 用户添加
    // 由于在 html 中新建页面跳转到 create 的 URI 所以我们需要在写一个 create 的处理函数
    http.HandleFunc("/create/", func(rw http.ResponseWriter, r *http.Request) {

        // Method == "GET" 直接加载页面
        if r.Method == "GET" {
            tpl, err := template.ParseFiles("template/create.html")
            if err != nil {
                log.Printf("[create err ]: %v\n", err)
                return
            }
            // 没有传递数据第三个参数我们写 nil 即可
            tpl.ExecuteTemplate(rw, "create.html", nil)
        } else {
            // 否则添加数据,通过 creat.html 中的表名字段来获取值
            users = append(users, &User{

                // 每次 id 加1
                int64(len(users) + 1),
                r.FormValue("name"),
                r.FormValue("sex") == "1",
                r.FormValue("addr"),
            })

            // 现在数据已经添加了我们就需要跳转到 用户页面,重定向让后端告诉浏览器重新请求地址
            // Redirect 第一个参数是 http.ResponseWriter
            // Redirect 第二个参数是 *http.Request
            // Redirect 第三个参数是 重定向的 URL
            // Redirect 第四个参数是 状态码,302 临时重定向
            http.Redirect(rw, r, "/", 302)
        }
    })

    // 用户删除
    http.HandleFunc("/delete/", func(rw http.ResponseWriter, r *http.Request) {
        // 删除肯定需要先获取数据,这里是通过 id 删除
        // FormValue 返回值是一个 string 所以我们需要将 string 转为一个 int64 类型
        // 判断我们输入的想要删除的 id 是否和我们 user 结构体里面的 id 相等

        // ParseInt 第一个参数为 string 类型,10 为 十进制,64 为 int 64 类型
        if id, err := strconv.ParseInt(r.FormValue("id"), 10, 64); err == nil {

            // make 一个切片长度为 0 个元素
            NewUsers := make([]*User, 0)
            for _, user := range users {

                // 将 users 赋值给了 user 遍历判断 user.ID 和用传入的 ID 是否相同
                // 相同就退出当前当前循环,进入到下一次循环赋值
                if user.Id == id {
                    continue
                }

                // 将 id 判断后的 user 追加给 NewUsers 结构体切片
                NewUsers = append(NewUsers, user)
            }

            // 最后重新赋值给 users 变量
            users = NewUsers
        }

        // 删除之后重定向到用户列表页面
        http.Redirect(rw, r, "/", 302)

    })

    http.ListenAndServe(addr, nil)
}

现在已经将用户删除、用户添加、用户显示、功能写完了,其实后端功能有了,但是前端还没有数据,所以开始编写前端代码

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>用户管理</title>
    </head>
    <body>
        <!-- 新建页面跳转到 create URI -->
        <a href="/create/">新建</a>
        <table>
            <thead>
                <tr>
                    <th>用户Id</th>
                    <th>用户名</th>
                    <th>性别</th>
                    <th>地址</th>
                    <th>操作</th>
                </tr>
            </thead>
            <tbody>
                <!-- 定义模板循环遍历传递过来的 users []结构体 -->
                {{ range . }}
                    <!-- range 每个元素就是一行,开始遍历传递过来的切片中的单个属性,以表格数据显示 -->
                    <tr>
                        <td>{{.Id}}</td>
                        <td>{{.Name}}</td>
                        <td>{{if .Sex}} 男 {{ else }}女 {{ end }}</td>
                        <td>{{.Addr}}</td>
                        <td>
                            <a>编辑</a>
                            <!-- web 页面输入 id = 遍历当前用户的 id 执行 后端 delete 函数-->
                            <a href="/delete/?id={{ .Id}}">删除</a>
                        </td>
                    </tr>
                    {{ . }}
                {{ end }}
            </tbody>
        </table>
    </body>
</html>

 

浏览器访问,从图中我们看到是 3 个用户信息

这里我删除了 id 为 3 的用户信息,已被删除

点击新建,也能创建一个 test 用户

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇