分享一个最近写的支持表单验证的时间选择组件。

import {AfterViewInit, Component, forwardRef, Input, OnInit, Renderer} from "@angular/core";
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from "@angular/forms";
@Component({
selector: 'time-picker',
template: `
<div id="container">
<input id="input_box" readonly
[(ngModel)]="_value"
[ngStyle]="{'width.px': inputWidth, 'height.px': inputHeight}"
(click)="onInputClick($event)"
(focus)="onInputFocus($event)"
(blur)="onInputBlur()"> <div id="panel" *ngIf="showBox">
<div class="ui-g">
<div class="ui-g-4 title">小时</div>
<div class="ui-g-8 title">分钟</div>
</div>
<div class="ui-g">
<div class="ui-g-4">
<span class="item" *ngFor="let hour of hours" [ngClass]="{'selected': hour == selectedHour}" (click)="onHourClick(hour, $event)">{{hour}}</span>
</div>
<div class="ui-g-8">
<span class="item" *ngFor="let minute of minutes" [ngClass]="{'selected': minute == selectedMinute}" (click)="onMinuteClick(minute, $event)">{{minute}}</span>
</div>
</div>
</div>
</div>
`,
styles: [`
:host{
display: inline-block;
}
#container{
position: relative;
}
#input_box{
outline: none;
box-sizing: border-box;
padding: 0 3px;
}
#panel{
position: absolute;
width: 400px;
background-color: white;
box-shadow: 0 2px 8px 4px rgba(0,0,0,0.2);
z-index: 2000;
}
.title{
text-align: center;
}
.item{
display: inline-block;
width: 30px;
height: 30px;
line-height: 30px;
text-align: center;
cursor: pointer;
}
.item:hover, .item.selected{
background-color: #0b7dd8;
color: white;
}
`],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => TimePicker), // 此时TimePicker未声明,因此需要使用forwardRef
multi: true
}
]
})
export class TimePicker implements ControlValueAccessor, OnInit, AfterViewInit{ // ControlValueAccessor,一座表单控件和原生元素或自定义输入组件之间的桥梁
@Input() inputWidth = 100; // 输入框宽度
@Input() inputHeight = 30; // 输入框高度
_value: string;
showBox = false; // 控制选择面板显示与否,true为显示
hours = []; // 小时数组
minutes = []; // 分钟数组
hourIsSelect = false; // 打开选择面板后,标记小时是否被点击
minIsSelect = false; // 打开选择面板后,标记分钟是否被点击 selectedHour; // 当前小时
selectedMinute; // 当前分钟 bodyClickListener: any; constructor(
public renderer: Renderer
){} ngOnInit(){
for(let i = 0; i < 24; i++){
let h;
if(i < 10){
h = '0' + i;
}else{
h = '' + i;
}
this.hours.push(h);
}
for(let j = 0; j < 60; j++){
let m;
if(j < 10){
m = '0' + j;
}else{
m = '' + j;
}
this.minutes.push(m);
}
} ngAfterViewInit(){
this.bodyClickListener = this.renderer.listenGlobal('body','click', () => { this.hide() });
} onChange = (time: string) => {};
onTouched = () => {}; get value(): string{
return this._value;
} set value(val: string){
if(val !== this._value){
this._value = val;
this.onChange(val);
}
} /**
* 实现ControlValueAccessor中的方法,用于将model显示到view
* @param val
*/
writeValue(val: string): void{
if(val !== this._value){
this._value = val;
}
if(this._value){
let time = this._value.split(':');
this.selectedHour = time[0];
this.selectedMinute = time[1];
}
} /**
* 实现ControlValueAccessor中的方法,用于通知Angular值已被修改
* @param fn
*/
registerOnChange(fn: (time: string) => void): void{
this.onChange = fn;
} /**
* 实现ControlValueAccessor中的方法,用于通知Angular输入框被鼠标聚焦过
* @param fn
*/
registerOnTouched(fn: () => void): void{
this.onTouched = fn;
} /**
* 隐藏选择面板
*/
hide(){
this.showBox = false;
this.hourIsSelect = false;
this.minIsSelect = false;
} /**
* 显示选择面板
*/
show(){
this.showBox = true;
} /**
* 输入框获得焦点
* @param event
*/
onInputFocus(event){
event.stopPropagation();
this.show();
} /**
* 输入框失去焦点
*/
onInputBlur(){
this.onTouched();
} /**
* 输入框点击
* @param event
*/
onInputClick(event){
event.stopPropagation();
} /**
* 选择小时
* @param h
* @param event
*/
onHourClick(h, event){
event.stopPropagation(); this.selectedHour = h;
this.value = this.selectedHour + ':' + (this.selectedMinute ? this.selectedMinute : '00'); this.hourIsSelect = true;
if(this.hourIsSelect && this.minIsSelect){
this.hide();
}
} /**
* 选择分钟
* @param min
* @param event
*/
onMinuteClick(min, event){
event.stopPropagation(); this.selectedMinute = min;
this.value = (this.selectedHour ? this.selectedHour : '00') + ':' + this.selectedMinute; this.minIsSelect = true;
if(this.hourIsSelect && this.minIsSelect){
this.hide();
}
}
}

Angular:自定义表单控件的更多相关文章

  1. angular 响应式自定义表单控件—注册头像实例

    1. 组件继承ControlValueAccessor,ControlValueAccessor接口需要实现三个必选方法 writeValue() 用于向元素中写入值,获取表单的元素的元素值 regi ...

  2. Angular19 自定义表单控件

    1 需求 当开发者需要一个特定的表单控件时就需要自己开发一个和默认提供的表单控件用法相似的控件来作为表单控件:自定义的表单控件必须考虑模型和视图之间的数据怎么进行交互 2 官方文档 -> 点击前 ...

  3. AngularJS自定义表单控件

    <!doctype html> <html ng-app="myApp"> <head> <script src="G:\\So ...

  4. Angular 从入坑到挖坑 - 表单控件概览

    一.Overview angular 入坑记录的笔记第三篇,介绍 angular 中表单控件的相关概念,了解如何在 angular 中创建一个表单,以及如何针对表单控件进行数据校验. 对应官方文档地址 ...

  5. MVC树控件,mvc中应用treeview,实现复选框树的多层级表单控件

    类似于多层级的角色与权限控制功能,用MVC实现MVC树控件,mvc中应用treeview,实现复选框树的多层级表单控件.最近我们的项目中需要用到树型菜单,以前使用WebForm时,树型菜单有微软提供的 ...

  6. Vue.js学习笔记——表单控件实践

    最近项目中使用了vue替代繁琐的jquery处理dom的数据更新,个人非常喜欢,所以就上官网小小地实践了一把. 以下为表单控件的实践,代码敬上,直接新建html文件,粘贴复制即可看到效果~ <! ...

  7. C#中缓存的使用 ajax请求基于restFul的WebApi(post、get、delete、put) 让 .NET 更方便的导入导出 Excel .net core api +swagger(一个简单的入门demo 使用codefirst+mysql) C# 位运算详解 c# 交错数组 c# 数组协变 C# 添加Excel表单控件(Form Controls) C#串口通信程序

    C#中缓存的使用   缓存的概念及优缺点在这里就不多做介绍,主要介绍一下使用的方法. 1.在ASP.NET中页面缓存的使用方法简单,只需要在aspx页的顶部加上一句声明即可:  <%@ Outp ...

  8. 认识HTML中表格、列表标签以及表单控件

    前端之HTML,CSS(二) HTML标签 列表标签 无序列表:闭标签,由<ul><li></li>...</ul>组合而成,效果成纵向列表.格式:&l ...

  9. 仿苹果电脑任务栏菜单&&拼图小游戏&&模拟表单控件

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

随机推荐

  1. python-异常处理总结

    一.异常处理 在程序运行的过程中,总会遇到各种各样的错误.程序一出错就停止运行了,下面的代码就不能运行了:这时候就需要捕捉异常,通过捕捉异常,再去做对应的处理. e.g: info = { " ...

  2. Net的网络层的构建(源码分析)

    概述 网络层的构建是在Net<Dtype>::Init()函数中完成的,构建的流程图如下所示: 从图中可以看出网络层的构建分为三个主要部分:解析网络文件.开始建立网络层.网络层需要参与计算 ...

  3. Python uuid库中 几个uuid的区别

    在用到uuid库的时候,发现uuid有很多个,比较好奇,就查了一下他们的区别 uuid1()——基于时间戳 uuid2()——基于分布式计算环境DCE(Python中没有这个函数) uuid3()—— ...

  4. [开发技巧]·如何让离线安装Python包

    [开发技巧]·如何让离线安装Python包 1.问题描述 PyPI(Python Package Index)是python官方的第三方库的仓库,所有人都可以下载第三方库或上传自己开发的库到PyPI. ...

  5. 在VS Code中使用Jupyter Notebook

    一.安装配置 1.在扩展商店中安装官方的Python扩展包 2.系统已经安装了Jupyter Notebook 由于系统上的Python环境是用Anaconda安装的,已经有Jupyter Noteb ...

  6. 日记smarthome

    测试命令:测试命令 7e 7e 两个字节 一个字节  两个字节 一个字节     解释: 两个字节是userid的值 int Userid = data[i] * 256 + data[i + 1]; ...

  7. 单调栈 && 洛谷 P2866 [USACO06NOV]糟糕的一天Bad Hair Day(单调栈)

    传送门 这是一道典型的单调栈. 题意理解 先来理解一下题意(原文翻译得有点问题). 其实就是求对于序列中的每一个数i,求出i到它右边第一个大于i的数之间的数字个数c[i].最后求出和. 首先可以暴力求 ...

  8. Codeforces 979D (STL set)(不用Trie简单AC)

    题面: 传送门 题目大意: 给定一个空集合,有两种操作: 一种是往集合中插入一个元素x,一种是给三个数x,k,s,问集合中是否存在v,使得gcd(x,v)%k==0,且x+v<=s若存在多个满足 ...

  9. 生产者消费者模型(JoinableQueue)

  10. 引入maven以外的jar包

    这里有2个案例,需要手动发出Maven命令包括一个 jar 到 Maven 的本地资源库. 要使用的 jar 不存在于 Maven 的中心储存库中. 您创建了一个自定义的 jar ,而另一个 Mave ...