使用AngularJS实现登录与注册
html和css布局
我们采用html+css框架bootstrap4,里面有很多已定义好的组件,可以拿过来直接用,非常简单,对于实现我们想要的功能,效率非常高。点击查看boststrap4官方文档。
我想把登录界面和注册界面集成在同一个页面上,于是想起了bs中的navs组件,利用该组件就可以切换不同的表单。
通过引入jQuery,bs4可以自动帮你完成界面的切换,点击链接实际指向以下内容:
通过在tab-pane中放置form表单就可以达到登录界面和注册界面的切换,以下是登录界面的form代码:
登录界面长这个样子:
以下是注册页面代码:
注册界面长这个样子:
javacript代码和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上这样绑定
使用not_equal判断错误信息是否显示:
The confirm password entered is invalid.
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上这样绑定:
使用not_checked判断错误信息是否显示:
You must agree with all the terms.
为了使接受条款默认选中,我们可以在控制器中设置:
$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
实际测试结果:
密码验证同理
表单提交只需要在添加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"
为了不使错误信息全部一起弹出来,设定只有在上一个输入框填入合法,才会显示下一个输入框的错误信息,链式判断:
...
...
...
...
这样错误信息只会一个个显示,用户可以按照错误信息,一步步完成表单,避免一次性出现多个错误提示。
小问题:表单自动补全会导致异步请求不生效。必须键盘触发向后台提交数据,应该使用监控数据变化的方式触发该事件。