展开
拓展 关闭
订阅号推广码
GitHub
视频
公告栏 关闭

Angular入门

构建项目

// 安装angular-cli
npm install -g @angular/cli
// 创建项目
ng new angular01
// 是否安装路由
// 选择(css/less/sass)
// 进入项目
cd angular01
// 安装依赖
npm install
// 启动并打开浏览器
ng serve -o
  • 导入vscode后,安装angular提示的插件

目录结构

  • 第一层文件
node_modules		第三方依赖包存放目录
e2e  				端到端的测试目录  用来做自动测试的
src   				应用源代码目录  
.angular-cli.json   Angular命令行工具的配置文件。后期可能会去修改它,引一些其他的第三方的包  比如jquery等
karma.conf.js  		karma是单元测试的执行器,karma.conf.js是karma的配置文件
package.json   		这是一个标准的npm工具的配置文件,这个文件里面列出了该应用程序所使用的第三方依赖包。实际上我们在新建项目的时候,等了半天就是在下载第三方依赖包。下载完成后会放在node_modules这个目录中,后期我们可能会修改这个文件。
protractor.conf.js  也是一个做自动化测试的配置文件
README.md           说明文件
tslint.json       	是tslint的配置文件,用来定义TypeScript代码质量检查的规则,不用管它
  • src目录
app目录				包含应用的组件和模块,我们要写的代码都在这个目录
assets目录  		资源目录,存储静态资源的  比如图片
environments目录   	环境配置。Angular是支持多环境开发的,我们可以在不同的环境下(开发环境,测试环境,生产环境)共用一套代码,主要用来配置环境的
index.html  		整个应用的根html,程序启动就是访问这个页面
main.ts    			整个项目的入口点,Angular通过这个文件来启动项目
polyfills.ts   		主要是用来导入一些必要库,为了让Angular能正常运行在老版本下
styles.css   		主要是放一些全局的样式
tsconfig.app.json	TypeScript编译器的配置,添加第三方依赖的时候会修改这个文件
tsconfig.spec.json	不用管
test.ts    			也是自动化测试用的
typings.d.ts        不用管
  • app目录
src\app\app.component.html      // 根组件
src\app\app.component.less      // 根组件
src\app\app.component.ts        // 根组件
src\app\app.module.ts          // 根模块
  • 根模块app.module.ts
// angular核心模块
import { NgModule } from '@angular/core';
// 浏览器解析模块
import { BrowserModule } from '@angular/platform-browser';
// 路由模块
import { AppRoutingModule } from './app-routing.module';
// 导入app目录下的三个根组件:app.component.html,app.component.less,app.component.ts
import { AppComponent } from './app.component';
@NgModule({
  declarations: [
    AppComponent        // 将上面导入的根组件注册为自己的组件
  ],
  imports: [         // 配置依赖的其他模块
    BrowserModule,      // 类似于导入挂载
    AppRoutingModule
  ],
  providers: [],
  bootstrap: [AppComponent]      // 指定根组件来启动应用
})
export class AppModule { }      // 暴露当前接口,根模块可不用暴露
  • 根组件app.component.ts
// 导入angular的核心
import { Component } from '@angular/core';
@Component({
  selector: 'app-root',   // 导入该组件的名称
  templateUrl: './app.component.html',    // 导入根组件html
  styleUrls: ['./app.component.less']     // 导入根组件less
})
export class AppComponent {     // 定义根组件的属性
  title = 'angular01';
}

新建子组件

  • 初始化项目:删除app.component.html中的默认代码
<style>
</style>

<div>
  这是根组件
</div>

<router-outlet></router-outlet>
  • 新建子组件
// 提示
ng g
// 在app/components/路径下新建组件news
ng g component components/news

// 之后就会在components路径下生成news文件夹,整个文件夹都是news组件
// 在根模块app.module.ts中会自动引入news组件
import { NewsComponent } from './components/news/news.component';
@NgModule({
  declarations: [
    NewsComponent        // 将上面导入的根组件注册为自己的组件
  ]
})

// 在子组件news.component.ts中
@Component({
  selector: 'app-news',    // 当前组件名称
  templateUrl: './news.component.html',
  styleUrls: ['./news.component.less']
})

// 在根组件中使用news组件
<app-news></app-news>

模板合成

  • 数据绑定
// 在news.component.ts中绑定一个title
export class NewsComponent implements OnInit {

  // 修饰符类型:public、private、protected
  public title: string="我是一个新闻组件";

  // 生成一个对象
  public userinfo: Object ={
    name:"张山",
    age:20
  };

  // 在构造函数中传入属性的值
  constructor() {
    this.title="这是改变后的值";
  }
}

// 在news.component.html中使用
<span>{{title}}</span>
<span>{{userinfo}}</span>
  • 属性绑定
// news.component.ts中
export class NewsComponent implements OnInit {

  public baidu: string="www.baidu.com";
}

// news.component.html中
<h2>属性绑定</h2>
<p title="这是title">baidu_0</p>
<p [title]="baidu">baidu_1</p><br>
<a [href]="baidu">baidu_2</a><br>
  • html绑定
// news.component.ts中
export class NewsComponent implements OnInit {

  // html绑定
  public content: string="<h2>这是一段文本!!</h2>"
  public content2:string=`
    <span>title</span>
    <p>p2</p>
  `
}

// news.component.html中
<h2>绑定html</h2>
<p>{{content}}</p>
<p [innerHTML]="content"></p>
<p [innerHTML]="content2"></p>
  • 简单运算
<h2>简单运算</h2>
<p>333+555={{333+555}}</p>
  • 数据循环
export class NewsComponent implements OnInit {
  // 数组循环
  public arr=["ass", "dsdsa", "asdf"];
  public list: string[]=["asdfasd", "asdfasd", "asdfasdf", "adsfasd"];
  public items:Array<string>=["asd", "asdf", "1324"];
  public userList:object[]=[{username:"user", password: "123456"},{username: "user2", password: "1234567"}]
}

<h2>数据循环</h2>
<ul>
    <li *ngFor="let item of arr">
        {{item}}
    </li>
</ul>
<p>-------</p>
<ul>
    <li *ngFor="let item of list">
        {{item}}
    </li>
</ul>
<p>-------</p>
<ul>
    <li *ngFor="let item of items">
        {{item}}
    </li>
</ul>
<p>-------</p>
<ul></ul>
<li *ngFor="let item of userList">
    {{item.username}}
</li>

组件合成

  • 新建子组件home,以下操作在home组件中完成
  • 引入图片
// ts组件中指定图片地址
public picUrl:string="https://tse4-mm.cn.bing.net/th/id/OIP-C.uPx-FbZ4nf2U05kCoJ7_1QHaE8?w=299&h=197&c=7&r=0&o=5&dpr=1.24&pid=1.7";

// html组件中
<span><img src="assets/images/OIP-C.jpg"></span><br>
<p>引入图片方式二</p>
<img [src]="picUrl">
  • 循环数据时,显示索引
// ts组件中指定数组
public list: any[]=[
    {"title":'新闻一'},{"title":'新闻二'},{"title":'新闻三'}
  ]

// html组件中
<ul>
  <li *ngFor="let item of list; let key=index">
    {{key}}----{{item.title}}
  </li>
</ul>
  • ngif条件判断
public flag:boolean=false;

// html组件中
<div *ngIf="flag">
  <img src="assets/images/OIP-C.jpg">
</div>
<div *ngIf="!flag">
  <img [src]="picUrl">
</div>
  • ngSwitch条件判断
// ts组件中
public orderStatus:number=1;

// html组件中
<span [ngSwitch]="orderStatus">
  <p *ngSwitchCase="1">已经支付</p>
  <p *ngSwitchCase="2">未支付</p>
  <p *ngSwitchCase="3">已收货</p>
  <p *ngSwitchCase="4">为收货</p>
</span>
  • ngclass样式绑定
// ts组件中
public flag:boolean=false;

// less组件中
.red{
    color: red;
}
.blue{
    color: blue;
}
.yellow{
    color: yellow;
}

// html组件中
<!-- 类名class1为true表示使用该样式 -->
<div [ngClass]="{'class1':true, 'class2':false}"></div>
<!-- flag为home.component.ts中定义的boolean类型 -->
<div [ngClass]="{'class3':flag, 'class4':!flag}"></div>
<ul>
  <li *ngFor="let item of list; let key=index;" [ngClass]="{'red': key==1, 'blue': key==2}"> 
    {{key}}----{{item.title}}
  </li>
</ul>
  • 样式绑定ngStyle
// ts 组件中
public attr: string='red';
// less 组件中
.red{
    color: red;
}
// html 组件中
<p [ngStyle]="{'color':'red'}">这是一段文本</p>
<p [ngStyle]="{'color':attr}">这是一段文本2</p>
  • 管道:转换格式
// ts组件中
public today: any=new Date();
// html 组件中
<span>未转换 -> </span>{{today}}
<br>
<span>转换后 -> </span>{{today | date:'yyyy-mm-dd HH-mm-ss'}}
  • 点击事件
// ts组件中
  // 事件
  run(){
    alert("hello!!");
  }
  // 获取
  getData(){
    alert(this.picUrl);
  }

  public title: string="asdf";
  // 设置数据
  setData(){
    this.title="sadfasdf"
  }

  // 点击获取事件对象
  runEvent(e){
    var dom: any =  e.target;
    dom.style.color='red';
    dom.style.height='200px';
  }
<button (click)="run()">点击1</button>
<br>
<!-- 点击时获取数据 -->
<button (click)="getData()">点击1</button>
<br>
<!-- 点击时改变数据 -->
data:{{title}}
<button (click)="setData()">点击2</button>
<br>
<br>
<button (click)="runEvent($event)">点击获取事件对象</button>
  • 键盘事件
// ts组件中
  keyDown(e){
    if(e.keyCode==13){
      console.log("按了回车")
    }else{
      console.log(e.target.value)
    }
  }
  // 键盘松开事件
  keyUp(e){
    if(e.keyCode==13){
      console.log(e.target.value)
      console.log("按了回车")
    }
  }

// html组件中
键盘按下<input type="text" (keydown)="keyDown($event)"/>
键盘松开<input type="text" (keyup)="keyUp($event)">
  • 报错
Error: src/app/components/home/home.component.ts:50:11 - error TS7006: Parameter 'e' implicitly has an 'any' type.     

50   keyDown(e){
  • 解决方案

  • 双向数据绑定需在根组件中引入FormsModule

// app.module.ts中导入form组件
import { FormsModule } from '@angular/forms';
@NgModule({
  imports: [         // 配置依赖的其他模块
    FormsModule
  ],
})

// ts组件中
public keywords: any;

// html组件中
<input type="text" [(ngModel)]='keywords'/>
<br>{{keywords}}
  • 报错
Property 'keywords' does not exist on type 'HomeComponent'.

76 <br>{{keywords}}
  • 错误原因:ts中keywords没有指定类型

表单双向绑定

  • 新建子组件form
  • ts组件
export class FormComponent implements OnInit {
  // 表单数据绑定
  public people: any = {
    username: '', 
    sex: '1',
    cityList: ['shanghai', 'beijing', 'chongqing'],
    city: 'shanghai',
    hobby: [
      {title: 'lanqiu', checked: false},{title: 'cang', checked: false},{title: 'tiao', checked: false}
    ],
    mark: ''
  }
}
  • html组件
<p>文本框双向绑定</p>
<input type="text" [(ngModel)]="people.username">
<br>{{people.username}}
<p>单选框双向绑定</p>
<input type="radio" value="1" [(ngModel)]="people.sex">
<input type="radio" value="2" [(ngModel)]="people.sex"><br>
<br>{{people.sex}}
<p>下拉列表双向绑定</p>
<select [(ngModel)]="people.city">
    <option *ngFor="let item of people.cityList" [value]="item">{{item}}</option>
</select>
<br>{{people.city}}
<p>多选框双向数据绑定</p>
<span *ngFor="let item of people.hobby, let key=index">
    <input type="checkbox" id="check+'key'" [(ngModel)]="item.checked">{{item.title}}--{{item.checked}}
</span>
<br>
<p>文本域双向数据绑定</p>
<textarea [(ngModel)]="people.mark"></textarea>
<br>{{people.mark}}

搜索框历史功能

  • 新建子组件search
  • html组件
<div class="search">
    <!-- 文本框双向绑定 -->
    <input type="text" [(ngModel)]="keyword" placeholder="Search">
    <!-- 点击按钮添加到历史记录 -->
    <button type="button" (click)="doSearch()">search</button>
    <br>
    <div class="historyList">
        <!-- 遍历循环历史记录,点击删除事件 -->
        <li *ngFor="let item of historyList; let key=index;">
            {{item}}<span (click)="deleteHistory(key)" class="del">X</span>
        </li>
    </div>
</div>
  • ts组件
// 与搜索框双向绑定
  public keyword: string;
  // 历史记录的数组
  public historyList: any[] = [];
  // 判断历史记录中是否存在,添加到历史记录
  doSearch(){
    if(this.historyList.indexOf(this.keyword)==-1){
      this.historyList.push(this.keyword);
    }
    this.keyword='';
  }
  // 删除历史记录
  deleteHistory(key){
    //alert(key);
    this.historyList.splice(key, 1);
  }

事项清单

  • 新建子组件todolist
  • html组件
<div class="todolist">
    <!-- 文本框双向绑定,键盘事件添加历史记录 -->
    <input type="text" placeholder="Search" [(ngModel)]="keyword" (keyup)="doAdd($event)">
    <hr>
    <div class="historyList">
        <p>待办事项</p>
        <div class="history"></div>
            <!-- 遍历历史记录,hidden表示status=0时显示 -->
            <div class="his"  *ngFor="let item of todolist; let key=index;" [hidden]="item.status==1">
                <!-- 双向绑定状态status -->
                <input type="checkbox" [(ngModel)]="item.status" class="inp">
                {{item.title}}
                <span class="del" (click)="delete(key)">x</span>
            </div>
        </div>
    </div>
    <div class="todos">
        <p>已完成事项</p>
        <div class="todo2">
            <!-- 遍历历史记录数组,hidden表示status为1时显示;删除记录的方法 -->
            <div class="todo" *ngFor="let item of todolist; let key=index;" [hidden]="item.status==0">
                <!-- 双向绑定单条历史记录的状态 -->
                <input type="checkbox" [(ngModel)]="item.status" class="inp">
                {{item.title}}
                <span class="del" (click)="delete2(key)">x</span>
            </div>
        </div>
    </div>
</div>
  • ts组件
export class TodolistComponent implements OnInit {
  // 双向绑定搜索框
  public keyword: string;
  // 默认的历史记录
  public todolist: any[] = [{title: 'vuejs', status: 0}];
  // 添加历史记录的方法
  doAdd(e){
    if(e.keyCode==13){
      this.todolist.push({title: this.keyword, status: 0});
      this.keyword='';
    }
  }
  // 删除历史记录的方法
  delete(key){
    this.todolist.splice(key,1);
  }
  // 删除历史记录的方法
  delete2(key){
    this.todolist.splice(key,1);
  }
}

angular服务

  • angular是基于组件化的开发,一个组件不能调用另一个组件中方法,这时需要把公共的方法或数据放到angular服务中,这样所有的组件都可以调用服务中的方法和数据,类型于vuex
  • 使用步骤1(不推荐):
// 1. 创建服务
ng g service services/storage
// 2. 在根模块app.module.ts中引入并注册
import { StorageService } from './service/storage.service';
@NgModule({
  providers: [StorageService],
})
// 3. 在创建的服务中编写公共的方法
export class StorageService {
  get(){
    console.log("这是一个公共方法!");
  }
}
// 4. 测试:在子组件search中使用;如果要在某个子组件中使用同样需映入并创建服务实例
import { StorageService } from '../../service/storage.service';
var storage = new StorageService;
export class SearchComponent implements OnInit {
  constructor() { 
    // 测试服务中的公共方法
    console.log(storage)
  }
}
  • 使用步骤2(官方推荐):
/// 1. 创建服务
ng g service services/storage
// 2. 在根模块app.module.ts中引入并注册
import { StorageService } from './service/storage.service';
@NgModule({
  providers: [StorageService],
})
// 3. 在创建的服务中编写公共的方法
export class StorageService {
  get(){
    console.log("这是一个公共方法!");
  }
}
// 4. 测试:在子组件search中使用;如果要在某个子组件中使用同样需映入并创建服务实例
import { StorageService } from '../../service/storage.service';
export class SearchComponent implements OnInit {
  constructor( public storage: StorageService) {    // 相当于上面的new服务实例
    let s= this.storage.test();
    console.log(s);
  }
}

案例:服务中编写公共方法

  • angular服务中编写方法
export class StorageService {
  // 事项清单添加数据到本地缓存
  set(key: string, value: any){
    localStorage.setItem(key, JSON.stringify(value));
  }
  // 从本地缓存中获取数据
  get(key: string){
    return localStorage.getItem(key);
  }
  // 删除本地缓存中的数据
  remove(key: string){
    localStorage.removeItem(key);
  }
}
  • 在search.component.ts中使用
  • 在ts组件中ngOnInit方法会在每次刷新时触发
import { StorageService } from '../../service/storage.service';
// 1.引入
export class SearchComponent implements OnInit {
  ngOnInit(): void {
    console.log("刷新页面时会触发该周期函数")
    // 2.每次刷新时都会从本地缓存中获取数据
    var searchlist: any= this.storage.get('searchlist');
    if(searchlist){
      this.historyList=searchlist;
      console.log("获取成功")
    }
    // console.log(this.storage.get('searchlist'))
    // let searchlist: any =this.storage.get('searchlist');    
    // this.historyList.push(searchlist);
  }

  doSearch(){
    if(this.historyList.indexOf(this.keyword)==-1){
      this.historyList.push(this.keyword);
      // 3.添加历史记录到本地缓存
      this.storage.set('searchlist', this.historyList);
      console.log("添加成功")
    }
    this.keyword='';
  }
}

dom节点操作

  • 方式一:
// html组件中有一个box节点
<div id="box">这是一个盒子!</div>

// ts组件中通过原生的js方法操作
ngOnInit(): void {
  let box: any= document.getElementById('box');
  console.log("dom" + box.innerHTML);
  box.style.color="blue";
}
  • 方式二:
// html组件中的dom节点中有angular指令
<div id="box2" *ngIf="flag">这是一个盒子!</div>

// ts组件中,在ngAfterViewInit函数中操作,注意大小写,否则不起作用
ngAfterViewInit(): void {
    let box2: any= document.getElementById('box2');
    console.log("dom" + box2.innerHTML);
    box2.style.color="red";
  }
  • 方式三:使用viewchild
// 1. html组件中自定义节点名称box3
<div #box3 *ngIf="flag" >这是一个盒子!</div>

// 2. ts组件中操作,引入viewchild
import { ViewChild } from '@angular/core';
export class DomComponent implements OnInit {
  public flag: boolean = true;

  // 3. 使用viewchild,定义组件名称
  @ViewChild('box3') box3: any;

  ngAfterViewInit(): void {
    // 4. 使用
    console.log("box3" + this.box3.nativeElement);
    this.box3.nativeElement.style.color="red";
  }
}
  • 使用viewChild调用另一个组件中的方法
// 例如dom组件调用form组件中的方法
// 1. 在dom组件的html中引入form组件,并指定名称
<app-form #form></app-form>

// 2. 在dom组件的ts组件中操作,引入viewchild
import { ViewChild } from '@angular/core';
export class DomComponent implements OnInit {
  // 3. 使用viewchild,定义组件名称
  @ViewChild('form') form: any;

  ngAfterViewInit(): void {
    // 4. 调用form组件中的方法
    console.log("form -> " + this.form.run());
  }
}

实现一个侧边栏

  • 新建组件transition
// 编写html
<div id="content">
    内容区<br>
    <button (click)="showAside()">弹出侧边栏</button>
    <button (click)="hiddenAside()">隐藏侧边栏</button>
</div>
<div id="aside">
    侧边栏
</div>

// 全局样式
body{
    width: 100%;
    overflow-x: hidden;
}

// ts组件中
export class TransitionComponent implements OnInit {
  showAside(){
    var asideDom = document.getElementById('aside');
    asideDom.style.transform = "translate(0,0)";
  }
  hiddenAside(){
    var asideDom = document.getElementById('aside');
    asideDom.style.transform = "translate(100%,0)";
  }
}
posted @ 2023-09-22 18:45  DogLeftover  阅读(13)  评论(0编辑  收藏  举报