上圖:javascript
module:css
import {NgModule} from "@angular/core"; import {CommonModule} from "@angular/common" import {PpInputComponent} from './pp-input' import {FormsModule} from "@angular/forms"; @NgModule({ declarations: [PpInputComponent], imports: [ CommonModule, FormsModule, ], exports: [PpInputComponent], }) export class PpInputComponentModule { }
ts:html
import {Component, Input, Output, EventEmitter, OnInit, ViewChild, ElementRef, Renderer2, AfterViewInit} from '@angular/core'; /** * Generated class for the PpInputComponent component. * * See https://angular.io/api/core/Component for more info on Angular * Components. */ @Component({ selector: 'pp-input', templateUrl: 'pp-input.html' }) export class PpInputComponent implements OnInit, AfterViewInit { constructor(private renderer: Renderer2) { } @ViewChild('ppLabel') private ppLabel: ElementRef; // 獲取元素 @Input() ppValue: any; // input的值,雙向綁定 @Input('pp-label') label: string = 'Label'; // label文案 @Input() validate: any = function (input) { // 驗證數據方法 if (input) { return true } else { return false } }; @Input() type: string = 'text'; // input類型 @Input() x: string; // label的X軸偏移量 @Input() isRequired: boolean; // false @Input('error-message') errorMessage: string = 'validate error'; // 錯誤提示信息 @Output() ppValueChange = new EventEmitter(); actived: boolean = false; // 樣式控制 float: boolean; // label是否浮動 showErrMsg: boolean = false; // 是否顯示錯誤信息 ngOnInit() { if (this.ppValue) { this.float = true; this.actived = true; } else { this.float = false; } } ngAfterViewInit() { if (this.x) { this.renderer.setStyle(this.ppLabel.nativeElement, 'transform', `translate3d(${Number(this.x) / 100}rem, 0.48rem, 0)`) } } // 得到焦點 ppFocus() { this.float = true; } // 失去焦點 ppBlur() { if (this.ppValue) { this.float = true; } else { this.float = false; } if (this.validate(this.ppValue)) { this.showErrMsg = false; } else { this.showErrMsg = true; } } // 更新父組件model值 changeValue() { this.ppValueChange.emit(this.ppValue); if (this.validate(this.ppValue)) { this.actived = true; this.showErrMsg = false; } else { this.actived = false; } } }
scssvue
pp-input { .pp-input-container { border-bottom: 1px solid #C1CCD5; height: 0.92rem; &.actived { border-color: #6308C7 } .label { font-size: 0.28rem; color: #95A1AB; position: relative; font-family: Avenir-Medium; pointer-events: none; transform: translate3d(0,0.48rem, 0); transition: all 0.2s; margin: 0.11rem 0.08rem 0.11rem 0; &.actived { transform: translate3d(0, 0, 0)!important; font-size: 0.22rem; transition: all 0.2s; .actived { color: #6308C7 } } .required { color: #F44E4E } } .pp-input { border: none; font-size: 0.28rem; height: 0.5rem; line-height: 0.5rem; } .content { display: flex; align-items: center; } } .error-message { color: #F44E4E; font-size: 0.22rem; height: 0; line-height: 0.4rem; opacity: 0.5; transition: all 0.2s; overflow: hidden; &.show { opacity: 1; height: 0.4rem; transition: all 0.2s; } } }
htmljava
<!-- Generated template for the PpInputComponent component --> <div class="pp-input-wrapper"> <div class="pp-input-container" [class.actived]="actived"> <div class="label" [class.actived]="float" #ppLabel> <span class="required" *ngIf="isRequired">*</span><span [class.actived]="actived">{{label}}</span> </div> <div class="content"> <ng-content></ng-content> <input class="pp-input" (focus)="ppFocus()" (blur)="ppBlur()" (keyup)="changeValue()" [type]="type" [(ngModel)]="ppValue"> </div> </div> <div class="error-message" [class.show]="showErrMsg">{{errorMessage}}</div> </div>
目前實現可傳入label文案, label的x軸偏移,input類型,驗證數據的validate方法,input的雙向綁定value值,錯誤提示信息等api
用<ng-content></ng-content>預留的編輯位置,能夠添加更多的html,方便擴展,例如上圖的國家圖標顯示。app
能夠考慮把全部的@Input集合成一個config,html的font-size的值我是動態算的,因此樣式rem的值可能要修改爲你須要的大小。flex
ps: 以前用vue組件也寫過類型的組件,傳送門:http://www.javashuo.com/article/p-nifvoiid-dk.htmlui