angular雙向數(shù)據(jù)綁定實踐 - 分頁組件

angular在Form模塊提供了ngModel指令,應用了此指令的表單元素(input,select等)可以支持數(shù)據(jù)的雙向綁定疫萤。當ngModel指令應用在非表單元素上時會在運行時報錯先朦,也就是說ngModel的使用是有前置條件的缰冤。在web應用中,有時候也希望自己開發(fā)的組件(比如日期烙无、多選等)可以支持雙向綁定锋谐,那現(xiàn)在就研究下如何讓自定義的組件也支持雙向數(shù)據(jù)綁定。

先從ngModel指令開始說起截酷,構(gòu)造函數(shù)如下涮拗。

//ngModel構(gòu)造函數(shù)
constructor(parent: ControlContainer, validators: Array<Validator | ValidatorFn>, asyncValidators: Array<AsyncValidator | AsyncValidatorFn>, valueAccessors: ControlValueAccessor[]);

angular的注入器會為構(gòu)造函數(shù)中注入以下實例:

  • parent - 父容器
  • validators - 同步驗證器
  • asyncValidators - 異步驗證器
  • valueAccessors - 類型為ControlValueAccessor的數(shù)組

可以猜測雙向綁定依賴于ControlValueAccessor, 通過valueAccessors 可以實現(xiàn)數(shù)據(jù)的雙向流動。查看ControlValueAccessor的相關(guān)文檔:ControlValueAccessor是連接dom元素與form api的橋梁三热,如果創(chuàng)建自定義的form指令(組件也是指令的一種)并與form交互可實現(xiàn)此接口鼓择。接口方法如下:

interface ControlValueAccessor { 
  writeValue(obj: any): void
  registerOnChange(fn: any): void
  registerOnTouched(fn: any): void
  setDisabledState(isDisabled: boolean)?: void
}
  • writeValue - 寫入值,data從model流向view
  • registerOnChange - 注冊值改變后的回調(diào)
  • registerOnTouched - 注冊失去焦點事件后的回調(diào)
  • setDisabledState - angular會在control的disabled狀態(tài)改變時調(diào)用此方法

在angualr的Form模塊中找下實現(xiàn)了ControlValueAccessor的類就漾∧拍埽可以發(fā)現(xiàn)DefaultValueAccessor, RadioControlValueAccessor, NumberValueAccessor, SelectControlValueAccessor, SelectMultipleControlValueAccessor,這些類均實現(xiàn)了ControlValueAccessor 接口抑堡。
在源碼中查看DefaultValueAccessor摆出,類聲名的相關(guān)代碼如下:

export const DEFAULT_VALUE_ACCESSOR: any = {
  provide: NG_VALUE_ACCESSOR,
  useExisting: forwardRef(() => DefaultValueAccessor),
  multi: true
};
@Directive({
  selector:
      'input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]',
  host: {
    '(input)': '$any(this)._handleInput($event.target.value)',
    '(blur)': 'onTouched()',
    '(compositionstart)': '$any(this)._compositionStart()',
    '(compositionend)': '$any(this)._compositionEnd($event.target.value)'
  },
  providers: [DEFAULT_VALUE_ACCESSOR]
})
export class DefaultValueAccessor implements ControlValueAccessor {}

看到這里,差不多就可以弄清楚如何實現(xiàn)一個支持雙向綁定的指令了首妖。兩個關(guān)鍵點:

  • 指令需要實現(xiàn)ContrlValueAccessor接口
  • 為指令提供ACCESSOR偎漫,以便注入器在ngModel中可注入accessor實例執(zhí)行雙向綁定

接下來,就照著DefaultValueAccessor 實現(xiàn)一個支持雙向綁定的組件有缆。之前項目中象踊,寫過一個jquery版本的分布插件,這里實現(xiàn)的angular組件就以分頁為例(需要bootstrap)棚壁。分頁組件要實現(xiàn)以下功能:

  • 支持雙向數(shù)據(jù)綁定
  • 可以顯示分頁信息
  • 頁碼改變后回調(diào)

pager.component.ts雙向綁定相關(guān)代碼如下:

import { Component,forwardRef, Input, Output, ViewChild, EventEmitter,ViewEncapsulation } from '@angular/core';
import {ControlValueAccessor,NG_VALUE_ACCESSOR} from '@angular/forms';

export const PAGER_CONTROL_VALUE_ACCESSOR: any = {
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => PagerComponent),
    multi: true
};
@Component({
    selector: 'upc-pager',
    templateUrl: './pager.component.html',
    encapsulation:ViewEncapsulation.None,
    styleUrls:['./pager.component.css'],
    providers:[PAGER_CONTROL_VALUE_ACCESSOR]
})
export class PagerComponent implements ControlValueAccessor {
    writeValue(v: any): void {
        this.currentEle.nativeElement.value = v;
    }
    registerOnChange(fn: any): void {
        this._onChange=fn;
    }
    registerOnTouched(fn: any): void {
        this._onTouched=fn;
    }
    setDisabledState?(isDisabled: boolean): void {
    }
    to(v: number,back=false, event?: Event) {
        if (event) {
            event.preventDefault();
        }
        if (v <= this.totalPage && v != this._current && v > 0) {
            this._current = v;
            this._pageInfo=this.getPageInfo();
            this.writeValue(v);
            this._onChange(this._current);
            this.onPageChanged.emit(v);
        }else{
            back?this.currentEle.nativeElement.value = this._current:void 0;
        }
    }
}

使用方式如下:

<upc-pager [pageSize]="pageSize"  [showInfo]="true" [(ngModel)]="current" [totalItem]="total" (onPageChanged)='onPageChanged($event)'></upc-pager>

效果圖如下:


pager.jpg

完整代碼:https://github.com/upcyoung/upc-pagination/tree/master/src/pager

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末检碗,一起剝皮案震驚了整個濱河市井厌,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖戈轿,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件剧包,死亡現(xiàn)場離奇詭異贷岸,居然都是意外死亡晋南,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進店門蚣驼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來魄幕,“玉大人,你說我怎么就攤上這事颖杏〈吭桑” “怎么了?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵留储,是天一觀的道長翼抠。 經(jīng)常有香客問我,道長获讳,這世上最難降的妖魔是什么阴颖? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮丐膝,結(jié)果婚禮上量愧,老公的妹妹穿的比我還像新娘钾菊。我一直安慰自己,他們只是感情好偎肃,可當我...
    茶點故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布煞烫。 她就那樣靜靜地躺著,像睡著了一般累颂。 火紅的嫁衣襯著肌膚如雪滞详。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天紊馏,我揣著相機與錄音料饥,去河邊找鬼。 笑死瘦棋,一個胖子當著我的面吹牛稀火,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播赌朋,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼篇裁!你這毒婦竟也來了沛慢?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤达布,失蹤者是張志新(化名)和其女友劉穎团甲,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體黍聂,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡躺苦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了产还。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片匹厘。...
    茶點故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖脐区,靈堂內(nèi)的尸體忽然破棺而出愈诚,到底是詐尸還是另有隱情,我是刑警寧澤牛隅,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布炕柔,位于F島的核電站,受9級特大地震影響媒佣,放射性物質(zhì)發(fā)生泄漏匕累。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一默伍、第九天 我趴在偏房一處隱蔽的房頂上張望欢嘿。 院中可真熱鬧授霸,春花似錦、人聲如沸际插。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽框弛。三九已至辛辨,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間瑟枫,已是汗流浹背斗搞。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留慷妙,地道東北人僻焚。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像膝擂,于是被迫代替她去往敵國和親虑啤。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,864評論 2 354