Flask 项目实战 3: 前端实现
待做清单程序的总体结构分为前端和后端两个部分,上一节介绍了后端的实现,本节讲解前端的实现。
1. 首页模板 templates/index.html
1.1 引入相关的库
<html>
<head>
<Meta charset='utf-8'>
<script src="https://lib.baomitu.com/jquery/2.2.4/jquery.min.js"></script>
<link href="https://lib.baomitu.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
<link href="{{url_for('static', filename='style.css')}}" rel="stylesheet">
<script src="{{url_for('static', filename='script.js')}}"></script>
<title>每日清单</title>
</head>
在第 4 行和第 5 行,引入 JQuery 和 font-awesome 库。
在第 6 行,引入网站的样式文件 static/style.css,函数 url_for(‘static’, filename=‘style.css’) 表示 static 文件夹下的文件 style.css,它的输出为 static/style.css。
在第 7 行,引入 JS 文件 static/script.js,函数 url_for(‘static’, filename=‘script.js’) 表示 static 文件夹下的文件 static/script.js,它的输出为 static/script.js。
<body>
<div class='header'>
<i class="fa fa-calendar-plus-o"></i> 待做清单
{% if hasLogin %}
<span class='login'>
<i class="fa fa-sign-out"></i>
<a href='/users/logout'>退出</a>
</span>
{% else %}
<span class='login'>
<i class="fa fa-sign-in"></i>
<a href='/users/login'>登录</a>
<i class="fa fa-user-plus"></i>
<a href='/users/register'>注册</a>
</span>
{% endif %}
</div>
1.3 输入待做事项的文本框
{% if hasLogin %}
<div>
<input type="text" class="row" placeholder="输入待办事项">
<i class="fa fa-fw fa-plus-square" onclick='onAddTodo(this);'></i>
</div>
{% endif %}
1.4 待做清单和完成清单
{% for todo in todos %}
<div>
<input type="text" class="row" value="{{todo.title}}">
<i class="fa fa-fw fa-check" onclick='onUpdatetodo({{todo.todoId}});'></i>
</div>
{% endfor %}
<div class='header'>
<i class="fa fa-calendar-check-o"></i> 完成清单
</div>
{% for done in dones %}
<div>
<input type="text" class="row" value="{{done.title}}">
<i class="fa fa-fw fa-trash" onclick='onDeletetodo({{done.todoId}});'></i>
</div>
{% endfor %}
</body>
</html>
在第 1 行,遍历 todos,展示所有的待做事项;在第 4 行,显示了一个 font awsome 图标 check,点击该图标表示已经完成该项,将待做事项移入到完成清单中,调用函数 onUpdatetodo(todo.todoId) 向后端请求更新待做事项的 status 为 ‘done’。
在第 12 行,遍历 dones,展示所有的完成事项;在第 15 行,显示了一个 font awsome 图标 delete,点击该图标,调用函数 onDeletetodo(todo.todoId) 向后端请求删除待做事项。
2.1 引入相关文件
<html>
<head>
<Meta charset='UTF-8'>
<link href="https://lib.baomitu.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
<link href="{{url_for('static', filename='style.css')}}" rel="stylesheet">
<title>注册</title>
</head>
在第 4 行和第 5 行,引入font-awesome 库。
在第 5 行,引入网站的样式文件 static/style.css,函数 url_for(‘static’, filename=‘style.css’) 表示 static 文件夹下的文件 style.css,它的输出为 static/style.css。
2.2 注册表单
<body>
<h3><i class='fa fa-user-plus'></i> 注册</h3>
<form action="/users/register" method="POST">
<div class="row">
{{ form.name.label }}
{{ form.name() }}
<b>{{ form.name.errors[0] }}</b>
</div>
<div class="row">
{{ form.password.label }}
{{ form.password() }}
<b>{{ form.password.errors[0] }}</b>
</div>
<div class="row">
{{ form.submit() }}
</div>
{{ form.hidden_tag() }}
</form>
</body>
</html>
<html>
<head>
<Meta charset='UTF-8'>
<link href="https://lib.baomitu.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
<link href="/static/style.css" rel="stylesheet">
<title>登录</title>
</head>
<body>
<div class="header"><i class='fa fa-sign-in'></i> 登录</div>
<form action="/users/login" method="POST">
<div class="row">
<label for="name">姓名</label>
<input id="name" name="name" required type="text" value="">
<b></b>
</div>
<div class="row">
<label for="password">密码</label>
<input id="password" name="password" required type="password" value="">
<b></b>
</div>
<div class="row">
<input id="submit" name="submit" type="submit" value="登录">
</div>
<input id="csrf_token" name="csrf_token" type="hidden" value="ImRlYTZjZDEwZjU3YjNjNGY0MDVkMDc4ZDhiZTMwNWM1OTk2MjhiMzAi.X2LvVA.0x7iz2PGVHH-r8dWf7KQNMkuSAE">
</form>
</body>
</html>
这里注意两点:
<html>
<head>
<Meta charset='UTF-8'>
<link href="https://lib.baomitu.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
<link href="{{url_for('static', filename='style.css')}}" rel="stylesheet">
<title>登录</title>
</head>
<body>
<div class="header"><i class='fa fa-sign-in'></i> 登录</div>
<form action="/users/login" method="POST">
<div class="row">
{{ form.name.label }}
{{ form.name() }}
<b>{{ form.name.errors[0] }}</b>
</div>
<div class="row">
{{ form.password.label }}
{{ form.password() }}
<b>{{ form.password.errors[0] }}</b>
</div>
<div class="row">
{{ form.submit() }}
</div>
{{ form.hidden_tag() }}
</form>
</body>
</html>
登录页面 templates/login.html 与注册页面 templates/register.html 几乎完全相同,除了 title 标签不一样。请参考对 templates/register.html 的解释。
3. 调用后端服务 static/script.js
3.1 配置 Ajax
function ajaxError()
{
alert('ajax error');
}
function ajaxSuccess(result)
{
if (result.error) {
alert('操作失败');
return;
}
location.reload();
}
客户端使用 ajax 技术请求服务端的服务。当 ajax 请求失败时,调用 ajaxError,提示用户 ajax 请求服务失败;当 ajax 请求成功时,调用 ajaxSuccess,提示用户 ajax 请求服务成功。
3.2 新增待做事项
function onAddTodo(button)
{
var children = $(button).parent().children();
var title = children.eq().val();
var data = JSON.stringify({'title': title});
$.ajax({
'url': '/todos/add',
'type': 'POST',
'contentType': 'application/json',
'data': data,
'dataType': 'json',
'error': ajaxError,
'success': ajaxSuccess
});
}
点击 “新增” 按钮后,执行函数 onAddTodo(button),button 指向的是 “新增” 按钮。在 templates/index.html 中,按钮、待做事项位于相同的 DIV 中,如下所示:
<div>
<input type="text" class="row" placeholder="输入待办事项">
<i class="fa fa-fw fa-plus-square" onclick='onAddTodo(this);'></i>
</div>
在第 3 行到第 4 行,表达式的含义如下所示:
表达式 | 含义 |
---|---|
$(button).parent() | 指向按钮的父节点 |
$(button).parent().children() | 表示 div 的 2 个子节点 |
children.eq(0) | 指向待做事项的文本框 |
children.eq(0).val() | 待做事项的文本框的值 |
3.3 更新待做事项
function onUpdatetodo(todoId)
{
var data = JSON.stringify({'todoId': todoId});
$.ajax({
'url': '/todos/update',
'type': 'POST',
'contentType': 'application/json',
'data': data,
'dataType': 'json',
'error': ajaxError,
'success': ajaxSuccess
});
}
3.4 删除待做事项
function onDeletetodo(todoId)
{
var data = JSON.stringify({'todoId': todoId});
$.ajax({
'url': '/todos/delete',
'type': 'POST',
'contentType': 'application/json',
'data': data,
'dataType': 'json',
'error': ajaxError,
'success': ajaxSuccess
});
}
4. 样式文件 static/style.css
style.css 是网站的样式文件,设置尺寸、字体大小等信息。
body {
width: px;
margin: auto;
}
a {
text-decoration: none;
}
.fa {
cursor: pointer;
}
.login {
font-size: ;
font-weight: normal;
}
.header {
padding-top: px;
padding-bottom: px;
font-size: ;
font-weight: bold;
}
.row {
width: px;
padding-top: px;
padding-bottom: px;
margin-top: px;
margin-bottom: px;
}
对 class 等于 fa 的元素设置,显示光标的形状为手形。
待做事项和完成事项的 class 等于 row,设置上下的 padding 和 margin,让它们互相之间存在一个间隔。
5. 小结
本节讲解了前端的实现,使用思维导图概括如下: