angular案例--电话簿

项目二

0 效果展示

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

一 项目初始化
1.搭建项目
ng new '项目名'(等到开始出现进度条的时候打断)
cd '项目目录'(进入你刚创建的项目中)
cnpm install(安装需要用的包等)
npm start(启动项目)
2. 初始化组件
//组件的划分与页面相关
cd '项目目录'
ng g component '组件名称'
//可以在app.component.html文件中引入每个组件对外提供的标签,标签名名称可以在组件对应的ts文件中查看
3.模板渲染
  1. 将已经准备好的静态页面的相关关内容添加到对应组件的html文件中,可以通过在app.component.html加入标签验证是否添加成功
  2. 为组件下载需要的模板,可以在原来的静态页面中查看当前页面原本引用了哪些页面,哪些是下载的,哪些是手动导入的(下载的时候@是为了规定版本)
    cnpm i -S bootstrap@3.3.7
  3. 在项目的styles.css文件中引入下载的文件(引入下载的文件到styles.css中而不是每个组件对应的.css中,因为这些样式是通用的)
    @import url(‘bootstrap/dist/css/bootstrap.css’)
  4. 将我们自己准备的样式添加到对应组件的对应的.css文件中;如果我们自己的写的css文件是全局样式,就可以将其粘贴到styles.css文件中
  5. 将单个组件准备好之后,我们需要在首页的html文件中添加创建好的组件,将首页布局展示出来-如下:
<app-navbar></app-navbar>
<div class="container-fluid">
    <div class="row">
      <app-sidebar></app-sidebar>
      <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
      <app-contact-list></app-contact-list>
    </div>
   </div>
</div>
二 核心功能实现:
1.处理路由
  1. 在首页中我们可以点击链接跳转到不同的页面,需要使用到angular的路由功能,这条指令的意思就是生成路由模块添加到我们的项目中
  ng generate module app-routing --flat --module=app
  //指令执行结束可以看到app目录下多了相应的ts文件,此外在app.module.ts文件中可以看到路由module也更新进去了
  1. 配置路由表:当请求到XXX路径的时候,就导航到XXX组件
  a) import { Routes, RouterModule } from '@angular/router';
	//在app-routing.module.ts文件中导入相应模块,视频中老师主动下载主动配置但是我的好像是自动下载自动配置
  b)  
  	import { SigninComponent } from './signin/signin.component'
	import { SignupComponent } from './signup/signup.component'

	const routes: Routes = [
  		{
    	path: 'signin',
    	component: SigninComponent
  		},
  		{
    	path: 'signup',
    	component: SignupComponent
  		}
	];
  	//在路由数据中直接配置就好,可以自动导入相应的组件
  1. 配置路由出口以及路由导航链接
    a) 路由出口,配置之后可以通过在浏览器中输入对应的path得到对应组件的页面
<router-outlet></router-outlet>	

b) 分析首页的路由情况,发现路由情况主要由标签决定,并且内容不能写死,应该由路由导航出口决定;两重路由,得出需要嵌套路由的结论–方式:为当前组件创建模板布局组件

	//路由出口
	<router-outlet></router-outlet>

c) ng g component layout //在控制台为当前项目创建模板布局组件,将下面的首页中应该显示的组件配置到layout组件的html文件中,注意注释掉的部分

<div class="container-fluid">
    <div class="row">
      <app-sidebar></app-sidebar>
      <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
      <--<app-contact-list></app-contact-list>-->
    </div>
   </div>
</div>

d) 为layout组件配置路由,方式同前面signin等,之后可以在浏览器中通过路径定位到Layout布局页面,可以看到中间部分没有什么数据,因为中间那部分没有路由到
e) 由于要创建的是嵌套路由,此时在上面的html中注释掉的部分要加上路由标签

<router-outlet></router-outlet>

f) 在配置路由的文件中配置子路由如下,使用path访问的时候就可以得到路由

path: 'contacts',
    component: LayoutComponent,
    children: [
      {
        path: '',
        component: ContactListComponent
    }
 //当我们访问contacts的时候,会先把LayoutComponent组件渲染出来,然后把children中的path为空的路由渲染到LayoutComponent组件的路由出口

g) 配置首页路由

//请求根路径的时候跳转到contacts组件
 {
    path: '',
    redirectTo: '/contacts',
    pathMatch: 'full'      //路径必须完全匹配的是时候才重定向
     //没有上面会报错
  },

h) 按照相同的方式将新增/编辑联系人添加为contacts的子路由;同理将tag的增删改添加为tags的子路由,之后可以在浏览器中进行验证

2.登录注册页面表单验证
  1. 在ts问文件中定义表单项,在视图文件中双向绑定(记得表单验证需要额外加入需要使用到的模块)
	import { FormsModule } from '@angular/forms'		//全局--imports处也要修改
	//注意angular会建议我们使用了双向绑定的控件添加name属性
	//双向绑定是否成功可以通过{{}}进行一遍输入一遍展示的验证
  1. angular处理表单验证–参建官网,根据需要的验证模式直接照着写
 <input type="email" id="inputEmail" class="form-control" name="email"
      placeholder="Email address" 
      required
      email="true"
      [(ngModel)]="signupForm.email"
      #email="ngModel">
//是根据required和email="true"这两个条件来进行验证的,当然要先使用#email="ngModel"绑定,属于angular固定语法
 <div *ngIf="email.invalid && (email.dirty || email.touched)"
            class="alert alert-danger">
        <div *ngIf="email.errors.required">
          Email is required.
        </div>
        <div *ngIf="email.errors.email">
          Email is invalid
        </div>
      </div>
//密码的验证方式非常相似,只是密码原本没有你做啥呢验证属性,我们为其加上minlength,以及Maxlength两个属性进行验证
<div *ngIf="password.invalid && (password.dirty || password.touched)"
            class="alert alert-danger">
        <div *ngIf="password.errors.required">
          password is required.
        </div>
        <div *ngIf="password.errors.minlength">
          password minlength 6
        </div>
        <div *ngIf="password.errors.maxlength">
          password maxlength 10
        </div>
      </div>
3.http请求的发送
  1. 在总的项目app.module.css中导入HTTP用的到module
	import { HttpClientModule} from "@angular/common/http"		//之后再Imports中注册
  1. 再我们要用到该服务的组件的ts文件中加入该module以及RouterModule
	import { HttpClient } from "@angular/common/http" //这里是HttpClient
	import { Router } from "@angular/router"
  1. 在方法中发送post请求
	//组件的构造方法中加入服务
	constructor(
    	private http: HttpClient,
    	private router: Router
    	) { }  
  	//钩子函数
  	ngOnInit() {
  	}
	//用于接收表单提交的数据
 	 signupForm={
    	email: '',
    	password: ''
  	}
	//用于接收返回的错误信息
  	email_err_msg=''

 	 public signup(){
    // 1. 表单验证--好像不是前端验证?
    // 2. 获取表单数据
    const formData=this.signupForm;  
    // 3. 发起 http 请求和服务端交互
    this.http.post('http://localhost:3000/users',formData)
    .toPromise()
    .then((data: any)=>{
      console.log(data)
      // 4. 根据响应结果做交互处理
      this.email_err_msg='';
        //将token和返回的用户信息存储到本地
      window.localStorage.setItem('auth_token',data.token);
      window.localStorage.setItem('user_info',JSON.stringify(data.user));
        //定向到list展示页面
      this.router.navigate(['/']);
    })
    .catch(err=>{
        //数据进行序偶无信息处理
      if(err.status===409){
        this.email_err_msg='邮箱已经注册'
      }
    })
  }
4. 登录成功之后实现页面的跳转
1) import { Router } from "@angular/router"   //在需要使用路由的组件ts文件中导入
2) //在组件的构造方法中声明路由就像声明Http那样 
	constructor(
    	private http: HttpClient,
    	private router: Router
    	) { }  
3) //配置返回数据处理之后的提跳转
	this.router.navigate(['/']);
5 基本的权限验证
  1. 存储token(本地存储可以使用浏览器抓包看到)
 window.localStorage.setItem('auth_token',data.token);
 //由于联系人列表展示页面在没有登陆的时候也能够访问,我们需要做出权限控制(后端的控制主要依靠shiro来实现),前端如何设置呢?前端进行去权限控制之后后端还需要进行权限控制吗?
  1. 在即将跳转的组件对应的ts文件的钩子函数ngOnInit()中进行判定,如果没有收到token,就跳转到登录页面
	//由于需要用到路由需要先导入,然后在构造函数中进行声明
  ngOnInit() {
      //本地有token才能够跳转到登录页面,没有的时候不跳转(注意token可以手动删除,同时注意过期时间,token与session和cookie很像)
    const token=window.localStorage.getItem('auth_token');
    if(!token){
      //如果没有token说明没有注册或者没有登录?,跳转到注册页面--应该是跳转到登录页面更有道理
      this.router.navigate(['/signup']);
     }
  }
  1. //但是由于众多页面都需要这样进行规定,重复写就很没意思,所以我们使用路由guards进行统一处理
	a) 在app下创建auth-guard.service.ts文件
    import { Injectable } from '@angular/core';
	import { CanActivate,  Router } from '@angular/router';
    
    @Injectable()
    export class AuthGuard implements CanActivate {
      constructor(private router: Router) {}
      //由于所有的需要token的接口都会判断token是否正确传输,这样进行配置以后,如果没有得到token
      //那么页面就会跳转到注册页面
      canActivate() {
        const token=window.localStorage.getItem('auth_token')
        if(!token){
            this.router.navigate(['/signup']);
        }
        return true;
      }
    }
  1. 使我们的配置生效
    a) 在全局路由配置文件app-routing.module.ts文件中导我们刚刚配置的文件
	//注意命名一定是大小写与驼峰的匹配
	import { AuthGuard } from './auth-guard.service'

b) 在所有需要权限验证的路由界面申明权限

	canActivate: [AuthGuard]  //如下
	//就是在路由之前先进入路由gaurds,验证通过返回true的时候继续导航,失败返回false的时候,按照路由guards的统一配置进行页页面跳转

c) 需要在app-routing.module.ts添加提供者

		@NgModule({
  			imports: [RouterModule.forRoot(routes)],
  			exports: [RouterModule],
  			providers: [AuthGuard]
			})

d) 同样的,可以在tags上面加上这些功能
//问题,进过验证同一个用户注册后可以跳转详情页面,登录之后也可以跳转详情页面,同一个用户登录和注册的时候生成的token值不一样,token是如何起作用的?

6.登录或者注册成功时候的用户信息显示
  1. 邮箱显示
	 a) 在登录/注册界面将用户信息写到本地
		window.localStorage.setItem('auth_token', data.token)
		//注意要将JSON格式的数据转换成字符串
         window.localStorage.setItem('user_info', JSON.stringify(data.user))
      b)在显示信息的组件的ts文件中得到数据
      	//取出数据后要将字符串解析为JSON格式,否则不能够使用user.来进行解析
      	user=JSON.parse(window.localStorage.getItem('user_info')|| '{}');
	  c) html中展示
      	<li><a href="#">{{ user.email }}</a></li>
      d) 导航链接:从登录页面到注册页面/从注册页面到达登陆页面
		<li><a routerLink="/signin">退出</a></li>
      e) 用户退出,就是点击退出触发一个方法,方法中销毁token,然后路由到登录界面
7.请求头header的处理
1) //导入需要的模块
   import { HttpClient,HttpHeaders } from '@angular/common/http'
2) //添加上头部信息 
    this.http.get("http://localhost:3000/contacts",{
       headers: new HttpHeaders().set('X-Access-Token',token)
     })
     .toPromise()
3) //发现出登录注册之外的所有的请求都需要带上头信息,重复比较麻烦--使用HTTP的请求响应拦截HTTPIntecepter
    a) 在app目录下新建global.interceptor.ts文件
		import { Injectable } from '@angular/core';
		import {
		  HttpEvent, HttpInterceptor, HttpHandler, HttpRequest
		 } from '@angular/common/http';
		
		import { Observable } from 'rxjs';
		
		@Injectable()
		export class GlobalInterceptor implements HttpInterceptor {
		  intercept(req: HttpRequest<any>, next: HttpHandler):
		    Observable<HttpEvent<any>> {
		    return next.handle(req);
		  }
		}
     b) 在全局路由文件中配置导入相应的模块
     	//import { HTTP_INTERCEPTORS } from '@angular/common/http';
     	...
     	//import { GlobalInterceptor } from './global.interceptor';
     c) //添加providers
     	providers: [{
 	   		provide: HTTP_INTERCEPTORS,
 	  		useClass: GlobalInterceptor,
 	   		multi: true
 	 		} 
     //完成以上操作后,就实现了统一处理,接着在global.interceptor.ts中写统一拦截之后的处理逻辑就行
    d) //在这里我们要做的是给每一个请求添加请求头
        intercept(req: HttpRequest<any>, next: HttpHandler):
		    Observable<HttpEvent<any>> {
		        const token =window.localStorage.getItem('auth_token')
		        const authReq=req.clone({headers: req.headers.set('X-Access-Token',token)})
		    return next.handle(authReq);
		  }
        //但是我发现还是只有/contracts的经过被拦截了(应该哪里写错了,又回到了最开始的单一设置header的方式)
     e) //
8.处理业务
1) 新增联系人
	a) //html进行简单修改,添加提交事件
    <form (submit)="addContact()" form="addForm">
    b) //导入http请求module,以及Router的module
    c) //拼凑好参数之后发送到后台即可--注意路径的的写法
    d) //注意<a>本身的部分功能要关闭
    	addContact(){
    	const token=window.localStorage.getItem('auth_token')
    	  this.http.post('http://localhost:3000/contacts',this.formData,{
    	    headers: new HttpHeaders().set('X-Access-Token',token)
    	  })
    	  .toPromise()
    	  .then(data=>
    	    //console.log(data)
    	    //window.alert('添加成功!')
    	    this.router.navigate(['/contacts'])
    	    )
    	    .catch(err=>{
    	      window.alert('联系人添加失败')
    	    })
    	 } 
2) 删除联系人
	a) //链接中添加方法--注意提示
    	<a (click)="deleteById(item.id,$event)" href='#'>删除</a>
    b) //写删除逻辑'
    	deleteById(id,e){
       //阻止默认的链接事件
    	e.preventDefault();
    c) //注意参数+ 弹框
    	if(!window.confirm('您确定要删除吗?')){
    	  return;
    	}
    	const token=window.localStorage.getItem('auth_token');
    	 this.http.delete(`http://localhost:3000/contacts/${id}`,{
    	   headers: new HttpHeaders().set('X-Access-Token',token)
    	 })
    	 .toPromise()
    	 .then((data: any)=>{
    	   console.log(data)
    	   window.alert('删除成功!')
    	   this.getContacts()
    	 })
    	 .catch(err=>{
    	   console.log(err)
    	   window.alert('删除失败')
    	 })
  	}
3) 编辑联系人
	a) 传递id
   	//更改路由文件,在原来的path路径后面添加
     {
        path: 'edit/:id',
        component: TagEditComponent
      }
     b)  绑定动态路径参数
	 	<a routerLink="['/contacts/edit',item.id]">编辑</a>
	c) 在目标组建中获得动态路由的参数
    	1) import { Router,ActivatedRoute } from '@angular/router'
		2)//得到id 
        	 ngOnInit() {
  		 		 const contactId=this.route.snapshot.params.id
  				}
    	3) //进行数据回显
        	getContactById(id){
    		const token=window.localStorage.getItem('auth_token')
    		this.http.get(`http://localhost:3000/contacts/${id}`,{
    		  headers: new HttpHeaders().set('X-Access-Token',token)
    		})
    		.toPromise()
    		.then((data:any)=>{
    		  this.formData=data
    		})
    		.catch(err=>{
    		  console.log(err)
    		})
           }
		4) //数据的编辑
        editContact(){
    		const id=this.formData.id;
    		const token=window.localStorage.getItem('auth_token')
    		this.http.patch(`http://localhost:3000/contacts/${id}`,this.formData,{
    		  headers: new HttpHeaders().set('X-Access-Token',token)
    		})
    		.toPromise()
    		.then((data: any)=>{
    		    this.router.navigate(['/contacts'])
    		})
    		.catch(err=>{
    		    window.alert('编辑请求失败')
    		})
    	}


  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值