当前位置: 首页 > Web前端 > HTML

构建一个自定义的angular2输入组件

时间:2023-04-02 19:44:24 HTML

构建一个自定义的angular2输入组件今天我们将学习如何正确构建一个输入组件,它具有与相同的功能,但也有自己的逻辑。在阅读本文之前,希望你至少看过一次官方文档和案例,一些具体的概念和细节文中不再赘述。我们先来看看本文介绍的组件的展示:OK,上图就是我们要实现的效果。那么,我们来分析一下我们的组件应该具备哪些功能。对焦时,底部边框是绿色的,有自己的部分逻辑。例如,如果有一个输入值,就会出现一个删除图标。当输入值为空时,会提示错误复制可以插入到其他DOM中,比如底部发送验证码按钮,支持输入的必要属性,比如maxlength,placeholder等,支持表单angular2表单控制表单的绑定。如上图所示,values都是从FormBuilder构造出来的。我们会一步步讲解如何实现这样一个自定义组件的功能;创建一个angular2组件让我们先构建一个基本的angular2组件,在这里我们首先创建一个名为input-control的新组件。首先是input-control.component.ts文件:@Component({selector:'input-control',templateUrl:'input-control.component.html',styleUrls:['input-control.component.scss'],封装:ViewEncapsulation.None,})然后是input-control.component.html文件:剩下的就是input-control.component.scss文件,这里就不贴代码了,大家可以根据自己的项目设置相应的样式最后就是我们调用的方式:请输入正确的手机号码

如果你对上面的一些属性和变量感到困惑,别着急,我来解答stepbystep!功能分解input属性@Input()有一点需要注意:我们是在用DIV来模拟一个输入的表现,同时有自己的逻辑;因此,当我们需要对应的input的属性值,我们需要从父容器传递给组件内部的input,所以这里需要用到@Input特性,我们在input-control.component.ts中定义一些我们需要的属性:@Component({selector:'input-control',templateUrl:'input-control.component.html',styleUrls:['input-control.component.scss'],host:{//宿主元素点击事件,触发focus()event'(click)':'focus()',//切换宿主元素焦点样式'[class.focus]':'focused'}})exportclassInputControlComponent{private_focused:boolean=false;private_value:any='';私人_禁用:布尔值=假;私有_readonly:布尔值=假;私有_required:布尔值=假;//外部传入属性@Input()type:string='text';@Input()名称:字符串=null;@Input()占位符:string=null;@Input()最小长度:数字;@Input()最大长度:数字;//value属性,拦截getvalue():any{返回这个._value;};@Input()设置值(v:any){v=this._convertValueForInputType(v);if(v!==this._value){this._value=v;//触发值改变事件,冒泡给父级this._onChangeCallback(v);}}//只读属性getfocused(){returnthis._focused;}@Input()getdisabled():boolean{returnthis._disabled;}setdisabled(value){this._disabled=this._coerceBooleanProperty(value);}@Input()getreadonly():boolean{returnthis._readonly;}setreadonly(value){this._readonly=this._coerceBooleanProperty(value);}@Input()getrequired():boolean{returnthis._required;}setrequired(value){this._required=this._coerceBooleanProperty(value);}}回想一下我们之前的input-control.component.html文件,我们定义了type、name、placeholder、minlength、maxlength可读写属性,以及value、readonly、disabled、required等只读属性,通过[attribute]="source"方法接收来自父数据的输入。OK,我们都知道如何从父级接收属性,那么我们来实现点击操作:我们先修改input-control.component.ts文件@Component({......host:{//宿主元素点击event,触发focus()事件'(click)':'focus()',//切换宿主元素焦点样式'[class.focus]':'focused'}})我们使用host属性给host元素对应操作,入口@Component的相关属??性;我们给宿主元素绑定一个点击事件,也就是,根据自己的属性focused切换一个.focus类。在我们组件的focus()事件中,我们需要聚焦组件内部的输入并切换其自身的聚焦值。为了在我们的组件中获取输入元素,这里我们需要使用@ViewChild()。修改input-control.component.ts文件如下:@Component({...host:{//宿主元素点击事件,触发focus()事件'(click)':'focus()',//switch宿主元素焦点样式'[class.focus]':'focused'}})exportclassInputControlComponent{.........private_focusEmitter:EventEmitter=newEventEmitter();@ViewChild('输入')_inputElement:ElementRef;//在组件内部输入元素@ViewChild('iconDelete')iconDelete:ElementRef;//删除图标元素constructor(privatehostRef:ElementRef){}//监听全局点击事件,如果不是当前input-control组,则视为失焦操作@HostListener('window:click',['$event'])inputControlBlurHandler(event){varparent=event.target;//当前节点如何不是宿主节点,不等于文档节点while(parent&&parent!=this.hostRef.nativeElement&&parent!=document){//取当前节点的父节点继续搜索parent=parent.parentNode;}//如果找到最顶层,则意味着它不再位于宿主元素内,触发失去焦点fnif(parent==document){this._focused=false;}}//主机聚焦focus(){//触发下面的_handleFocus()事件this._inputElement.nativeElement.focus();}//输入框焦点_handleFocus(event:FocusEvent){this._focused=true;这个._focusEmitter.emit(事件);}//清除输入值_handleClear(){this.value='';返回假;}//这里触发了blur操作,但是this._focused的值没有改变,//否则删除图标不能实现它的功能,//设置this._focused的值会由上面的@HostListener处理('window:click',['$event'])//触发父级的模糊事件_handleBlur(event:any){this._onTouchedCallback();这个._blurEmitter.emit(事件);}//对外暴露焦点事件@Output('focus')onFocus=this._focusEmitter.asObservable();……}上面代码中,我们通过宿主的focus()事件,让input元素获得焦点,input元素获得焦点后,会触发下面的_handleFocus()方法。在这个方法中,我们修改了组件本身的focused属性,并向父组件发出一个focus事件。同时我们的删除图标也根据组件的focused属性进行切换:我们的输入是双向绑定到组件内部的value属性的,所以在_handleClear之后,我们输入框的值自然就清空了。ValueaccessorControlValueAccessor完成上面的一些步骤后,我们组件的基本功能就完成了,但是最重要的是让我们的自定义组件能够获取到值的访问权限。官方文档中有提到https://github.com/angular/material2/blob/master/src/lib/input/input.ts查看官方文档后发现需要实现自定义的值component对于访问权限,我们需要继承ControlValueAccessor接口,同时在里面实现相应的接口//要实现双向数据绑定,这个必不可少(()=>InputControlComponent),multi:true};constnoop=()=>{};@Component({选择器:'input-control',templateUrl:'input-control.component.html',styleUrls:['input-control.component.scss'],host:{//宿主元素点击事件,触发focus()事件'(click)':'focus()',//切换宿主元素焦点样式'[class.focus]':'focused'},//encapsulation:ViewEncapsulation.None,providers:[INPUT_CONTROL_VALUE_ACCESSOR]})exportclassInputControlComponentimplementsControlValueAccessor{........./**通过registerOnTouched(ControlValueAccessor)注册的回调*This做表单验证时属性是必不可少的,*如果缺少这个属性,FormControl.touched属性将不会被检测到,切记!!*/私人_onTouchedCallback:()=>void=noop;/**通过registerOnChange(ControlValueAccessor)注册的回调*/private_onChangeCallback:(_:any)=>void=noop;/***向元素写入一个新值。*/writeValue(value:any){this._value=value;}/***设置控件接收到更改事件时要调用的函数。*/registerOnChange(fn:any){this._onChangeCallback=fn;};/***设置当控件接收到触摸事件时要调用的函数。*/registerOnTouched(fn:any){this._onTouchedCallback=fn;}………}如上代码所示,实现了这些对应的接口后,我们就可以像普通的input元素一样使用我们自定义的组件,让组件在里面加载其他的DOM元素。回头看我们上一篇文章开头的GIF图,我们也有一个获取验证码的按钮,我们的报错信息也放在了组件内部。为了支持这种形式,我们需要在组件中添加标签。在此之后,所有包裹在组件中的元素都会渲染到组件内部的父组件中去调用input-control:Pleaseentertheverificationcode

浏览器渲染后DOM结构:与FormControl结合使用的注意事项在后期,我会将自定义输入组件与FormControl一起使用。在使用的过程中,我们发现当我们需要使用.touched特性的时候,发现无法生效。查看资料后发现,如果我们需要激活这个功能,我们的输入组件必须监听blur事件,并在processingevent中调用并触发外部的blur事件。具体代码见前面的_handleBlur()内容。完整demo地址:mcare-app本demo集成了路由、子模块、服务、动态表单等功能的使用。有兴趣的可以参考一下,目前还在完善中。这个Demo是基于我做过的项目的UI,当然不会涉及到核心业务代码:)。参考资料Angular2material2官方UI库CUSTOMFORMCONTROLSINANGULAR2http://stackoverflow.com/questions/38447681/touched-untouched-not-updating-in-custom-input-component-angular-2