Angular速成2 基本开发#

  • 这次主要找一套模板,看一下各个问题点。

  • 推荐先大致看一下这两本书,跑一下代码。是最新的angular12版本。 <<Angular Projects - Second Edition Build modern web apps by exploring Angular 12 with 10 different projects and cutting-edge technologies>> https://github.com/PacktPublishing/Angular-Projects-Second-Edition <<Angular Cookbook Over 80 actionable recipes every Angular developer should know>> https://github.com/PacktPublishing/Angular-Cookbook

  • 结合官方文档和之前的几本书: << ng-book The Complete book on Angular>> << Getting Started With Angular>> << Learning Angular>>

  • 另外需要各种js/ts的书和教程做参考。


模板#

  • themeforest.net 是一个模板网站。搜angular 12可以找到不少模板。 我买的这个 https://themeforest.net/item/minible-angular-admin-dashboard-template/31165271 ui库用的是Bootstrap。 这些模板一般二三十美元一个,会持续更新,包括更新angular版本。 一般包含几种排版,明暗主题,二十个左右常用例子页面,如登录注册、产品列表、dashboard页面等。 和所有的组件示例。 适应手机平板等。 代码也是比较清晰的。足够初学者学一阵子的。值得购买。

  • 我还买了fuse模板。发现结构和代码挺相似。不知道谁抄谁的。


Minible模板#

  1. 把minible模板的代码跑起来 进代码Admin目录 可能需要安装 ng add @angular-devkit/build-angular ng serve --host 0.0.0.0 可用ip访问4200端口

  2. 依赖 看package.json,跟一个ng new新建的项目比较。看看它用来哪些主要的库。 先不要改动,等以后熟练了再说。 ng-bootstrap chart ckeditor 等等各种

  3. 总体结构 app.module.ts里bootstrap默认是AppComponent

    app-routing.module.ts里定义路由。默认初始组件是LayoutComponent。

    Route的配置 https://angular.io/api/router/Route

    用loadChildren可以设置lazy load。打开组件才加载相关文件。

    canActivate可以配置一些逻辑,来决定当前用户能否打开这个组件。

    LayoutComponent里存一个当前排版的标志,控制页面的总排版,比如横还是竖。 然后横竖各做一个组件。根据当前标志显示对应的排版即可。

    以横板为例,horizontal.component.html包含顶部的。 中间是按当前路由显示各种功能页。 底部的。 右边有,点击齿轮按钮会滑出。

  4. 账号相关功能 login.component.ts包含登录框架,调用authFackservice的login来登录。 这个模板把service统一放一起。 AuthfakeauthenticationService.login用http请求后端登录接口。 成功后可以用localStorage存数据。 成功后跳转 this.router.navigate([‘/dashboard’]); 注册等页面同理。

    路由设置里有个canActivate,配置为AuthGuard。 auth.guard.ts里实现canActivate逻辑。 检查AuthfakeauthenticationService里设置的数据。 如果设置了正确的状态,判断为已登录,返回true。否则跳转到/account/login,返回false。

  5. 看代码 再结合各种文档和书籍,研究各个例子页面的代码和组件用法即可。


其他js/ts知识#

这里可以在线测试ts代码 https://www.typescriptlang.org/play

js/ts的异步机制#

promise#

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

如果要进行好几个操作,如果是异步一般用callback,可能出现大量的callback。 如果是同步,需要逐个处理每个操作的结果。 都可能导致混乱或难看。 比如要读好几个文件,或者发多次http。

用promise搭配then等语法可以把这种程序做得比较简洁。 例如

fs.promises.readFile("./test1.txt")
    .then((value) => {
        console.log(`ps test1.txt read : ${value}`);
        return fs.promises.readFile("./test2.txt");
    }).then((value) => {
        console.log(`ps test2.txt read : ${value}`);
        return fs.promises.readFile("./test3.txt");
    }).then((value) => {
        console.log(`ps test3.txt read : ${value}`);
    }).catch((error) => {
        console.log(`an error occurred : ${error}`);
    });

调用fs库接口的promise版本,会返回一个promise对象。 可以对promise对象用then/catch。如果promise是成功的,走then,否则就是出错走catch。 这样可以一行顺着写完。非常清晰。

一个Promise本质上是一个异步操作,一定会走一个异步的过程,无法同步完成一个Promise。 本质上也是一种callback。

主动起promise需要提供一个函数pf。pf有两个参数resolve和reject,都是返回void的函数。 成功时调resolve,失败时调reject。

function delayedPromise(): Promise<void> {
    // return new Promise object
    return new Promise<void>( // start constructor
        (
            resolve: () => void, // resolve function
            reject: () => void // reject function
        ) => {
            // start of function definition
            function afterTimeout() {
               resolve();
            }
            setTimeout(afterTimeout, 1000);
            // end of function definition
        }
    ); // end constructor
}

可以设置promise的数据类型,比如Promise<Product[]>。 resolve的参数类型得和promise保持一致,而reject不必。

function delayedPromise(timeout: number): Promise<number> {
    // return new Promise object
    return new Promise<number>( // start constructor
        (
            resolve: (timeout:number) => void, // resolve function
            reject: () => void // reject function
        ) => {
            // start of function definition
            function afterTimeout() {
                resolve(timeout);
            }
            setTimeout(afterTimeout, timeout);
            // end of function definition
        }
    ); // end constructor
}

这里改成了可设置定时,并返回传入的时长。

delayedPromise(3000).then((value) => {console.log(value)})
delayedPromise(1000).then((value) => {console.log(value)})

注意到1秒后先打印了1000,3秒后打印了3000。 起一个Promise是起了一个异步操作,不会阻塞,而是直接往下走,等结果来了再走回调。


async/await#

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function

js本身是单线程的,事件驱动的。回调也只不过是注册事件,本质还是单线程。

之前起了2个Promise,直接开始执行,不会阻塞。 有时我们需要等某个Promise有了结果才往下走,就得用async/await。

用async声明函数后,函数一定返回一个Promise。如果没有显示返回Promise,会做隐式转换。

await用来等待一个Promise,直到Promise有了结果才往下走。 await必须写在async函数里。

async function wtf() {
    await delayedPromise(3000).then((value) => {console.log(value)});
    delayedPromise(1000).then((value) => {console.log(value)});

}

wtf(); 

// 这样搞,3秒后打印3000,再过1秒打印1000。
可以直接取Promise的结果

async function wtf() {
    let a = await delayedPromise(3000);
    console.log(a);
    delayedPromise(1000);

}

wtf();

这时如果Promise被reject,在await处会挂掉。 我的环境下现象貌似是整个环境挂掉,wtf剩下的代码不再执行。已经起的Promise还是会正常跑。 加上try/catch可以抓到reject。

async function wtf() {
    try {
        let a = await delayedPromise(3000);
        console.log(a);
    } catch(error){
        console.log(`${error}`);
    }
    
    delayedPromise(1000);

}

wtf();

js有很多不同的运行环境,如果不处理reject,可能出各种问题。 一般推荐所有await都用上try/catch。


Promise VS Observable#

那么Promise和Observable感觉本质都是个回调。有啥区别?应该用哪个? https://stackoverflow.com/questions/37364973/what-is-the-difference-between-promises-and-observables

貌似主推Observable。 目前感觉Observable会更容易操作,只是请求一两个http的场景貌似更合适。

Observable可以转化成Promise https://rxjs-dev.firebaseapp.com/api/index/function/lastValueFrom


rxjs#

Observable “可观察”的数据。rxjs的基石。

Subject 一种Observable。可以让监听的数据广播。 ReplaySubject 一种Subject。会replay一次老的值给新的订阅者。


angular的一些常用概念#

component里做逻辑,处理数据,与模板(html)交互和数据绑定。

一般来说一个service不应横跨多个模块。 service的一些常见功能: 用http请求服务端数据 与浏览器的本地数据交互 log处理 数据传输

几种数据绑定

<li>{{hero.name}}</li>
数据从component到template。直接拿component的hero变量。

<app-hero-detail [hero]="selectedHero"></app-hero-detail>
https://angular.io/guide/property-binding
数据从component到template。
拿出component的selectedHero变量赋给hero。
即把app-hero-detail对应组件的hero赋了值。常用来传数据到子组件。

<li (click)="selectHero(hero)"></li>
https://angular.io/guide/user-input#binding-to-user-input-events
事件绑定到组件的函数。

<input type="text" id="hero-name" [(ngModel)]="hero.name">
https://angular.io/api/forms/NgModel
input内容和组件数据的双向绑定。

angular library#

可以用angular做库,把功能做到库里,进行分享和复用。 暂时不会。以后再学。


到此可以做一个简单的前端项目了。比如做一个博客(需要找一些angular的markdown库)。