1. 程式人生 > >【angular5項目積累總結】表單復雜校驗

【angular5項目積累總結】表單復雜校驗

整數 touch strong menu his pointer bin ring export

view

技術分享圖片

code

form.css

:host {
    display: flex;
    width: 100%;
    height:100%;
    border-left:1px solid #ccc;
}
.invalid-box {
    border: 1px solid #a94442;
}

.invalid-error-tip {
    color: #a94442;
}

.select-box {
    width:308px;
    height: 22px;
}
label {
    line-height:20px;
}
.note {
    color
:#bbb; font-size:12px; } .small-input { width: 80%; height: 19px; line-height: 19px; } .small-input :-moz-placeholder, .small-input :-ms-input-placeholder, .small-input ::-webkit-input-placeholder { line-height: 10px; font-size: 10px; } .sm-textBox-wrapper { width: 80%; } .sm-textBox-wrapper [placeholder]
{ text-overflow: ellipsis; font-style: italic; } .sm-textBox-wrapper .azc-input { box-sizing: border-box; font-size: 12px; outline: 0; width: 100%; } .error-input { border-color: #e81123 !important; border-style: solid; border-width: 1px; } .edit-input-wrapper { display
: inline-block; float: left; } .error-icon { height: 19px; width: 10px; line-height: 19px; } .tootip-balloon-w { width: 115px; } .fxc-grid2.fxc-grid-sorting a.fxc-sortable { height:35px; }

form.html

<panel-component [menuItems]="menuItems" headerTitle="創建部署" initWidth="400px" (closeEvent)="onClose()">
    <form style="margin:25px; width: 90%;height: 100%;overflow-x: hidden;" #dpyForm="ngForm">
        <div style="width:90%;padding:5px 3px; font-size:12px;">
            <div style="padding-top:4px;">
                <label>App ID:</label>&nbsp;&nbsp;
                <span>{{currApp.name}}</span>
            </div>
        </div>
        <div style="width:90%;padding:5px 3px; font-size:12px;">
            <div style="padding-top:4px;">
                <label>Package ID:</label>&nbsp;&nbsp;
                <span>{{currPkg.version}}</span>
            </div>
        </div>
        <div style="width:90%;padding:5px 3px; font-size:12px;">
            <div class="azc-required-anchor" style="float:left;padding-top:4px;">
                <svg xmlns="http://www.w3.org/2000/svg" class=" fxs-portal-svg" role="presentation" aria-hidden="true" viewBox="0 0 6 6" focusable="false" xmlns:NS1="" NS1:xmlns:svg="http://www.w3.org/2000/svg">
                    <g>
                        <path class="msportalfx-svg-c22" d="M 3.543 2.352 l 2.08 -0.716 L 6 2.687 l -2.076 0.675 L 5.21 5.158 l -0.942 0.676 l -1.242 -1.867 l -1.264 1.867 l -0.97 -0.676 l 1.305 -1.796 L 0 2.687 L 0.38 1.63 l 2.058 0.743 V 0.233 h 1.105 v 2.119 Z" />
                    </g>
                </svg>
            </div>
            <div style="width:200px;float:left;padding-top:4px;">
                <label>請選擇級別</label>
            </div>
            <div style="width:90%">
                <select (change)="onValid(null)" class="select-box" [(ngModel)]="currDpy.Level" required name="Level" #level="ngModel" [ngClass]="{‘invalid-box‘: (level.invalid && (level.dirty || level.touched))||showLevelError}">
                    <option *ngFor="let level of lstLevel" [value]=‘level.Key‘>{{level.Value}}</option>
                </select>
                <div *ngIf="(level.invalid && (level.dirty || level.touched))||showLevelError" class="invalid-error-tip">
                     級別為必填字段
                </div>
            </div>
        </div>
        <div style="width:90%;padding:5px 3px; font-size:12px;">
            <div class="azc-required-anchor" style="float:left;padding-top:4px;">
                <svg xmlns="http://www.w3.org/2000/svg" class=" fxs-portal-svg" role="presentation" aria-hidden="true" viewBox="0 0 6 6" focusable="false" xmlns:NS1="" NS1:xmlns:svg="http://www.w3.org/2000/svg">
                    <g>
                        <path class="msportalfx-svg-c22" d="M 3.543 2.352 l 2.08 -0.716 L 6 2.687 l -2.076 0.675 L 5.21 5.158 l -0.942 0.676 l -1.242 -1.867 l -1.264 1.867 l -0.97 -0.676 l 1.305 -1.796 L 0 2.687 L 0.38 1.63 l 2.058 0.743 V 0.233 h 1.105 v 2.119 Z" />
                    </g>
                </svg>
            </div>
            <div style="width:200px;float:left;padding-top:4px;">
                <label>端口號配置</label>
            </div>
            <div style="width:100%">
                <!--列表信息-->
                <div class="ext-hubs-browse-grid fxc-base fxs-grid-focus fxc-grid-sorting fxc-grid-scrolling fxc-grid-resizing fxs-grid-selection fxc-grid-contextMenu fxc-grid-grouping fxc-grid2 azc-control fxc-grid-verticalScroll" style="width: 100%;">
                    <div class="fxc-grid-container azc-br-muted">
                        <div class="fxc-grid-tableContainer azc-br-muted" style="padding-top: 42px;">
                            <div class="fxc-grid-tableScrollContainer azc-br-muted">
                                <table class="fxc-grid-tableHeader fxs-grid-multiselection" data-grid-activation="true">
                                    <thead>
                                        <tr>
                                            <th class="fxc-grid-sorting-header fxc-grid-column-header " style="width: 21%;">
                                                <div class="fxc-grid-header-wrapper">
                                                    <a aria-sort="none" class="fxc-sortable fxc-none">
                                                        <span class="fxc-grid-headerlabel">序號</span>
                                                    </a>
                                                    <div class="fxc-grid-resizableColumn-handle">
                                                        <div class="fxc-grid-resizableColumn-handle-line azc-bg-muted">
                                                        </div>
                                                    </div>
                                                </div>
                                            </th>
                                            <th class="fxc-grid-sorting-header fxc-grid-column-header ">
                                                <div class="fxc-grid-header-wrapper">
                                                    <a aria-sort="none" class="fxc-sortable fxc-none">
                                                        <span class="fxc-grid-headerlabel">Docker鏡像</span>
                                                    </a>
                                                    <div class="fxc-grid-resizableColumn-handle">
                                                        <div class="fxc-grid-resizableColumn-handle-line azc-bg-muted">
                                                        </div>
                                                    </div>
                                                </div>
                                            </th>
                                            <th class="fxc-grid-sorting-header fxc-grid-column-header ">
                                                <div class="fxc-grid-header-wrapper">
                                                    <a aria-sort="none" class="fxc-sortable fxc-none">
                                                        <span class="fxc-grid-headerlabel">部署應用</span>
                                                    </a>
                                                    <div class="fxc-grid-resizableColumn-handle">
                                                        <div class="fxc-grid-resizableColumn-handle-line azc-bg-muted">
                                                        </div>
                                                    </div>
                                                </div>
                                            </th>
                                        </tr>
                                    </thead>
                                </table>
                                <div class="fxc-grid-tableContent" style="position: relative; overflow-x: hidden;" >
                                    <table class="fxc-grid-full fxs-grid-multiselection" data-grid-activation="true">
                                        <tbody class="fxc-grid-groupdata ">
                                            <tr class="fxc-grid-row fxs-portal-focus fxs-portal-hover" *ngFor="let port of lstPorts;let i = index">
                                                <td class="fxc-grid-cell azc-br-muted" style="width:10%">
                                                    <span class="fxc-grid-cellContent fxs-ellipsis">
                                                        <span class="msportalfx-gridcolumn-assetsvg-text">{{i}}</span>
                                                    </span>
                                                </td>
                                                <td class="fxc-grid-cell azc-br-muted" style="width:12%;">
                                                    <span class="fxc-grid-cellContent fxs-ellipsis">
                                                        <span class="msportalfx-gridcolumn-assetsvg-text">{{port.docker}}</span>
                                                    </span>
                                                </td>
                                                <td class="fxc-grid-cell azc-br-muted" style="width:20%;">
                                                    <div class="sm-textBox-wrapper" tabindex="-1">
                                                        <div class="edit-input-wrapper">
                                                            <input [(ngModel)]="port.app" (blur)="onValid(i)" (keyup)="onValid(i)" class="azc-input small-input" min="1" pattern="^[1-9]+[0-9]*$" maxlength="5" name="Ports" required type="number" placeholder="輸入端口號" tabindex="0" [ngClass]="{‘error-input‘: !port.valid}">
                                                        </div>
                                                        <div *ngIf="!port.valid" class="fxc-base azc-control azc-dockedballoon azc-dockedballoon-validation azc-bg-default fxs-bg-error error-icon" (mouseenter)="toggleBalloonTip($event,true)" (mouseleave)="toggleBalloonTip($event,false)">
                                                            <div class="azc-dockedballoon-anchor">
                                                                <span>
                                                                    <svg height="100%" width="100%" aria-hidden="true" role="presentation" focusable="false">
                                                                        <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#FxSymbol0-063"></use>
                                                                    </svg>
                                                                </span>
                                                            </div>
                                                        </div>
                                                    </div>
                                                </td>
                                            </tr>
                                        </tbody>
                                    </table>

                                </div>
                            </div>

                        </div>

                    </div>
                    <div [ngClass]="{‘azc-balloon-hidden‘:!isShowBalloon}" class="azc-dockedballoon-balloon azc-dockedballoon-validation azc-bg-default fxc-base azc-control azc-balloon azc-balloon-forcedisplayblock azc-balloon-position-alternate azc-balloon-box-top tootip-balloon-w" [ngStyle]="{‘top.px‘: balloonTop,‘left.px‘: balloonLeft}">
                        <div class="azc-br-muted-80-10 azc-balloon-pointer"></div>
                        <div class="azc-bg-muted-80-10 fxs-text-white azc-balloon-content"><div class="azc-balloon-text">此字段為必填項且長度不超過5位的正整數</div></div>
                    </div>
                </div>

            </div>
        </div>
        <div style="width:90%;padding:5px 3px; font-size:12px;">
            <div class="azc-required-anchor" style="float:left;padding-top:4px;">
                <svg xmlns="http://www.w3.org/2000/svg" class=" fxs-portal-svg" role="presentation" aria-hidden="true" viewBox="0 0 6 6" focusable="false" xmlns:NS1="" NS1:xmlns:svg="http://www.w3.org/2000/svg">
                    <g>
                        <path class="msportalfx-svg-c22" d="M 3.543 2.352 l 2.08 -0.716 L 6 2.687 l -2.076 0.675 L 5.21 5.158 l -0.942 0.676 l -1.242 -1.867 l -1.264 1.867 l -0.97 -0.676 l 1.305 -1.796 L 0 2.687 L 0.38 1.63 l 2.058 0.743 V 0.233 h 1.105 v 2.119 Z" />
                    </g>
                </svg>
            </div>
            <div style="width:200px;float:left;padding-top:4px;">
                <label>實例數</label>
            </div>
            <div style="width:90%">
                <input type="number" min="1" pattern="^[1-9]+[0-9]*$" maxlength="5" (keyup)="onValid(null)" class="azc-input" style="width:305px;" [(ngModel)]="currDpy.InstanceCount" name="InstanceCount" [ngClass]="{‘invalid-box‘: (instanceCount.invalid && (instanceCount.dirty || instanceCount.touched))||showInsCountError}" required  #instanceCount="ngModel" />
                <div *ngIf="(instanceCount.invalid && (instanceCount.dirty || instanceCount.touched))|| showInsCountError" class="invalid-error-tip">
                    實例數為必填字段且為有效數字
                </div>
            </div>
        </div>
        <div style="width:90%;padding:5px 3px; font-size:12px;">
            <div style="width:100%;float:left;padding-top:4px;">
                <label>描述</label>
                <span class="note">(註:多個描述項之間請用英文分號“;”分隔)</span>
            </div>
            <div style="width:90%">
                <textarea class="azc-input" style="width:305px;height:100px" [(ngModel)]="currDpy.Description" name="Description"></textarea>
            </div>
        </div>
    </form>
</panel-component>
<router-outlet></router-outlet>

form.ts

import { Component, ViewChild} from ‘@angular/core‘;
import { Router, ActivatedRoute, Params } from ‘@angular/router‘;
import { AppStoreService } from ‘../service/appStoreService‘;
import { CommonService } from ‘../../providers/commonService‘;

@Component({
    selector: ‘deploy-page‘,
    templateUrl: ‘./deploy.html‘,
    styleUrls: [‘./deploy.css‘]
})

export class DeployPage {

    @ViewChild(‘dpyForm‘) dpyForm;
    constructor(
        private router: Router,
        private actRouter: ActivatedRoute,
        private appStoreService: AppStoreService,
        private comService: CommonService) {
    }
    
    currDpy: any = {
        AppId: "",
        PackageId:"",
        Level:"",
        Description: "",
        InstanceCount: "",
        Ports:""
    };
    id: string;
    pkgId: any;
    currApp: any = {
        id: ‘‘,
        name:‘‘
    };
    currPkg: any = {
        id: ‘‘,
        version:‘‘
    };
    lstPorts: any = [];
    lstLevel: {} = [
        {
            "Key": 0,
            "Value": "高"
        },
        {
            "Key": 1,
            "Value": "中"
        },
        {
            "Key": 2,
            "Value": "低"
        }
    ];
    showLevelError: boolean;
    showInsCountError: boolean;
    isShowBalloon: boolean = false;
    balloonTop: any;
    balloonLeft: any;
    parentUrl: string;
    menuItems: any = [
        { title: "提交", icon: "#FxSymbol0-001", event: this.onSaveDpyInfo.bind(this) }
    ]
    ngOnInit(): void {
        this.actRouter.params.subscribe((params: Params) => {
            this.id = params["id"];
            this.pkgId = params["pkgId"];
        });
        this.appStoreService.GetPkgOne(this.pkgId, (rtv) => {
            this.currPkg = rtv;
            rtv.ports.split(‘,‘).forEach(p => {
                this.lstPorts.push({ ‘docker‘:p,‘app‘:‘‘,‘valid‘:true});
            });
        });
        this.appStoreService.GetAppOne(this.id, (rtv) => {
            this.currApp = rtv;
        });
        this.parentUrl = "/webAppStore/" + this.id + "/version";
    }
    onClose() {
        this.router.navigate([this.parentUrl, { id: this.id, pkgId: this.pkgId }]);
    }
    onValid(index: any) {
        this.showLevelError = this.currDpy.Level ? false : true;
        if (this.currDpy.InstanceCount && /^[1-9][0-9]{0,4}$/.test(this.currDpy.InstanceCount)) {
            this.showInsCountError = false;
        } else {
            this.showInsCountError = true;
        }
        if (index && this.lstPorts[index]) {
            this.validPort(this.lstPorts[index])
        } else {
            this.lstPorts.map(p => this.validPort(p))
        }
    }
    validPort(port: any) {
        port.app && /^[1-9][0-9]{0,4}$/.test(port.app) ? port.valid = true : port.valid = false;
    }
    onSaveDpyInfo() {
        this.onValid(null);
        let emptyItem = this.lstPorts.find(item => { return !item.app || item.valid == false });
        if (this.dpyForm.form.valid && !emptyItem && this.showLevelError == false && this.showInsCountError == false) {
            this.currDpy.AppId = this.currApp.id;
            this.currDpy.PackageId = this.currPkg.id;
            this.lstPorts.forEach(p => delete p.valid);
            this.currDpy.Ports = JSON.stringify(this.lstPorts);
            this.appStoreService.SaveAppDpyInfo(this.currDpy, () => { 
                var notifyBody = { action: ‘refreshWebDpy‘, pkgId: this.pkgId};
                this.comService.notifyOther(notifyBody);
                this.router.navigate([this.parentUrl, { id: this.id, pkgId: this.pkgId }]);
            });
        }
    } 
    toggleBalloonTip(event: any, isShow: boolean) {
        this.isShowBalloon = isShow;
        if (event) {
            this.balloonLeft = event.pageX - 110;
            this.balloonTop = event.pageY - 100;
            event.stopPropagation();
        }
    }
}

【angular5項目積累總結】表單復雜校驗