萧慢慢 發表於 2021-2-18 08:15:00

Angular响应式表单及封装表单控件

<p>响应式表单也叫模型驱动型表单。</p>
<p>有三个重要元素FormControl,FormGroup和FormBuilder。还有一个FormArray。</p>
<p>验证器和异步验证器。</p>
<p>动态指定验证器。条件改变验证方式改变。</p>
<p>自定义FormControl。用于表单过于复杂之后,逻辑难以理清楚。把复杂问题拆成若干简单问题永远是万能钥匙。<span style="background-color: rgba(255, 255, 0, 1)">用于简化form表单自己的逻辑。</span></p>
<h1>一、登录表单</h1>
<p><img src="https://img2020.cnblogs.com/blog/315302/202102/315302-20210218075554419-1592806477.png" alt="" width="398" height="221" loading="lazy"></p>
<p>多个validators:</p>
<div>Validators.compose()返回ValidatorFn。</div>
<div>动态指定validator:&nbsp;</div>
<div>一开始可以不指定validator,在某些条件下动态指定validator:</div>
<div>
<div>&nbsp;this.form.controls['email'].setValidators(this.validate);</div>
<div>查看errors:</div>
<div>&lt;mat-error&gt;{{form.controls['email'].errors&nbsp;|&nbsp;json}}&lt;/mat-error&gt;</div>
<div>模板:</div>
</div>
<div class="cnblogs_code"><img src="https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" id="code_img_closed_301c386b-f22e-422f-92de-66164170932e" class="code_img_closed"><img src="https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" id="code_img_opened_301c386b-f22e-422f-92de-66164170932e" class="code_img_opened" style="display: none">
<div id="cnblogs_code_open_301c386b-f22e-422f-92de-66164170932e" class="cnblogs_code_hide">
<pre><span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">form </span><span style="color: rgba(255, 0, 0, 1)"></span><span style="color: rgba(0, 0, 255, 1)">="form"</span><span style="color: rgba(255, 0, 0, 1)"> (ngSubmit)</span><span style="color: rgba(0, 0, 255, 1)">="onSubmit(form,$event)"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">mat-card </span><span style="color: rgba(255, 0, 0, 1)">class</span><span style="color: rgba(0, 0, 255, 1)">="example-card"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
      <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">mat-card-header</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
            <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">mat-card-title</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>登录:<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">mat-card-title</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
      <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">mat-card-header</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
      <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">mat-card-content</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
            <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">mat-form-field </span><span style="color: rgba(255, 0, 0, 1)">class</span><span style="color: rgba(0, 0, 255, 1)">="example-full-width"</span><span style="color: rgba(255, 0, 0, 1)"> class</span><span style="color: rgba(0, 0, 255, 1)">="full-width"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
                <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">input </span><span style="color: rgba(255, 0, 0, 1)">type</span><span style="color: rgba(0, 0, 255, 1)">="text"</span><span style="color: rgba(255, 0, 0, 1)"> formControlName</span><span style="color: rgba(0, 0, 255, 1)">="email"</span><span style="color: rgba(255, 0, 0, 1)"> matInput placeholder</span><span style="color: rgba(0, 0, 255, 1)">="您的email"</span><span style="color: rgba(255, 0, 0, 1)"> style</span><span style="color: rgba(0, 0, 255, 1)">="text-align: right"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
                <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">mat-error</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>{{form.controls['email'].errors | json}}<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">mat-error</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
            <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">mat-form-field</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
            <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">mat-form-field </span><span style="color: rgba(255, 0, 0, 1)">class</span><span style="color: rgba(0, 0, 255, 1)">="example-full-width"</span><span style="color: rgba(255, 0, 0, 1)"> class</span><span style="color: rgba(0, 0, 255, 1)">="full-width"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
                <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">input </span><span style="color: rgba(255, 0, 0, 1)">type</span><span style="color: rgba(0, 0, 255, 1)">="password"</span><span style="color: rgba(255, 0, 0, 1)"> formControlName</span><span style="color: rgba(0, 0, 255, 1)">="password"</span><span style="color: rgba(255, 0, 0, 1)">matInput placeholder</span><span style="color: rgba(0, 0, 255, 1)">="您的密码"</span><span style="color: rgba(255, 0, 0, 1)"> style</span><span style="color: rgba(0, 0, 255, 1)">="text-align: right"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
            <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">mat-form-field</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
            <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">button </span><span style="color: rgba(255, 0, 0, 1)">mat-raised-button type</span><span style="color: rgba(0, 0, 255, 1)">="submit"</span><span style="color: rgba(255, 0, 0, 1)"> color</span><span style="color: rgba(0, 0, 255, 1)">="primary"</span><span style="color: rgba(255, 0, 0, 1)"> </span><span style="color: rgba(0, 0, 255, 1)">="!form.valid"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>登录<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">button</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>

      <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">mat-card-content</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
      <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">mat-card-actions </span><span style="color: rgba(255, 0, 0, 1)">class</span><span style="color: rgba(0, 0, 255, 1)">="text-right"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
            <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">p</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>还没有账户?<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">a </span><span style="color: rgba(255, 0, 0, 1)">routerLink</span><span style="color: rgba(0, 0, 255, 1)">="/register"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>注册<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">a</span><span style="color: rgba(0, 0, 255, 1)">&gt;&lt;/</span><span style="color: rgba(128, 0, 0, 1)">p</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
            <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">p</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>忘记密码?<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">a </span><span style="color: rgba(255, 0, 0, 1)">href</span><span style="color: rgba(0, 0, 255, 1)">=""</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>找回<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">a</span><span style="color: rgba(0, 0, 255, 1)">&gt;&lt;/</span><span style="color: rgba(128, 0, 0, 1)">p</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
      <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">mat-card-actions</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">mat-card</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>

    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">mat-card </span><span style="color: rgba(255, 0, 0, 1)">class</span><span style="color: rgba(0, 0, 255, 1)">="example-card"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
      <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">mat-card-header</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
            <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">mat-card-title</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>每日佳句<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">mat-card-title</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
            <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">mat-card-subtitle</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>满足感在于不断的努力,而不是现有成就。全心努力定会胜利满满。<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">mat-card-subtitle</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
      <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">mat-card-header</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
      <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">img </span><span style="color: rgba(255, 0, 0, 1)">mat-card-image src</span><span style="color: rgba(0, 0, 255, 1)">="/assets/images/quote_fallback.jpg"</span><span style="color: rgba(255, 0, 0, 1)"> alt</span><span style="color: rgba(0, 0, 255, 1)">=""</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
      <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">mat-card-content</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span><span style="color: rgba(0, 0, 0, 1)">
            Satisfaction lies in the effort, not in the attainment. Full effort is full victory.
      </span><span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">mat-card-content</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">mat-card</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">form</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span></pre>
</div>
<span class="cnblogs_code_collapse">View Code</span></div>
<p>组件:</p>
<div class="cnblogs_code"><img src="https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" id="code_img_closed_19836464-f1e7-4ef9-8e01-f0e979d60a52" class="code_img_closed"><img src="https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" id="code_img_opened_19836464-f1e7-4ef9-8e01-f0e979d60a52" class="code_img_opened" style="display: none">
<div id="cnblogs_code_open_19836464-f1e7-4ef9-8e01-f0e979d60a52" class="cnblogs_code_hide">
<pre><span style="color: rgba(0, 0, 0, 1)">export class LoginComponent implements OnInit {
form: FormGroup;
constructor(private fb: FormBuilder) {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> this.form = new FormGroup({</span>
    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">   email: new FormControl("wang@163.com", Validators.compose()),</span>
    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">   password: new FormControl("",Validators.required),</span>
    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> })</span>

    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">formBuilder不需要显示的new FormControl</span>
    <span style="color: rgba(0, 0, 255, 1)">this</span>.form = <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.fb.group({
      email: [</span>"wang@163.com", Validators.compose() ],
      password:[</span>""<span style="color: rgba(0, 0, 0, 1)">,Validators.required]

    })
}

ngOnInit(): </span><span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> {
   
}

onSubmit(form: FormGroup, event: Event) {
    event.preventDefault();
    console.log(JSON.stringify(form.value));
    console.log(form.valid);
}

validate(c:FormControl):{:any} </span>| <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">{
    </span><span style="color: rgba(0, 0, 255, 1)">if</span>(!<span style="color: rgba(0, 0, 0, 1)">c.value){
      </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;
    }
    const pattern</span>=/^wang+/<span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)">(pattern.test(c.value)){
      </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;
    }</span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">{
      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> {
      emailNotValid: </span>'The email must start with wang'<span style="color: rgba(0, 0, 0, 1)">
      }
    }
}

}</span></pre>
</div>
<span class="cnblogs_code_collapse">View Code</span></div>
<p>&nbsp;</p>
<h1>&nbsp;二、封装自定义表单控件</h1>
<p>把注册表单中的图片列表抽成一个独立组件。</p>
<p><img src="https://img2020.cnblogs.com/blog/315302/202102/315302-20210218080828236-618461140.png" alt="" width="379" height="212" loading="lazy"></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;ng g c shared/image-list-select生成组件</p>
<ul>
<li>&nbsp;实现ControlValueAccessor接口。实现writeValue(),registerOnChange()和registerOnTouched()</li>
<li>定义一个providers,令牌NG_VALUE_ACCESSOR和NG_VALIDATORS。用useExisting加forwardRef。并且设置multi为true。</li>
</ul>
<p>&nbsp;在image-list-select中可以放开的属性有很多,有没有必要一一放开?需要权衡。</p>
<p>&nbsp;如果想要充分的自由度的话,可以用transclude嵌入组件&lt;ng-content&gt;&lt;/ng-content&gt;。</p>
<p>&nbsp;在image-list-select中隔离封装,放开有限的属性。</p>
<p>模板:</p>
<div class="cnblogs_code"><img src="https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" id="code_img_closed_762c248f-43b4-4536-813f-1ca4d77ea5ac" class="code_img_closed"><img src="https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" id="code_img_opened_762c248f-43b4-4536-813f-1ca4d77ea5ac" class="code_img_opened" style="display: none">
<div id="cnblogs_code_open_762c248f-43b4-4536-813f-1ca4d77ea5ac" class="cnblogs_code_hide">
<pre><span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">div</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">span</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>{{title}}<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">span</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">mat-icon </span><span style="color: rgba(255, 0, 0, 1)">class</span><span style="color: rgba(0, 0, 255, 1)">="avatar"</span><span style="color: rgba(255, 0, 0, 1)"> </span><span style="color: rgba(0, 0, 255, 1)">="selected"</span><span style="color: rgba(255, 0, 0, 1)"> *ngIf</span><span style="color: rgba(0, 0, 255, 1)">="useSvgIcon else imgSelect"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span> <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">mat-icon</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">ng-template </span><span style="color: rgba(255, 0, 0, 1)">#imgSelect</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">img </span><span style="color: rgba(255, 0, 0, 1)"></span><span style="color: rgba(0, 0, 255, 1)">="selected"</span><span style="color: rgba(255, 0, 0, 1)"> alt</span><span style="color: rgba(0, 0, 255, 1)">="image selected"</span><span style="color: rgba(255, 0, 0, 1)"> class</span><span style="color: rgba(0, 0, 255, 1)">="cover"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">ng-template</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">div</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>

<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">div </span><span style="color: rgba(255, 0, 0, 1)">class</span><span style="color: rgba(0, 0, 255, 1)">="scroll-container"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">mat-grid-list </span><span style="color: rgba(255, 0, 0, 1)"></span><span style="color: rgba(0, 0, 255, 1)">="cols"</span><span style="color: rgba(255, 0, 0, 1)"> </span><span style="color: rgba(0, 0, 255, 1)">="rowHight"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">mat-grid-tile </span><span style="color: rgba(255, 0, 0, 1)">*ngFor</span><span style="color: rgba(0, 0, 255, 1)">="let item of items; let i = index"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
      <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">div </span><span style="color: rgba(255, 0, 0, 1)">class</span><span style="color: rgba(0, 0, 255, 1)">="image-container"</span><span style="color: rgba(255, 0, 0, 1)"> (click)</span><span style="color: rgba(0, 0, 255, 1)">="onChange(i)"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
      <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">mat-icon </span><span style="color: rgba(255, 0, 0, 1)">class</span><span style="color: rgba(0, 0, 255, 1)">="avatar"</span><span style="color: rgba(255, 0, 0, 1)"> </span><span style="color: rgba(0, 0, 255, 1)">="item"</span><span style="color: rgba(255, 0, 0, 1)"> *ngIf</span><span style="color: rgba(0, 0, 255, 1)">="useSvgIcon else imgItem"</span><span style="color: rgba(0, 0, 255, 1)">&gt;&lt;/</span><span style="color: rgba(128, 0, 0, 1)">mat-icon</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
      <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">ng-template </span><span style="color: rgba(255, 0, 0, 1)">#imgItem</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
          <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">img </span><span style="color: rgba(255, 0, 0, 1)"></span><span style="color: rgba(0, 0, 255, 1)">="item"</span><span style="color: rgba(255, 0, 0, 1)"> alt</span><span style="color: rgba(0, 0, 255, 1)">="image item"</span><span style="color: rgba(255, 0, 0, 1)"> </span><span style="color: rgba(0, 0, 255, 1)">="{'width': itemWidth}"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
      <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">ng-template</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
      
      <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">div </span><span style="color: rgba(255, 0, 0, 1)">class</span><span style="color: rgba(0, 0, 255, 1)">="after"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
          <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">div </span><span style="color: rgba(255, 0, 0, 1)">class</span><span style="color: rgba(0, 0, 255, 1)">="zoom"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
            <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">mat-icon</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>checked<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">mat-icon</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
          <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">div</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
      <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">div</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
      <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">div</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>

    <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">mat-grid-tile</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">mat-grid-list</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">div</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span></pre>
</div>
<span class="cnblogs_code_collapse">View Code</span></div>
<p>组件:</p>
<div class="cnblogs_code"><img src="https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" id="code_img_closed_90061d67-1296-43e6-8b93-7753f51fb7a9" class="code_img_closed"><img src="https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" id="code_img_opened_90061d67-1296-43e6-8b93-7753f51fb7a9" class="code_img_opened" style="display: none">
<div id="cnblogs_code_open_90061d67-1296-43e6-8b93-7753f51fb7a9" class="cnblogs_code_hide">
<pre><span style="color: rgba(0, 0, 255, 1)">import</span> { Component, forwardRef, Input, OnInit } from '@angular/core'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">import</span> { ControlValueAccessor, FormControl, NG_VALIDATORS, NG_VALUE_ACCESSOR } from '@angular/forms'<span style="color: rgba(0, 0, 0, 1)">;

@Component({
selector: </span>'app-image-list-select'<span style="color: rgba(0, 0, 0, 1)">,
templateUrl: </span>'./image-list-select.component.html'<span style="color: rgba(0, 0, 0, 1)">,
styleUrls: [</span>'./image-list-select.component.scss'<span style="color: rgba(0, 0, 0, 1)">],
providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> ImageListSelectComponent),
      multi: </span><span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">
    },
    {
      provide: NG_VALIDATORS,
      useExisting: forwardRef(() </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> ImageListSelectComponent),
      multi: </span><span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">
    }
]
})
export </span><span style="color: rgba(0, 0, 255, 1)">class</span> ImageListSelectComponent <span style="color: rgba(0, 0, 255, 1)">implements</span><span style="color: rgba(0, 0, 0, 1)"> ControlValueAccessor {
@Input() title </span>= "选择"<span style="color: rgba(0, 0, 0, 1)">
@Input() cols </span>= 6<span style="color: rgba(0, 0, 0, 1)">;
@Input() rowHight </span>= '64px'<span style="color: rgba(0, 0, 0, 1)">
@Input() items: string[] </span>=<span style="color: rgba(0, 0, 0, 1)"> [];
@Input() useSvgIcon: </span><span style="color: rgba(0, 0, 255, 1)">boolean</span> = <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
@Input() itemWidth </span>= '80px'<span style="color: rgba(0, 0, 0, 1)">;
selected: string </span>= ''<span style="color: rgba(0, 0, 0, 1)">;
constructor() { }

</span><span style="color: rgba(0, 0, 255, 1)">private</span> propagateChange = (_: any) =&gt;<span style="color: rgba(0, 0, 0, 1)"> { };

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">写值,设置控件的值form中setValue设置初始值,通过表单控件的writeValue方法设值。</span>
writeValue(obj: any): <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.selected =<span style="color: rgba(0, 0, 0, 1)"> obj;
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">控件view发生变化,把变化emit给表单</span>
registerOnChange(fn: any): <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.propagateChange =<span style="color: rgba(0, 0, 0, 1)"> fn;
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">什么状态算touched,告诉表单</span>
registerOnTouched(fn: any): <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> {
}

onChange(i: number) {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.selected = <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.items;
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.propagateChange(<span style="color: rgba(0, 0, 255, 1)">this</span>.selected); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">变化通知表单</span>
<span style="color: rgba(0, 0, 0, 1)">}

validate(c: FormControl): { : any } </span>| <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)"> {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">this</span>.selected ? <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)"> : {
      imageListInvalid: {
      valid: </span><span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">
      }
    }
}

}</span></pre>
</div>
<span class="cnblogs_code_collapse">View Code</span></div>
<h2>1,UI布局&nbsp;</h2>
<p>图片鼠标划过去如果没有任何反应,用户会无法感知到有没有选中,所以</p>
<div class="cnblogs_code">
<pre> <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">div </span><span style="color: rgba(255, 0, 0, 1)">class</span><span style="color: rgba(0, 0, 255, 1)">="image-container"</span><span style="color: rgba(255, 0, 0, 1)"> (click)</span><span style="color: rgba(0, 0, 255, 1)">="onChange(i)"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
      <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">mat-icon </span><span style="color: rgba(255, 0, 0, 1)">class</span><span style="color: rgba(0, 0, 255, 1)">="avatar"</span><span style="color: rgba(255, 0, 0, 1)"> </span><span style="color: rgba(0, 0, 255, 1)">="avator"</span><span style="color: rgba(0, 0, 255, 1)">&gt;&lt;/</span><span style="color: rgba(128, 0, 0, 1)">mat-icon</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
      <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">div </span><span style="color: rgba(255, 0, 0, 1)">class</span><span style="color: rgba(0, 0, 255, 1)">="after"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
          <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">div </span><span style="color: rgba(255, 0, 0, 1)">class</span><span style="color: rgba(0, 0, 255, 1)">="zoom"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
            <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">mat-icon</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>checked<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">mat-icon</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
          <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">div</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
      <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">div</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">div</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span></pre>
</div>
<p>让组件既处理icon又处理图片。用useSvgIcon控制,通过条件子句判断。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">mat-icon </span><span style="color: rgba(255, 0, 0, 1)">class</span><span style="color: rgba(0, 0, 255, 1)">=""</span><span style="color: rgba(255, 0, 0, 1)"> </span><span style="color: rgba(0, 0, 255, 1)">="selected"</span><span style="color: rgba(255, 0, 0, 1)"> *ngIf</span><span style="color: rgba(0, 0, 255, 1)">="useSvgIcon else imgSelect"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span> <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">mat-icon</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">ng-template </span><span style="color: rgba(255, 0, 0, 1)">#imgSelect</span><span style="color: rgba(0, 0, 255, 1)">&gt;&lt;/</span><span style="color: rgba(128, 0, 0, 1)">ng-template</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span></pre>
</div>
<p>list太多支持滚动。包在.scroll-container容器中。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(128, 0, 0, 1)">.scroll-container </span>{<span style="color: rgba(255, 0, 0, 1)">
    overflow-y</span>:<span style="color: rgba(0, 0, 255, 1)"> scroll</span>;<span style="color: rgba(255, 0, 0, 1)">
    height</span>:<span style="color: rgba(0, 0, 255, 1)"> 200px</span>;
}</pre>
</div>
<h2>&nbsp;2,实现表单控件</h2>
<p>可以通过&nbsp;formContrlName来操作</p>
<p>&nbsp;实现ControlValueAccessor接口。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">private</span> propagateChange = (_: any) =&gt;<span style="color: rgba(0, 0, 0, 1)"> { };

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">写值,设置控件的值form中setValue设置初始值,通过表单控件的writeValue方法设值。</span>
writeValue(obj: any): <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.selected =<span style="color: rgba(0, 0, 0, 1)"> obj;
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">控件view发生变化,把变化emit给表单</span>
registerOnChange(fn: any): <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.propagateChange =<span style="color: rgba(0, 0, 0, 1)"> fn;
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">什么状态算touched,告诉表单</span>
registerOnTouched(fn: any): <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> {
    </span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> Error('Method not implemented.'<span style="color: rgba(0, 0, 0, 1)">);
}

onChange(i: number) {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.selected = <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.items;
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.propagateChange(<span style="color: rgba(0, 0, 255, 1)">this</span>.selected); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">变化通知表单</span>
}</pre>
</div>
<p>providers中定义自己的provider,把自己注册进去。</p>
<p>包括NG_VALUE_ACCESSOR和NG_VALIDATORS。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> ImageListSelectComponent),
      multi: </span><span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">
    },
    {
      provide: NG_VALIDATORS,
      useExisting: forwardRef(() </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> ImageListSelectComponent),
      multi: </span><span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">
    }
]

validate(c: FormControl): { : any } </span>| <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)"> {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">this</span>.selected ? <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)"> : {
      imageListInvalid: {
      valid: </span><span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">
      }
    }
}</span></pre>
</div>
<h2>&nbsp;3,调用/使用</h2>
<p>在sharedModule中导出ImageListSelectComponent。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">app-image-list-select
          </span><span style="color: rgba(255, 0, 0, 1)"></span><span style="color: rgba(0, 0, 255, 1)">="true"</span><span style="color: rgba(255, 0, 0, 1)">
          </span><span style="color: rgba(0, 0, 255, 1)">="6"</span><span style="color: rgba(255, 0, 0, 1)">
          </span><span style="color: rgba(0, 0, 255, 1)">="'选择头像:'"</span><span style="color: rgba(255, 0, 0, 1)">
          </span><span style="color: rgba(0, 0, 255, 1)">="items"</span><span style="color: rgba(255, 0, 0, 1)">
          formControlName</span><span style="color: rgba(0, 0, 255, 1)">="avatar"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">app-image-list-select</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span></pre>
</div>
<p><img src="https://img2020.cnblogs.com/blog/315302/202102/315302-20210219082652990-108187634.png" alt="" width="518" height="284" loading="lazy"></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>2019-04-07</p>

</div>
<div id="MySignature" role="contentinfo">
    <p><strong>如果觉得本文对您有帮助~可以<span style="display: none">支付宝(左)或</span>微信支持一下:</strong></p>
<br>
<p style="display: none"><strong>看到小伙伴打赏时给我写一些鼓励的话,真的非常感动,谢谢你们。</strong></p>
<br>
<p style="display: none"><strong>我开了个微信公众号(<span>第三个二维码</span>)用来分享自己的职场英语相关学习经验,感兴趣可以关注,我会不断更新~</strong></p><br>
<div class="pay_img"><img style="display: none" class="alipay" src="http://files.cnblogs.com/files/starof/starof_zfb.bmp" alt="支付宝打赏"><img src="http://files.cnblogs.com/files/starof/starof_wx.bmp" alt="微信打赏"><img style="display: none" src="https://files.cnblogs.com/files/starof/starof_gzh.bmp" alt="微信公众号"></div><br><br><br>
来源:https://www.cnblogs.com/starof/p/10666517.html
頁: [1]
查看完整版本: Angular响应式表单及封装表单控件