使用AngularJS实现登录与注册
html和css布局
我们采用html+css框架bootstrap4,里面有很多已定义好的组件,可以拿过来直接用,非常简单,对于实现我们想要的功能,效率非常高。点击查看boststrap4官方文档。
我想把登录界面和注册界面集成在同一个页面上,于是想起了bs中的navs组件,利用该组件就可以切换不同的表单。
<nav>
<div class="nav nav-tabs" id="nav-tab" role="tablist">
<a class="nav-item nav-link w-50 text-center active"
id="nav-login-tab"
href="#nav-login"
role="tab"
data-toggle="tab"
aria-controls="nav-login"
aria-selected="true">
<strong>Login</strong>
</a>
<a class="nav-item nav-link w-50 text-center"
id="nav-register-tab"
href="#nav-register"
role="tab"
data-toggle="tab"
aria-controls="nav-register"
aria-selected="false">
<strong>Register</strong>
</a>
</div>
</nav>
通过引入jQuery,bs4可以自动帮你完成界面的切换,点击<a>链接实际指向以下内容:
<div class="tab-content mt-4" id="nav-tabContent">
<div class="tab-pane fade show active" id="nav-login" role="tabpanel" aria-labelledby="nav-login-tab">
......
</div>
<div class="tab-pane fade" id="nav-register" role="tabpanel" aria-labelledby="nav-register-tab">
......
</div>
</div>
通过在tab-pane中放置form表单就可以达到登录界面和注册界面的切换,以下是登录界面的form代码:
<form novalidate="novalidate" name="loginForm">
<div class="form-group">
<label for="lemail">Account</label>
<input type="email"
class="form-control my-input"
id="lemail"
placeholder="Enter your account"
name="lemail"
required="required"
ng-minlength="6"
ng-maxlength="18"
ng-model="login_user.email"
email-check-login>
<div ng-cloak ng-if="loginForm.lemail.$dirty&&loginForm.lemail.$invalid">
<span class="error" ng-if="loginForm.lemail.$error.required">
Your account is required.
</span>
<span class="error" ng-if="!loginForm.lemail.$error.minlength&&!loginForm.lemail.$error.maxlength&&loginForm.lemail.$error.email">
It is not a valid account.
</span>
<span class="error" ng-if="loginForm.lemail.$error.minlength">
Your account is required to be at least 6 characters.
</span>
<span class="error" ng-if="loginForm.lemail.$error.maxlength">
Your account cannot be longer than 18 characters.
</span>
<span class="error" ng-if="!loginForm.lemail.$error.email&&loginForm.lemail.$error.email_error">
Your account does not exist.
</span>
</div>
</div>
<div class="form-group">
<label for="lpassword">Password</label>
<input type="password"
class="form-control my-input"
id="lpassword"
placeholder="Enter your password"
name="lpassword"
required="required"
ng-minlength="6"
ng-maxlength="18"
ng-model="login_user.password">
<div ng-cloak ng-if="loginForm.lpassword.$dirty&&loginForm.lpassword.$invalid">
<span class="error" ng-if="loginForm.lpassword.$error.required">
Your password is required.
</span>
<span class="error" ng-if="!loginForm.lpassword.$error.minlength&&!loginForm.lpassword.$error.maxlength&&loginForm.lpassword.$error.password">
It is not a valid password.
</span>
<span class="error" ng-if="loginForm.lpassword.$error.minlength">
Your password is required to be at least 6 characters.
</span>
<span class="error" ng-if="loginForm.lpassword.$error.maxlength">
Your password cannot be longer than 18 characters.
</span>
</div>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="remember-me" ng-model="login_user.remember_me">
<label class="form-check-label" for="remember-me">Remember me, <em>validity will last for one week.</em></label>
</div>
<button type="submit" class="mt-15 btn btn-primary btn-block" ng-disabled="loginForm.$invalid">Login</button>
</form>
登录界面长这个样子:
登录界面.png以下是注册页面代码:
<form novalidate="novalidate" name="registerForm">
<div class="form-group">
<label for="remail">Create Account</label>
<input type="email"
class="form-control my-input"
id="remail"
placeholder="Create your account"
name="remail"
required="required"
ng-minlength="6"
ng-maxlength="18"
ng-model="register_user.email"
email-check>
<div ng-cloak ng-if="registerForm.remail.$dirty&®isterForm.remail.$invalid">
<span class="error" ng-if="registerForm.remail.$error.required">
Your account is required.
</span>
<span class="error" ng-if="!registerForm.remail.$error.minlength&&!registerForm.remail.$error.maxlength&®isterForm.remail.$error.email">
It is not a valid account.
</span>
<span class="error" ng-if="registerForm.remail.$error.minlength">
Your account is required to be at least 6 characters.
</span>
<span class="error" ng-if="registerForm.remail.$error.maxlength">
Your account cannot be longer than 18 characters.
</span>
<!--ensure unique need back-end support-->
<span class="error" ng-if="!registerForm.remail.$error.email&®isterForm.remail.$error.email_error">
Your account is taken, please try another one.
</span>
</div>
</div>
<div class="form-group">
<label for="rpassword">Create Password</label>
<input type="password"
class="form-control my-input"
id="rpassword"
placeholder="Create your password"
name="rpassword"
required="required"
ng-minlength="6"
ng-maxlength="18"
ng-model="register_user.password"
password-check>
<div ng-cloak ng-if="registerForm.remail.$valid&®isterForm.rpassword.$dirty&®isterForm.rpassword.$invalid">
<span class="error" ng-if="registerForm.rpassword.$error.required">
Your password is required.
</span>
<span class="error" ng-if="!registerForm.rpassword.$error.minlength&&!registerForm.rpassword.$error.maxlength&®isterForm.rpassword.$error.email">
It is not a valid password.
</span>
<span class="error" ng-if="registerForm.rpassword.$error.minlength">
Your password is required to be at least 6 characters.
</span>
<span class="error" ng-if="registerForm.rpassword.$error.maxlength">
Your password cannot be longer than 18 characters.
</span>
<!--ensure strength need back-end support-->
<span class="error" ng-if="!registerForm.rpassword.$error.email&®isterForm.rpassword.$error.password_error">
Your password is so weak, please try another one stronger.
</span>
</div>
</div>
<div class="form-group">
<label for="cpassword">Confirm Password</label>
<input type="password"
class="form-control my-input"
id="cpassword"
placeholder="Confirm your password"
name="cpassword"
required="required"
ng-model="register_user.confirm_password"
equal-check="register_user.password">
<div ng-cloak ng-if="registerForm.rpassword.$valid&®isterForm.cpassword.$dirty&®isterForm.cpassword.$invalid">
<span class="error" ng-if="registerForm.cpassword.$error.required">
Confirm password is required.
</span>
<!--confirm password-->
<span class="error" ng-if="!registerForm.cpassword.$error.required&®isterForm.cpassword.$error.not_equal">
The confirm password entered is invalid.
</span>
</div>
</div>
<div class="form-check">
<input type="checkbox"
class="form-check-input"
id="accept"
name="accept"
ng-model="register_user.accept"
accept-check>
<label class="form-check-label" for="accept"><em>I have read all the <a href="#" class="blue" data-toggle="modal" data-target="#acceptTerms">agreements</a>.</em></label>
<div ng-if="registerForm.rpassword.$valid&®isterForm.cpassword.$valid&®isterForm.accept.$dirty&®isterForm.accept.$invalid">
<span class="error" ng-if="registerForm.accept.$error.not_checked">
You must agree with all the terms.
</span>
</div>
</div>
<button type="submit" class="mt-15 btn btn-primary btn-block" ng-disabled="registerForm.$invalid">Register</button>
</form>
注册界面长这个样子:
注册页面.pngjavacript代码和angularjs代码
弹出效果
由于加载的原因,所有错误信息总是会在加载的时候先出现,再马上消失,很难看,所以写了一个js代码避免这个效果:
思路就是先把整个容器的透明度设置为0,加载之后再把透明度调至1,在透明的这段时间内,错误信息无法察觉,代码:
function getStyle(obj,name) {
if(obj.currentStyle){
return obj.currentStyle[name];
}
else{
return getComputedStyle(obj,null)[name];
}
}
function startMove(obj,json,ispeed,fnEnd) {
clearInterval(obj.timer);
obj.timer = setInterval(function () {
var bStop = true;
for(var attr in json){
var cur = 0;
if(attr =='opacity'){
cur = Math.round(parseFloat(getStyle(obj,attr))*100);
}
else{
cur = parseInt(getStyle(obj,attr));
}
var speed = (json[attr]-cur)/ispeed;
speed = speed>0?Math.ceil(speed):Math.floor(speed);
if(cur!=json[attr]){
bStop = false;
}
if(attr == 'opacity'){
obj.style.filter='alpha(opacity:'+(cur+speed)+')';
obj.style.opacity=(cur+speed)/100;
}
else{
obj.style[attr]=cur+speed+'px';
}
}
if(bStop){
clearInterval(obj.timer);
if(fnEnd){
fnEnd();
}
}
},30)
}
window.onload = function () {
var mainForm = document.getElementById('mainForm');
startMove(mainForm,{'opacity':100},6);
};
验证表单
使用angularjs自带的表单状态验证器
ng-valid is set if the form is valid.
ng-invalid is set if the form is invalid.
ng-pending is set if the form is pending.
ng-pristine is set if the form is pristine.
ng-dirty is set if the form is dirty.
ng-submitted is set if the form was submitted.
以及输入字段校验指令,具体使用方法官方文档
剩下我们需要自己写的指令有:
1、确认密码和输入密码的相等性判断
2、接受所有条款,否则无法继续注册
3、异步请求服务器,判断账号是否已存在
4、异步请求服务器,判断密码格式和强度要求
1、首先相等判断:
app.directive('equalCheck',function(){
return{
require : 'ngModel',
scope : {
orgText : '=equalCheck'
},
link : function(scope, elm, attrs, ctrl){
ctrl.$validators.not_equal = function(viewValue) {
return viewValue === scope.orgText;
};
scope.$watch('orgText', function() {
ctrl.$validate();
});
}
}
});
html上这样绑定
<input ... ng-model="register_user.confirm_password" equal-check="register_user.password">
使用not_equal判断错误信息是否显示:
<span class="error" ng-if="...registerForm.cpassword.$error.not_equal">The confirm password entered is invalid.</span>
2、必须接受条款判断:
app.directive('acceptCheck', function() {
return {
require : 'ngModel',
link : function(scope, elm, attrs, ctrl) {
ctrl.$parsers.push(function(viewValue) {
if (viewValue === true) {
ctrl.$setValidity('not_checked', true);
} else {
ctrl.$setValidity('not_checked', false);
}
return viewValue;
});
}
};
});
html上这样绑定:
<input ... ng-model="register_user.accept" accept-check>
使用not_checked判断错误信息是否显示:
<span class="error" ng-if="registerForm.accept.$error.not_checked">You must agree with all the terms.</span>
为了使接受条款默认选中,我们可以在控制器中设置:
$scope.login_user = {};
$scope.register_user = {};
$scope.register_user.accept = true;
3、4、异步请求服务器,提交密码账号,接受返回结果,显示错误信息指令代码:
app.directive('emailCheck',function ($http, $log, $rootScope) {
var timer;
return{
require : 'ngModel',
link : function(scope, elm, attrs, ctrl) {
elm.bind('keyup', function() {
if(!(scope.registerForm.remail.$error.required||
scope.registerForm.remail.$error.minlength||
scope.registerForm.remail.$error.maxlength||
scope.registerForm.remail.$error.email)) {
timer = setTimeout(function () {
$http(
{
method: 'POST',
url: 'http://127.0.0.1:5000/auth/email_check',
// params: {"email": scope.register_user.email}
data: {"email": scope.register_user.email}
}
).then(
function (response) {
if (response.status === 200 && response.data["status"] === "yes") {
ctrl.$setValidity('email_error', true);
$log.info(response);
}
else {
ctrl.$setValidity('email_error', false);
$log.info(response);
}
},
function (reason) {
ctrl.$setValidity('email_error', false);
console.log(reason);
}
);
}, 2000)
}
}).bind('keydown', function() {
if(scope.registerForm.remail.$error.email_error){
ctrl.$setValidity('email_error',true);
}
clearTimeout(timer);
});
}
}
});
设置定时器,两秒没有键盘操作,向服务器提交一次数据,接受返回结果,html上绑定指令email-check,服务端python-flask测试代码:
@auth.route('/email_check', methods=['POST'])
def email_check():
print request.json['email']
s = [{"status": "yes"}]
if request.json['email']:
user = User.query.filter_by(email=request.json['email']).first()
if user:
s = [{"status": "no"}]
response = make_response(jsonify(s[0]))
response.headers['Access-Control-Allow-Origin'] = '*'
response.headers['Access-Control-Allow-Methods'] = 'POST'
response.headers['Access-Control-Allow-Headers'] = 'x-requested-with,content-type'
return response
实际测试结果:
异步验证密码界面.png 调试框.png 异步验证账户服务.png
密码验证同理
表单提交只需要在添加action="/auth/register2""
服务端代码:
@auth.route('/register2', methods=['POST'])
def register2():
print request.form.get('remail')
if request.form.get('remail'):
user = User.query.filter_by(email=request.form.get('remail')).first()
if user:
return "email has been register!"
else:
user = User(email=request.form.get('remail'),
username=request.form.get('remail'),
password=request.form.get('rpassword'))
db.session.add(user)
db.session.commit()
token = user.generate_comfirmation_token()
send_email(user.email, 'Confirm your account', 'email/confirm', user=user, token=token,
next=request.args.get('next'))
# send_email(current_app.config['DEFPIS_ADMIN'],'New User','email/new_user',user=user,next=request.args.get('next'))
flash('A confirmation email has been sent to you by email!')
return redirect(url_for('auth.login', email=request.form.get('remail'), password=request.form.get('rpassword'),remember=True))
return "data error"
为了不使错误信息全部一起弹出来,设定只有在上一个输入框填入合法,才会显示下一个输入框的错误信息,链式判断:
<div ng-cloak ng-if="registerForm.remail.$dirty&®isterForm.remail.$invalid">...
<div ng-cloak ng-if="registerForm.remail.$valid&®isterForm.rpassword.$dirty&®isterForm.rpassword.$invalid">...
<div ng-cloak ng-if="registerForm.rpassword.$valid&®isterForm.cpassword.$dirty&®isterForm.cpassword.$invalid">...
...
这样错误信息只会一个个显示,用户可以按照错误信息,一步步完成表单,避免一次性出现多个错误提示。
小问题:表单自动补全会导致异步请求不生效。必须键盘触发向后台提交数据,应该使用监控数据变化的方式触发该事件。
网友评论