<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                本節我們展示一種更加簡單的批量刪除方法,它的代碼量并沒有增加,但思索量卻會小很多。無論你是科班出身,還是通過其它途徑加入到計算機科學與工程這個領域的,相信一定都學習過面向對象。而且,我們當前的Angular便是完全的面向對象的思想。提到面向對象,大家腦海中想到的最多的可能就是定義幾個類、幾個接口、將方法與屬性封裝起來等。 如果面向對象僅僅是定義幾個類、幾個接口,那顯然不足以支撐它如此大的名氣。本節中,我們將以面向對象的思想重寫上節中的刪除功能。 ## Student對象 在學生列表中,每行學生其實對應了一個學生對象。 ![image-20210608081519817](https://img.kancloud.cn/97/39/973915b2bcfb105cfa4d64209ba4c824_2514x218.png) 顯示的學生的姓名、學號等信息則可以認為是學生的屬性,而每行這個小小的選擇框可以被點擊與用戶進行交互 ,則可以認為是學生對象的方法。 那么是否可以按面向對象的思想,為Student加一個刪除被點擊的方法呢? ## 建立方法 找到`Student`實體,添加一個刪除被點擊的方法如下: ```typescript +++ b/first-app/src/app/entity/student.ts @@ -42,4 +42,8 @@ export class Student { this.email = data.email as string; this.clazz = data.clazz as Clazz; } + + public onDeleteClick(): void { + console.log('delete click'); + } } ``` 我們接下來修改學生列表組件的V層,測試一下: ```html +++ b/first-app/src/app/student/student.component.html @@ -20,7 +20,7 @@ </thead> <tbody> <tr *ngFor="let student of pageData.content; index as index"> - <td><input type="checkbox" (click)="onCheckboxClick(index)"></td> + <td><input type="checkbox" (click)="student.onDeleteClick()"></td> <td>{{index + 1}}</td> <td>{{student.name}}</td> <td>{{student.number}}</td> ``` 當點擊選擇框時,在控制臺得到了如下異常: ![image-20210608082934788](https://img.kancloud.cn/ba/9c/ba9c1c8ee1b87483c7d4f7f391fff2e3_1796x434.png) 提示說:`onDeleteClick`不是一個方法。無論我們怎么檢查語法或是重啟`ng t`,都將還是這個錯誤。 要想徹底的弄清楚這個問題,還要深入學習下`JSON對象`與`對象`間的區別。 ## 測試代碼 學習計算機最大的優勢在于其實驗成本極低。相較于土木、化工、機器、汽車等傳統行業,我們可以使用極低的成本來驗證自己的想法。這種極低的成本當然也可以應用到代碼示例上。 我們在`student.service.spec.ts`中新建一個測試方法,以代碼的方式深入學習下`JSON對象`與`對象`的區別: 首先,我們建立一個測試用例: ```typescript fit('JSON對象與對象', () => { }); ``` 然后在測試用例中新建一個`Test`類,該類有兩個屬性`id`與`name`: ```typescript fit('JSON對象與對象', () => { class Test { id: number; name: string; constructor(id: number, name: string) { this.id = id; this.name = name; } } }); ``` 接著,為該類增一個`sayHello()`方法: ```typescript fit('JSON對象與對象', () => { class Test { id: number; name: string; constructor(id: number, name: string) { this.id = id; this.name = name; } sayHello(): void { console.log('hello'); } } }); ``` 基礎的準備工作做完以后,我們依據該類建立一個對象,并調用對象上的`sayHello()`方法: ```typescript +++ b/first-app/src/app/service/student.service.spec.ts @@ -81,5 +81,11 @@ describe('StudentService', () => { console.log('hello'); } } + + const test = new Test(1, '123'); + console.log(test.id); + console.log(test.name); + test.sayHello(); }); }); ``` 單元測試通過,在`bash`中打印了對象上的屬性`id`、`name`的值,并且成功的調用了`sayHello`方法: ```bash LOG: 1 Firefox 89.0 (Mac OS 10.15): Executed 0 of 66 SUCCESS (0 secs / 0 secs) LOG: '123' Firefox 89.0 (Mac OS 10.15): Executed 0 of 66 SUCCESS (0 secs / 0 secs) LOG: 'hello' Firefox 89.0 (Mac OS 10.15): Executed 0 of 66 SUCCESS (0 secs / 0 secs) ``` > `bash`與瀏覽器控制臺查看打印信息大同小異。 ### JSON對象 接下來,我們再嘗試增加一個`JSON對象`: ```typescript test.sayHello(); + + const jsonString = '{"id": 2, "name": "456"}'; + const jsonTest = JSON.parse(jsonString) as Test; + console.log(jsonTest.id); + console.log(jsonTest.name); + jsonTest.sayHello(); }); ``` 此時控制臺在執行`sayHello()`方法時得到了一個異常: ```bash Firefox 89.0 (Mac OS 10.15): Executed 0 of 66 SUCCESS (0 secs / 0 secs) LOG: 2 Firefox 89.0 (Mac OS 10.15): Executed 0 of 66 SUCCESS (0 secs / 0 secs) LOG: '456' Firefox 89.0 (Mac OS 10.15): Executed 0 of 66 SUCCESS (0 secs / 0 secs) Firefox 89.0 (Mac OS 10.15) StudentService JSON對象與對象 FAILED TypeError: jsonTest.sayHello is not a function in main.js (line 2385) ``` 該異常類型與我們在學生對象上調用`onDeleteClick()`方法的錯誤類型相同,異常得以重現: ![image-20210608082934788](https://img.kancloud.cn/ba/9c/ba9c1c8ee1b87483c7d4f7f391fff2e3_1796x434.png) 上述異常的關鍵點在于在聲明`jsonTest`時,我們使用了`JSON.parse(jsonString) as Test;`而這個`as`代表`看做`。潛臺詞是:你可能并不是這個類型,但是沒有關系,我在這把你看做這個類型。這種`看做`的方式被編譯器認同,所以在編譯的階段可以順利通過。 我們剛剛使用 `JSON.parse(jsonString) as Test;`來將字符串`jsonString`轉換為了一個對象,該對象使用`as`關鍵字并聲明為`Test`。而我們把這種通過字符串轉換過來的對象,稱為`JSON對象`。 字符串只所以可以通過`JSON.parse(jsonString)`來轉換為JSON對象,是由于該字符串符合轉換為`JSON對象`的規范: ![image-20210608100049930](https://img.kancloud.cn/01/a6/01a61d44dca8a263fec497818e001c5d_1174x182.png) 如果不符合轉換為`JSON對象`的規范,則會發生轉換異常: ![image-20210608100208061](https://img.kancloud.cn/d6/a8/d6a8201c8e63595cd5d265ace5d9a8ec_1480x238.png) 也就是說:只有符合`JSON對象`轉換規范的字符串才能夠使用`JSON.parse()`方法將其轉換為對象。我把這種符合轉換規范的字符串稱為`JSON字符串`,而通過`JSON字符串`轉換過來的對象稱為`JSON對象`。 最后我們注釋掉刪除剛剛觸發異常的代碼后繼續學習。 ```typescript - jsonTest.sayHello(); + // jsonTest.sayHello(); ``` ### as 往往為了開發方便,我們還會根據`JSON字符串`的內容使用`as`關鍵字將其聲明為某個特定的類型。比如我們常用的`httpClient.get<T>`中的`T`便是這個作用。此處的泛型`T`僅僅是說,可以把后臺返回的數據看做是`T`,該`T`的類型本質上是個`JSON對象`,其僅具體`T`類型的屬性,但卻不具體`T`類型的方法。 由于在前臺的交互過程中,我們并沒有辦法對后臺的代碼進行約束。所以在與后臺對接時,只能是按照API的規范將返回值看做某個類型,而后臺具體返回的是不是這個類型,還需要在真實的與后臺對接時才能夠判斷出。 在這種特定的場景下,`as`是最適用不過的了。但由于`as`在類型上的靈活性,應該避免濫用,比如我們借助于`as`,完全可以這么寫: ```typescript const a = '123' as any as number[]; a.push(123); ``` 這種寫法在編譯時同樣不會出錯,但在運行時則必然出錯。 ## HttpClient.get<T>() 以`HttpClient`的`get<T>()`方法為例,其實質的作用是進行Http請求,然后將請求的結果在內部使用` JSON.parse()`以及`as`關鍵字將其**看做**`T`返回。所以使用`httpClient.get<T>()`等方法得到的數據的本質上是個不具有任何方法的`JSON對象`。 所以若要使用`Student`類上的`onDeleteClick()`方法,則需要將`JSON對象`轉換為`對象`。同時由于我們早早的就在`Stduent`類中聲明了如下構造函數: ```typescript constructor(data = {} as { id?: number, name?: string, number?: string, phone?: string, email?: string, clazz?: Clazz }) { ``` 該函數中的參數`data`恰恰也是通過`as`關鍵字來**看做**一個**對象**,被看做的**對象**上只擁有屬性(id, name,...)而不具備任何方法,所以`data`完全可以看做是一個`JSON對象`。 也就是說,通過`Student`的構造函數,可以將一個`JSON對象`轉換為`對象`。 思想有了,寫代碼便成了最簡單的事情: ```typescript +++ b/first-app/src/app/student/student.component.ts @@ -32,7 +32,10 @@ export class StudentComponent implements OnInit { this.studentService.pageOfCurrentTeacher({ page, size: this.size - }).subscribe(data => this.pageData = data); + }).subscribe(data => { + data.content = data.content.map(d => new Student(d)); + this.pageData = data; + }); } ``` 上述語句便使得`data.content`中的每一項都是一個Student對象,而不是看做Stduent對象的JSON對象了。 此時,當我們再次點擊某條數據前的選擇框時在控制臺得到了預期的效果: ```typescript LOG: 'delete click' ``` ## 完善功能 對象中的方法被觸發后,我們在`Student`類中再增加一個以`_`打頭的屬性,以`_`打頭代表該屬性該屬性與后臺不對接,是一個前臺的特有屬性。 ```typescript +++ b/first-app/src/app/entity/student.ts @@ -4,6 +4,11 @@ import {Clazz} from './clazz'; * 學生. */ export class Student { + /** + * 是否被選中 + */ + _checked = false; + ``` 此時我們使用的IDE將報一個語法錯誤: ![image-20210608111526422](https://img.kancloud.cn/db/18/db1861278a41f4eb2115483cb4d7cc84_2228x406.png) 它在說`TSLInt`報了一個語法錯誤:變量的名字必須是以下三種(小駝峰、大駝峰、大寫字母和下劃線)情況之一。 ## TSLint 在編程的世界時,以`lint`打頭的大多的作用都是語法檢查。所以如果你使用了是其它編程的語言,也可以使用`lint`后綴來查找相應的語法檢查器,比如`PHPLint`或是`JavaLint`。 所以`TSLint`顧名思義它是一個`typescript`的語法檢查器。 同時每個語法檢查器都會有一個相應的配置文件,而`TSLint`的配置文件則是位于項目根目錄的`tslint.json`: ```bash panjie@panjies-iMac first-app % tree -L 1 . ├── README.md ├── angular.json ├── e2e ├── karma.conf.js ├── node_modules ├── package-lock.json ├── package.json ├── src ├── tsconfig.app.json ├── tsconfig.json ├── tsconfig.spec.json └── tslint.json ?? ``` 該文件中對在`rules -> variable-name -> options`上對變量名做出了限制,在此我們增加一項:允許變量名以下劃線`_`打頭: ```json +++ b/first-app/tslint.json @@ -108,7 +108,8 @@ "options": [ "ban-keywords", "check-format", - "allow-pascal-case" + "allow-pascal-case", + "allow-leading-underscore" ] }, ``` 此時`Stduent`中的`_checked`字段的語法錯誤將會自動消失。 ## 完善功能 有了`_checked`屬性后,便可以在`onDeleteClick()`方法來改變這個屬性: ```typescript +++ b/first-app/src/app/entity/student.ts @@ -49,6 +49,6 @@ export class Student { } public onDeleteClick(): void { - console.log('delete click'); + this._checked = !this._checked; } } ``` 然后在組件中根據該屬性遍歷出要刪除的學生: ```typescript b/first-app/src/app/student/student.component.ts /** * 批量刪除按鈕被點擊 */ onBatchDeleteClick(): void { const beDeleteIds = this.pageData.content.filter(s => s._checked).map(d => d.id); ① if (beDeleteIds.length === 0) { Report.warning('出錯啦', '請先選擇要刪除的學生', '返回'); } else { Confirm.show('請確認', '該操作不可逆', '確認', '取消', () => { // 調用批量刪除 this.studentService.batchDelete(beDeleteIds) .subscribe(() => { this.loadData(this.page); }); }); } } ``` ①中我們連續使用了`filter()`以及`map()`方法。最終得到了一個待刪除的id數組。 ![image-20210608114419061](https://img.kancloud.cn/70/a9/70a9c4c99fab3412776781cd79957a05_916x184.png) 同時,此時原組件中的`onCheckboxClick()`等已經完成了歷史使命,可能退出歷史舞臺了: ```typescript +++ b/first-app/src/app/student/student.component.ts @@ -15,8 +15,6 @@ export class StudentComponent implements OnInit { page = 0; size = environment.size; - beDeletedIndexes = new Array<number>(); - constructor(private studentService: StudentService) { } @@ -58,18 +56,6 @@ export class StudentComponent implements OnInit { this.loadData(this.page); } - /** - * checkbox被點擊 - * @param index 索引值 - */ - onCheckboxClick(index: number): void { - if (this.beDeletedIndexes.indexOf(index) === -1) { - this.beDeletedIndexes.push(index); - } else { - this.beDeletedIndexes = this.beDeletedIndexes.filter(i => i !== index); - } - } - /** * 批量刪除按鈕被點擊 */ ``` 最后進行單元測試及集成測試,功能正常。 ## 小結 本節我們深入學習了`JSON對象` 與`對象`,兩者的區別大概可以概括為:JSON對象是長的像對象的數據集合,它就像一個`1:1`的汽車模型,除了不會動以外真實汽車有的它都有;有對象就是一個真正的汽車,該汽車除了有方向盤、發動機等屬性外,還可以在公路上馳騁。 面向對象的思想在于一切皆對象,面象對象的封裝性決定了其是屬性與方法的混合體。屬性是其對象內部的各個狀態,比如一個人的年齡、身高都是屬性,而方法則是該對象具有的功能。 在計算機的世界時,實踐出真知永不過時。從來沒有一門學問是有捷徑可以走的,變道超車也僅僅停留在記者的新聞稿里。我們相信,腳踏實地就是最佳的捷徑。在這條捷徑上學而時習之就是我們最有效的學習方法。 > 學而時習之,不亦說乎:時常能夠使用學習到的知識來指導實踐,不是一件令人心生喜悅的事嗎? ## 本節資源 | 鏈接 | 名稱 | | ------------------------------------------------------------ | ------------------- | | [https://github.com/mengyunzhi/angular11-guild/archive/step7.5.1.zip](https://github.com/mengyunzhi/angular11-guild/archive/step7.5.1.zip) | 本節源碼 | | [https://zhuanlan.zhihu.com/p/29119549](https://zhuanlan.zhihu.com/p/29119549) | JSON對象與對象 | | [https://palantir.github.io/tslint/](https://palantir.github.io/tslint/) | TSLint | | [https://eslint.org/](https://eslint.org/) | ESLint | | [https://ts.xcatliu.com/basics/type-assertion.html](https://ts.xcatliu.com/basics/type-assertion.html) | Typescript 類型斷言 |
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看