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: </div>
<div>一开始可以不指定validator,在某些条件下动态指定validator:</div>
<div>
<div> this.form.controls['email'].setValidators(this.validate);</div>
<div>查看errors:</div>
<div><mat-error>{{form.controls['email'].errors | json}}</mat-error></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)"><</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)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</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)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">mat-card-header</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">mat-card-title</span><span style="color: rgba(0, 0, 255, 1)">></span>登录:<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">mat-card-title</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">mat-card-header</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">mat-card-content</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</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)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</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)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">mat-error</span><span style="color: rgba(0, 0, 255, 1)">></span>{{form.controls['email'].errors | json}}<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">mat-error</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">mat-form-field</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</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)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</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)">></span>
<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">mat-form-field</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</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)">></span>登录<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">button</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">mat-card-content</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</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)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">p</span><span style="color: rgba(0, 0, 255, 1)">></span>还没有账户?<span style="color: rgba(0, 0, 255, 1)"><</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)">></span>注册<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">a</span><span style="color: rgba(0, 0, 255, 1)">></</span><span style="color: rgba(128, 0, 0, 1)">p</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">p</span><span style="color: rgba(0, 0, 255, 1)">></span>忘记密码?<span style="color: rgba(0, 0, 255, 1)"><</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)">></span>找回<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">a</span><span style="color: rgba(0, 0, 255, 1)">></</span><span style="color: rgba(128, 0, 0, 1)">p</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">mat-card-actions</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">mat-card</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</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)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">mat-card-header</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">mat-card-title</span><span style="color: rgba(0, 0, 255, 1)">></span>每日佳句<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">mat-card-title</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">mat-card-subtitle</span><span style="color: rgba(0, 0, 255, 1)">></span>满足感在于不断的努力,而不是现有成就。全心努力定会胜利满满。<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">mat-card-subtitle</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">mat-card-header</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</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)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">mat-card-content</span><span style="color: rgba(0, 0, 255, 1)">></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)"></</span><span style="color: rgba(128, 0, 0, 1)">mat-card-content</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">mat-card</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">form</span><span style="color: rgba(0, 0, 255, 1)">></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> </p>
<h1> 二、封装自定义表单控件</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> </p>
<p> </p>
<p> ng g c shared/image-list-select生成组件</p>
<ul>
<li> 实现ControlValueAccessor接口。实现writeValue(),registerOnChange()和registerOnTouched()</li>
<li>定义一个providers,令牌NG_VALUE_ACCESSOR和NG_VALIDATORS。用useExisting加forwardRef。并且设置multi为true。</li>
</ul>
<p> 在image-list-select中可以放开的属性有很多,有没有必要一一放开?需要权衡。</p>
<p> 如果想要充分的自由度的话,可以用transclude嵌入组件<ng-content></ng-content>。</p>
<p> 在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)"><</span><span style="color: rgba(128, 0, 0, 1)">div</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">span</span><span style="color: rgba(0, 0, 255, 1)">></span>{{title}}<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">span</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</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)">></span> <span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">mat-icon</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</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)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</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)">></span>
<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">ng-template</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">div</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</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)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</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)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</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)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</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)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</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)">></</span><span style="color: rgba(128, 0, 0, 1)">mat-icon</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</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)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</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)">></span>
<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">ng-template</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</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)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</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)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">mat-icon</span><span style="color: rgba(0, 0, 255, 1)">></span>checked<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">mat-icon</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">div</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">div</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">div</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">mat-grid-tile</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">mat-grid-list</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">div</span><span style="color: rgba(0, 0, 255, 1)">></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>=><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>=><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) =><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布局 </h2>
<p>图片鼠标划过去如果没有任何反应,用户会无法感知到有没有选中,所以</p>
<div class="cnblogs_code">
<pre> <span style="color: rgba(0, 0, 255, 1)"><</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)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</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)">></</span><span style="color: rgba(128, 0, 0, 1)">mat-icon</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</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)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</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)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">mat-icon</span><span style="color: rgba(0, 0, 255, 1)">></span>checked<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">mat-icon</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">div</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">div</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">div</span><span style="color: rgba(0, 0, 255, 1)">></span></pre>
</div>
<p>让组件既处理icon又处理图片。用useSvgIcon控制,通过条件子句判断。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)"><</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)">></span> <span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">mat-icon</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</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)">></</span><span style="color: rgba(128, 0, 0, 1)">ng-template</span><span style="color: rgba(0, 0, 255, 1)">></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> 2,实现表单控件</h2>
<p>可以通过 formContrlName来操作</p>
<p> 实现ControlValueAccessor接口。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">private</span> propagateChange = (_: any) =><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>=><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>=><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> 3,调用/使用</h2>
<p>在sharedModule中导出ImageListSelectComponent。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)"><</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)">></span>
<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">app-image-list-select</span><span style="color: rgba(0, 0, 255, 1)">></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> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </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]