@angglar/formsソース解析の双方向結合


Anglarの@anglar/formsパッケージはNgModelコマンドを提供して、双方向結合を実現します.即ちJS変数(nameと仮定します.)と一つのDOM元素(input元素と仮定します.)を結びつけると、nameの値が変化し、input元素のvalueも自動的に変化します.input元素のvalueが変化し、nameの値も自動的に変化します.下記のコードのように、最も簡単な双方向結合(stackblitz demoも見られます)を示します.
@Component({
  selector: 'my-app',
  template: `
    
    
    

{{name}}

`, styleUrls: [ './app.component.css' ] }) export class AppComponent { name = 'banana'; }
上のコードはNgModelコマンドを使用して変数nameとinput DOM元素を双方向に結合しました.ここではNgModelの本質をより明確に理解するために、[gModel]文法飴を使用していません.実は、テンプレートに「(xxx)」という「BAANA_」を書いています.BOX'文法、@anglar/comppilerのTemplate Paserはこの文法を[xxx]と(xxChange)に分解して、L 448-L 453とL 501-L 505を見ることができますので、[xxx]は手間を省くための簡単な書き方だけです.
stackblitz demoを見ると、inputの値を変更すると、name変数の値が自動的に変化します.これはnameと紐付けられているpタグの値から自動的に変化します.buttonをクリックしてnameの値を修正すれば、input入力ボックス内のvalue値も変化します.これはinputボックス内の値から変化が見られます.NgModelコマンドはどうやって双方向に結合されますか?
NgModel命令の双方向バインディング原理を理解する前に、まず双方向バインディングの最も簡単な形式を見てもいいです.


Hello {{country}}!

buttonをクリックしてmodelを修正すると、inputのvalue値が自動的に修正されます.即ち、自動的にviewを修正します.データの流れの方向はmodel->viewです.inputボックスの値を更新すると、countryというmodel値が自動的に修正されます.これはバインディングされたpラベルから見られます.このとき、データの流れの方向はview->modelです.もちろん、これは最も単純で、最も拡張不可能な双方向結合の例であり、もし命令を設計するならば、viewの異なるタイプを考慮するだけでなく、データ検証問題を考慮する必要がある.この簡単な例は、NgModel命令の本質と類似している.
もし自分でこのような双方向バインディングコマンドを設計するなら、その入力はバインディングされた変数nameであり、このコマンドはnameを受け取ってからinput要素のvalue値を更新します.(textarea、selectなどDOM元素、さらにはコンポーネントなどのカスタムDOM元素もサポートします.).このようにnameが変化し、inputのvalueも自動的に変化します.すなわちmodel-view.出力の必然はinput元素のvalue値で、それからnameに値を割り当てて、このようにinput元素の値は変化して、name値も自動的に変化して、つまりview->model.ここでの最も難しい点は、DOM要素(元のものやカスタムDOM要素に関係なく)の値を書くことができ、DOM要素の値の変化を監視し、変化の値を読み取ることができることである.したがって、元のDOM元素をサポートするために、またはカスタムDOM元素をサポートするために、良いデザインモードがあるために、必然的にインターフェースを抽象化して、DOM元素の値を書き込みと傍受するためにコマンドを支援します.
今、私達は2つの問題をはっきりさせなければなりません.name値が変化する時、inputのvalueはどのように自動的に変化しますか?inputのvalueの変化、nameの値はどのように自動的に変化しますか?
inputに結合されたNgModelコマンドは、実際に実行されると、その構造関数は、まずControlValueAccessオブジェクトを検索します.このControl ValueAccess orは前述の抽象的なオブジェクトです.上記のテンプレートのinput要素はNgModelコマンドだけではなく、実際にDefaultValueAccessorコマンドをバインドしています.これはコマンドのセレクタから知ることができます.inputテンプレートがこのように書かれている場合:
それはDefaultValueAccess指令だけでなく、NumberValueAccess指令も結びつけられています.
DefaultValue Access orのproviders属性でNGを提供しました.VALE_ACCESSORトークンは、トークンが指すオブジェクトがDefaultValueAccessであるため、NgModelアーキテクチャ関数に注入されたNG_VALE_ACCESSORトークンに含まれるControl ValueAccess orのオブジェクト配列はDefault ValueAccessしかありません.type=「number」のinputであれば、valueAccess orsはNumberValueAccess orとDefaultValueAccess orの2つのオブジェクトを含んでいます.コンストラクタの中のselectValue Access orの方法は順番にNGを巡回します.VALE_ACCESSORトークンが提供するControl ValueAccessターゲット配列は、カスタムのControl ValueAccess orが優先的にカスタムを選択し、@anglar/forms内蔵のControl ValueAccess orが内蔵のものを選択します.そうでなければ、最後にデフォルトのControlValueAccessオブジェクトを選択します.本文のデモに対して、それはデフォルトのDefaultValueAccessの対象です.注意する点は、注入のNG_です.VALE_ACCESSORトークンは飾り器@Selfを持っていますので、この依存性は自分で調べられます.自分の意味はNgModel命令自体です.これと一緒にinput要素の他のコマンドにマウントします.また、inputにはvalidatorsコマンドがバインドされていませんので、注入されたNGです.ValDATORSとNG_ASYNC_VALDATORSトークン解析の値は空で、inputは単独で使用して、form元素内に置かれていない、またはFormGroupバインディングの要素内にあるので、宿主コントロール容器CotrolContinerが存在しない、つまりparentも空です.
NgModelコマンドは、初の実装時に_を実行します.setUpControl()メソッドは、Control ValueAccess orを利用して、NgModelコマンド内部のFormControlオブジェクトをDOM元素と結びつけます.本デモでは、NgModelコマンドでバインディングされたinputは親コントロール容器がないため、_を呼び出します.setsUpStandarlone方法は主にsetUpControl()であり、この方法は主に2点を含んでいます.第1のポイントは、setUpViewe Change Pipeline()を呼び出してDefaultValueAccess orオブジェクトに1つのコール関数を登録し、input値が変化した時にinputイベントをトリガすると、このコールバック関数を実行します.このコールバック関数の論理は、FormControlのvalueを更新し、2番目はNgModelコマンドにngModelChangeイベントをスローさせることであり、このイベントに含まれる値は現在のinputの変化の新しい値です.だから、setUpViePipeline()方法の役割はview-modelのパイプを構築することです.FormControlオブジェクトのvalue値を同期させ、NgModelコマンドにこの新しい値を出力させます.第二のポイントは、FormControlオブジェクト内にsetUpModel Chare Pipelineを呼び出すことによってフィードバックを登録します.このフィードバックロジックは、FormControlのvalue値が変化したときです.trueのためには、FormControlのvalue値FormControl.setValueを更新する必要があります.これにより、前述のFormControlオブジェクト内のコールバック関数がトリガされます.Control Valess.wrol.writeValess(ここではinput)のvaluteを使用します.その後、NgModelコマンドにngmodel Changeイベントを投げさせます.このイベントに含まれる値は現在のFormControlオブジェクトの変化の新しい値です.だから、setUpModel Change Pipeline()メソッドの役割はmodel->viewのパイプを構築することです.このようにFormControlオブジェクトの値が変化した時、viewのvalueを同期して出力します.
以上の説明により、name値が変化した時、inputのvalueはどのように自動的に変化したかが分かります.inputのvalueが変化した時、name値はどのように自動的に変化しますか?(一つずつリンクをクリックしてソースを調べたほうがいいです.より効率がいいです.)一つの言葉は、NgModelコマンドの初期化時に2つのフィードバックをインストールして、データの流れの方向はview-molから変更されました.FormControlオブジェクトを更新し、その値を持つngModel Changeイベントを抛り出し、データの流れ方向がmodel->viewの場合、Control Value Access orを利用してview値を更新し、同時にこの値を持つngmodel Changeイベントを抛り出す.投げ出したngModelChangeイベントには新しい値が含まれています.テンプレートの中の$eventは@anglar/comppilerによって特殊処理され、ngModel Changeイベントのために投げ出された値です.
もちろん、本論文ではValidatorsが存在することを考慮していません.inputテンプレートが次のコードに修正された場合:
このテンプレートはNgModel命令を結びつける以外に、RequiredValidator命令を結びつけています.データの流れの方向がview->modelであるか、それともmodel->viewであるかに関わらず、データの流れの前に検証器を実行して、データの有効性を検証する必要があります.このようにNgModelのコンストラクタにはRequiredValidatorオブジェクトが含まれています.そしてこのValidatorをFormCotrolオブジェクトに転送して、最後にvalidatoChangeフィードバックを登録します.そうすると、FormControl値が更新されるとValidatorsが実行されます.
つまり、NgModelコマンドはmodel viewのデータフローを管理しています.内部にはFormControlオブジェクトがあります.保存値を読み取り、有効性を検証します.FormControlから読み取った値は外部に伝わったmodelに割り当てられます.viewはControl ValueAccess orによって読みます.全体の@angglar/formsパッケージのデザインもこのようなデータの流れの形式によってで、複雑ではありません.
AnglarフォームのControlValueAccessをどうやって書くかは、@anglar/forms関連記事を読むこともできます.