angular8入门

声明:这篇文章绝大部分是参考https://blog.csdn.net/qq_39053584/article/details/93997318,感谢作者。

环境,工具

先确保安装了nodejs和npm

在开始之前,请确保你的开发环境中包括 Node.js和 npm 包管理器

Nodejs

Angular 需要 Node.js 版本 10.9.0 或更高版本。要检查你的版本,请在终端/控制台窗口中运行 node -v 。

windows更新版本不能通过网上说的安装n模块更新,只能重新下载新版的nodejs重新安装覆盖.

我的版本是v12.16.1

要获取 Node.js,请转到nodejs.org

npm 包管理器

Angular、Angular CLI 和 Angular 应用都依赖于 npm 包中提供的特性和功能。要想下载并安装 npm 包,你必须拥有一个 npm 包管理器。

本搭建指南使用 npm 客户端命令行界面,Node.js 已经默认安装了它。

要检查你是否安装了 npm 客户端,请在终端/控制台窗口中运行 npm -v 。

更新npm的命令是 npm install

我的版本是6.13.4

安装angular cli

你可以使用 Angular CLI 来创建项目、生成应用和库代码,以及执行各种持续开发任务,比如测试、打包和部署。

  • 查看angular版本的命令:ng --version(我的版本是9)
  • 最新版用的是:npm install -g @angular/cli

编译工具

我是后端的,前端的不精通,公司其他人前端用的都是vscode,我用的是idea,大概开发了两三个月,没觉得有什么不方便的地方,所以用哪个看个人喜好吧

项目结构

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

  • e2e:端对端的测试文件
  • node_modules:下载到本地的依赖包,代码上传git不需要上传这个,而且很大
  • src:工作文件,定义组件模块服务
  • app:app根模块,app下定义每个模块,下面几个文件每个模块都有(具体下一个部分会介绍)
  1. app.component.html:html页面
  2. app.component.scss:样式文件,相当于css
  3. app.component.spec.ts:测试文件
  4. app.component.ts:逻辑代码
  5. app.module.ts:引入模块、服务等
  6. app-routing.module.ts:路由
  • assets:放图片,以及i18n(国际化)
  • environment:环境的配置(后台服务的ip)
  • theme: 公共的css
  • package.json:配置文件,包括依赖

根模块app.module.ts

在这里插入图片描述

根组件app.component.ts

在这里插入图片描述

创建新的組件news

生成組件:

ng g component commons/news

例如: ng g component commons/news(文件夾/組件名)
這樣會自動在app下面創建一個commons文件夾並且在commons文件夾下面創建一個news組件,並且自動在app.moudle.ts裡面添加news組件的引入.

在这里插入图片描述
在跟组件里面引用news组件
在这里插入图片描述

启动项目

ng serve 默认启动在端口4200

属性操作,元素操作,指令

model的定义

export class AddressModel{
       city?: string;// 市
       cityName?: string;// 市区名称
       }

变量及其显示

在这里插入图片描述
打开页面:
在这里插入图片描述

定义对象、数组

 public userInfoModelList: UserInfoProfileModel;

 constructor(
    private nav: NavController,
    private myService: MyService,
    private alert: AlertControllerService,
    private actionSheetCtrl: ActionSheetController,
    private uploadImgService: UploadImgService,
    private commonUtil: CommonUtil
  ) {

    this.userInfoModelList = new UserInfoProfileModel();
    this.backImgList = new Array<ImageModel>();
  }

定义数组及用*ngFor循环

在这里插入图片描述
打开页面:
在这里插入图片描述

图片

在这里插入图片描述

ngSwitch

在这里插入图片描述

ngif需要补充

[ngClass] (动态css)

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

[ngStyle] (动态style)

指定css

在这里插入图片描述

动态切换显示、不显示

就是一个块儿,false不显示,true显示

<div [ngStyle]="{'display':isShowMenu ? 'block' : 'none' }">

在这里插入图片描述

管道

比如说很多时候我们需要把数字显示成金额、大小写转换、日期小数转换等等。 Angular管道对于象这样小型的转换来说是个很方便的选择。
管道是一个简单的函数,它接受一个输入值,并返回转换结果。

在这里插入图片描述

自定义管道

  1. 公共的工具类
import { API_URL } from 'src/app/shared/api/api.url';
import { ToastController } from '@ionic/angular';
import { Injectable } from '@angular/core';

@Injectable()
export class CommonUtil {
/**
     * cnd图片路径
     */
    public static cndImgUrl = API_URL.COMMON.cndUrl;

/**
     * 根据图片路径返回远程图片路径
     */
    public static getImgUrl(imgUrl): string {
        if (imgUrl != null && imgUrl !== undefined) {
            if (imgUrl.indexOf('http') !== -1 || imgUrl.indexOf('assets/') !== -1) {
                return imgUrl;
            } else {
                return this.cndImgUrl + imgUrl;
            }
        } else {
            return 'assets/images/no-url.png';
        }
    }
  1. 自定义管道
import { Pipe, PipeTransform } from '@angular/core';
import { CommonUtil } from '../../util/common.util';

@Pipe({
  name: 'imgUrl'
})
export class ImgUrlPipe implements PipeTransform {

  transform(value: any): any {
    return CommonUtil.getImgUrl( value );
  }
}
  1. 使用
<img src="{{item.headPortrait|imgUrl}}">

事件

普通点击事件

在这里插入图片描述
这里只是演示事件,数据的双向绑定会有更好的方法:后面提到.

表单事件,事件对象

在这里插入图片描述

数据双向绑定

  1. 首先在app.module.ts里面引入并声明;
  2. 再在input里使用[(ngModel)]="inputVal"双向绑定数据
  3. home.component.ts里面的方法为了测试通过model来改变视图
    在这里插入图片描述
    打开页面,input框和后面显示的值是1,点击按钮几下,两个地方的值一起变化
    在这里插入图片描述

父子或非父子之间的通信,传值

重新建一个项目,然后cnpm install之后再新建4个组件:news,home,header,footer.
这里,news和header是父子组件关系,home和footer是父子组件关系.

组件给子组件传值@input

在这里插入图片描述
我们先看一下上面流程,再做.
先把app.component.html里面自动生成的代码去掉并引入news组件,然后在news组件的html里面引入header组件.
在这里插入图片描述
父组件给子组件传值,也就是子组件需要使用父组件的属性和方法,按照三步走:

父组件给子组件传数据
子组件引入Input模块
子组件@Input接受传过来的数据

子组件给父组件传值

父组件通过@ViewChild主动获取子组件的数据和方法

home和footer是父子组件关系.我们把app.component.html里面的引入替换成,并且在home组件中引入

在这里插入图片描述
子组件通过@OutPut触发父组件
没了解,用到的时候再百度吧.

数据交互

这里我只记录angular内置的HttpClientModule的数据交互方式

GET

在这里插入图片描述

post

先准备好后台服务的接口:
很显然,我写了两个post的方法,一个是用RequestBody接受参数,另一个是用RequestParam接受参数的。

后台接口

package com.example.angulartest;
import com.sun.org.apache.xpath.internal.operations.String;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;
@RestController
@CrossOrigin(origins = "http://localhost:4200")
public class TestController {
    //用RequestBody接受数据
    @RequestMapping(value = "/testPostBody",method = RequestMethod.POST)
    public Map testPostBody(@RequestBody  Map<Object,Object> jsonString){
        System.out.println("jsonString : "+jsonString.get("uname")+"---------"+jsonString.get("pword") );
        return jsonString;
    }
    //@RequestParam接受数据
    @RequestMapping(value = "/testPostPara",method = RequestMethod.POST)
    public Map<String, String> testPostPara(@RequestParam Object uname,
                                            @RequestParam Object pword){
        System.out.println("jsonString : "+uname+"---------"+pword );
        Map map=new HashMap();
        map.put("uname",uname);
        map.put("pword",pword);
        return map;
    }
}

angular中引入相关模块
ap.module.ts中引入

import { HttpClientModule } from '@angular/common/http';

  imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule
  ],

然后在使用的地方引入

import { HttpClient, HttpHeaders } from '@angular/common/http';

并且再构造函数中实例化HtpClient,
测试代码:

import { Component, OnInit } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';

@Component({
  selector: 'app-news',
  templateUrl: './news.component.html',
  styleUrls: ['./news.component.css']
})
export class NewsComponent implements OnInit {
  constructor(public http: HttpClient) { }

  testPostBody() {//post请求,参数在body中
    const header = {headers: new HttpHeaders({'Content-Type': 'application/json'})};
    let url = "http://localhost:8080/testPostBody";
    this.http.post(url,{ 'uname': 'kone', 'pword': '2222222222'},header).subscribe(re=>{
        console.log(re);
    });
  }
  testPostPara(){//post请求,参数在url后面
    const header = {headers: new HttpHeaders({'Content-Type': 'application/json'})};
    let url = "http://localhost:8080/testPostPara?uname=mxl&pword=111111";
    //body中就不放入参数,直接拼接到url后面
    this.http.post(url,{},header).subscribe(re=>{
        console.log(re);
    });
  }
  ngOnInit() {
  }
}

效果
在这里插入图片描述

项目中对其进行了封装

配置后端服务器地址(environment.ts)

export const environment = {
  production: false,
  apiRoot: 'http://127.0.0.1:9999',
  cdnRoot: 'http://assets.aihangyun.com/images/mall',
  chatRoot: 'http://192.168.100.223:9666'
};

配置api及url(所有后端的url都在这)

import { environment } from './../../../environments/environment';

const API_HOST = environment.apiRoot; //
const CDN_HOST = environment.cdnRoot;
const CHAT_HOST = environment.chatRoot;

const API_ROOT = `${API_HOST}`; // root
const CDN_MALL_IMAGE_ROOT = `${CDN_HOST}/images`;

export const API_URL = {
    CHAT: {
        chatHost: `${CHAT_HOST}`
    },

    COMMON: {
        // oss图片路径
        cndUrl: `http://img.xxx.com/`,
        // 获取图片签名
        //getOssSign: `${API_HOST}/message/oss/get`,
        getOssSign: `http://127.0.0.1/message/oss/get`,
    },
    LOGIN: {
        // 帐号密码登录
        cypherLogin: `${API_HOST}/uc/user/passwordLogin`
    },
    SEARCH: {
        getRelatedWords: `${API_HOST}/ahxx/relatedWords`,
        getSearchGoods: `${API_HOST}/ahxx/store/goods/search`,
        getSearchStore: `${API_HOST}/ahxx/user/store/search`
    },
    // 店铺
    STORE: {
        storeGoods: `${API_HOST}/ahxx/store/goods`,
        initEditGoods: `${API_HOST}/ahxx/store/goods/update/init`
        }
}

封装http请求api.service.ts

import { Observable } from 'rxjs';
import {Injectable} from '@angular/core';
import {HttpClient, HttpErrorResponse} from '@angular/common/http';
import { throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';

export class ApiService {

    constructor(
        private http: HttpClient,
    ) {}

/**
     * post 请求
     *
     * @param {string} apiUrl
     * @param {string} body
     * @returns {Promise<any>}
     */
    public post( apiUrl: string , body?: string,httpOptions?:any ): Observable< any > {

        // tslint:disable-next-line: variable-name
        const _body = body || '';
       
        // console.log( '==========================post=====================================' );
        return this.http
            .post( apiUrl, _body, httpOptions ).pipe(
                catchError(this.handleError)
                
            );
    }

/**
     * get 请求
     *
     * @param {string} apiUrl
     * @returns {Promise<any>}
     */
    public get( apiUrl: string ): Observable< any > {

         console.log( '==========================get=====================================' );

         return this.http
            .get( apiUrl ).pipe(

                catchError(this.handleError)
            );
    }

使用封装好的(InfoService)

import { InfoModel } from './../../model/info/info.model';
import { Injectable } from '@angular/core';
import { ApiService } from 'src/app/shared/api/api.service';
import { API_URL } from 'src/app/shared/api/api.url';
import { BuyServicePackModel } from 'src/app/model/quanyi-center/buyServicePack.model';

@Injectable({
  providedIn: 'root'
})
export class InfoService {

  constructor(public apiService: ApiService) { }

  // 使用get请求
  public getSerachGoods(searchName:string){
    const url = API_URL.INFO.getSerachGoods + '?searchName='+ searchName;
    return this.apiService.get(url).toPromise().then(response => response);
  }

// 使用post请求
public publishInfo(infoModel:InfoModel){
    const url=API_URL.INFO.publishInfo;
    return this.apiService.post(url,JSON.stringify(infoModel)).toPromise().then(response=>response);
 }

路由

我现在对路由作用的理解是:

1:根据不同的url地址,动态的让根组件(父组件)挂载其他组件来实现一个单页面应用
2:在分页的查询的情况下能通过url栏分享出当前的页数或者detail信息

在用 ng new 新项目时,第一步问你要不要加入路由:

Would you like to add Angular routing? (y/N)

此时,我们输入y创建。然后在生成的文件中看到下图,并且在app.module.ts里面也自动引入了路由模块。

在这里插入图片描述
在app-routing.module.ts里面配置项目的路由即可
在这里插入图片描述
还有app.component.html底部多了一行标签。这个接下来就会一并讲到。

2:准备几个组件

ng g component components/product
ng g component components/news
ng g component components/home
  1. 配置路由

打开app-routing.module.ts,此时你应该在这里面引入了这几个组件,然后开始配置

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { HomeComponent } from './components/home/home.component';
import { NewsComponent} from './components/news/news.component';
import { ProductComponent } from './components/product/product.component';

// 这里配置路由
const routes: Routes = [
  {path: 'home', component: HomeComponent},
  {path: 'news', component: NewsComponent},
  {path: 'product', component: ProductComponent}
];
@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

4:测试配置,解释

打开浏览器输入http://localhost:4200/home
后面的/home也是我在上面配置的一个路径,看是否能够显示home组件的内容

在这里插入图片描述
从上图中看到这里红色部分就是home组件的html内容。路由配置成功。但是他是在什么地方显示出来的呢。我们打开app.component.html看到
至于他是什么时候生成的,在你新建项目Would you like to add Angular routing? (y/N),你输入y即可。剩下的路由引入就是Angualr的事情了。

可以简单把它理解为: 页面的占位符,动态加载,会被替换掉的。
当点击 home、about、 dashboard 时,在导航栏的下方,
会被对应的 XX.component.html 替换掉。 这就是单页面路由的原理。

routerLink配置动态路由

app.component.html

<h3>我是根组件app.component</h3><br>
<a [routerLink]="[ '/home' ]"  routerLinkActive="active">首页</a><br><br>
<a routerLink='/news' routerLinkActive="active">新闻</a><br><br>
<a [routerLink]="[ '/product' ]" routerLinkActive="active">商品页面</a><br>

<router-outlet></router-outlet>

这里面routerLink里面用到的path就是在app-routing.module.ts里配置的。当你点击首页时,首页组件会被替换到 的位置。
routerLinkActive作用:给显示的组件一个自定义的样式。所以下面当我点击新闻时候会出现下面的效果:

在这里插入图片描述
默认组件
一开始打开页面并没有点击那哪个组件的时候,下面没有显示任何组件的信息,是很不友好的,所以要配置一个默认显示的组件。
在app-routing.module.ts加配置:

// 匹配不到的时候显示的组件
  {path: '**',  redirectTo : 'home'}

这样的话,刚进入页面就可以加载首页的组件。

项目中配置路由的方式

path就是访问路径

const routes: Routes = [
  {
    path: '',
    loadChildren: () => import('./tabs/tabs.module').then(m => m.TabsPageModule)
  },
  {
    path: 'sign-in',
    loadChildren: () => import('./login/sign-in/sign-in.module').then(m => m.SignInPageModule)
  }
];

@NgModule({
  imports: [
    RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules })
  ],
  exports: [RouterModule]
})
export class AppRoutingModule { }

路由传值以及JS跳转路由

新闻组件里面有几条新闻,希望通过点击新闻到新闻详情页查看该新闻详情。

准备详情组件及新闻数据和页面

ng g component components/detail

然后在路由配置里面配置{path: ‘detail’, component: DetailComponent}
然后在新闻组件里面定义几条数据并展示

在这里插入图片描述

1:路由的get传值:

在这里插入图片描述
detail接受传过来的值并展示
在这里插入图片描述
get路由传值效果
在这里插入图片描述

2.动态路由传值

修改路由配置
在这里插入图片描述
传值,接收
在这里插入图片描述
动态路由传值测试
在这里插入图片描述

get传值JS跳转

先多引入一个NavigationExtras,其实不引入跑起来也可以,应该是标准的问题吧。。

代码
在这里插入图片描述
至于怎么接受数据,在之前的例子中已经提到了

点击按钮后的效果
跳转成功,并且url后面的参数就是我传过去的。
在这里插入图片描述

页面跳转,传的值为对象

// 引入Router并实例化
// 传递对象
public gotoPreview() {
   this.router.navigate(['publish-preview'],{
    queryParams: {
      infoModel: JSON.stringify(this.infoModel),
  },
   });
}

// 引入ActivatedRoute并实例化
// 接收对象
this.activeRoute.queryParamMap.subscribe((param) => {
      this.infoModel = JSON.parse(param.get('infoModel'));
      console.log(this.infoModel);
    });

O:路由守卫

路由守卫有几种,可以参考 这个链接
我这里这讲其中的两种:CanActivate,CanActivateChild
他们俩的目的是一样的:就是在跳转到目标路由之前先判断是否符合设置的逻辑(canActivate()的返回值,返回true就可以跳转到目标路由,返回false则做其他自定义处理),只不过CanActivateChild针对是所有子路由.

1:创建公共服务类ServeService
这个服务实现了CanActivate ,CanActivateChild,并重写canActivate和canActivateChild方法.我这里的目的是如果用户信息不存在于cookie中,则判断用户信息是否在缓存中,两者都不在,则跳转到登录页,并且返回false;否则,返回true,也就是跳转到目标路由.

import { Injectable } from '@angular/core';
import { CookieService } from 'ngx-cookie-service';
import { Router,CanActivate,CanActivateChild ,RouterStateSnapshot,ActivatedRouteSnapshot } from '@angular/router';

@Injectable()
export class ServeService  implements CanActivate ,CanActivateChild{
  //跳轉到目標路由前檢查有沒有登錄
  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot,
  ): boolean {
    let cookieVal=this.cookieService.get("userName");
    let storages=this.get("userName");
    console.log("访问一次canActivate---cookieVal:"+cookieVal+"----storages:"+storages);
    if(cookieVal != null && cookieVal != ""){
      return true;
    }else if(storages != null && storages != ""){
      return true;
    }else{
      this.router.navigate(['login'],{ queryParams: { 'errorInfo': 'noAuth'}} );
      return false;
    }
  }

  canActivateChild(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot,
  ): boolean {
    return this.canActivate(route, state);
  }

  //保存到緩存
  set(key:string,value:any){
    localStorage.setItem(key,JSON.stringify(value));
  }
  //取緩存值
  get(key:string){
    return JSON.parse(localStorage.getItem(key));
  }
  //刪除緩存
  remove(key:string){
    localStorage.removeItem(key);
  }
  constructor(private router: Router,private cookieService: CookieService) {}
}

2:路由配置

这里实际上配置哪些路由在跳转之前需要经过这个路由守卫,雷士于java的filter

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值