具有ng-content的父组件中的模板驱动表单验证

时间:2017-03-20 07:41:00

标签: validation angular2-forms

我一直在寻找过去几天的答案,但我找不到解决方案。

我有一个基页组件,所有其他页面都扩展了它,因为涉及到大量的crud页面。在我的场景中,我创建了一个模态和ng-content选择器,以便在需要时填充输入。

代码:

基页list.component.html

    <section class="widget animated fadeIn" widget>
    <header>
        <h4 class="page-title" *ngIf="pageType !== PageType.TAB">
            <ng-content select=".page-title"></ng-content>
        </h4>
        <div class="widget-controls">
            <a data-widgster="expand" title="Expand" href="#"><i class="glyphicon glyphicon-chevron-up"></i></a>
            <a data-widgster="collapse" title="Collapse" href="#"><i class="glyphicon glyphicon-chevron-down"></i></a>
        </div>
    </header>

    <div class="widget-body" [ngSwitch]='status'>
        <div class="mt-0 clearfix">
            <alert *ngFor="let toast of alertService.toasts; let i = index" [type]="toast.type + ' alert-sm'" (close)="alertService.closeToast(i)" dismissible="true" dismissOnTimeout="5000">
                <div [innerHTML]="toast.msg"></div>
            </alert>

            <!-- Spinner -->
            <div class="sk-fading-circle" *ngSwitchCase="status === PageStatus.LOADING || status === PageStatus.EASY_LOADING ? status : -1">
                <div class="sk-circle1 sk-circle"></div>
                <div class="sk-circle2 sk-circle"></div>
                <div class="sk-circle3 sk-circle"></div>
                <div class="sk-circle4 sk-circle"></div>
                <div class="sk-circle5 sk-circle"></div>
                <div class="sk-circle6 sk-circle"></div>
                <div class="sk-circle7 sk-circle"></div>
                <div class="sk-circle8 sk-circle"></div>
                <div class="sk-circle9 sk-circle"></div>
                <div class="sk-circle10 sk-circle"></div>
                <div class="sk-circle11 sk-circle"></div>
                <div class="sk-circle12 sk-circle"></div>
            </div>
            <!-- Spinner End -->

            <!--Tables-->
            <div *ngSwitchCase="status == PageStatus.DONE || status === PageStatus.EASY_LOADING ? status : -1">
                <div class="form-control-static">

                    <div class="form-group pull-left">
                        <ng-content select="custom-filter-content"></ng-content>
                    </div>

                    <!-- Search Box -->
                    <div class="form-group pull-right">
                        <div class="input-group">
                            <span class="input-group-addon"><i class="fa fa-search"></i></span>
                            <input type="text" class="form-control" [(ngModel)]="filter.searchTerm"
                                   [disabled]="false"
                                   (keyup.enter)="runQuery()"
                                   placeholder="{{'global.label.search' | translate}}"/>
                            <div class="input-group-btn">
                                <button type="button" class="btn btn-default" (click)="reset()">
                                    <i class="fa fa-refresh text-primary"></i>
                                </button>
                            </div>
                        </div>
                    </div>
                    <!-- Search Box End -->

                    <div *ngIf="isFiltered() && isSearchResultEmpty()">
                        <label>{{'global.label.nodata' | translate}}</label>
                        <ng-content select="table-no-data"></ng-content>
                    </div>
                </div>

                <div *ngIf="isFiltered() && !isSearchResultEmpty()" class="form-control-static">
                    <ng-content select="table-content"></ng-content>
                </div>
            </div>
            <!-- Tables End -->
        </div>

        <!-- Table Buttons -->
        <div class="form-actions">
            <button class="btn btn-inverse" (click)="showModal(ModalState.CREATE)">
                <i class="glyphicon glyphicon-plus-sign text-warning"></i>
                {{'global.label.addNew' | translate}}
            </button>
            <ng-content select="table-buttons"></ng-content>
        </div>
        <!-- Table Buttons End -->
    </div>
</section>

<!-- Modal -->
<div bsModal #modal="bs-modal" data-backdrop="true" data-keyboard="false" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true">
    <div class="modal-dialog modal-lg">
        <div class="modal-content">
            <div class="modal-header">
                <button (click)="modal.hide()" aria-label="Close" class="close" role="button">
                    <span aria-hidden="true">×</span>
                </button>
                <div [ngSwitch]="modalState">
                    <h4 class="modal-title" *ngSwitchCase="ModalState.CREATE">{{'global.label.addNew' | translate}}</h4>
                    <h4 class="modal-title" *ngSwitchCase="ModalState.UPDATE">{{'global.label.editRecord' | translate}}</h4>
                </div>
            </div>

            <form #recordForm="ngForm" enctype="multipart/form-data">
                <div class="modal-body">
                    <!-- Spinner -->
                    <div class="sk-fading-circle" *ngIf="status === PageStatus.LOADING || status === PageStatus.EASY_LOADING">
                        <div class="sk-circle1 sk-circle"></div>
                        <div class="sk-circle2 sk-circle"></div>
                        <div class="sk-circle3 sk-circle"></div>
                        <div class="sk-circle4 sk-circle"></div>
                        <div class="sk-circle5 sk-circle"></div>
                        <div class="sk-circle6 sk-circle"></div>
                        <div class="sk-circle7 sk-circle"></div>
                        <div class="sk-circle8 sk-circle"></div>
                        <div class="sk-circle9 sk-circle"></div>
                        <div class="sk-circle10 sk-circle"></div>
                        <div class="sk-circle11 sk-circle"></div>
                        <div class="sk-circle12 sk-circle"></div>
                    </div>
                    <!-- Spinner End -->

                    <ng-content select="modal-body-content"></ng-content>

                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-gray" (click)="modal.hide()">
                        {{'global.button.close' | translate}}
                    </button>
                    <span [ngSwitch]="modalState">
                        <button *ngSwitchCase="ModalState.CREATE" role="button" [disabled]="status === PageStatus.LOADING || !recordForm.form.valid" class="btn btn-success" (click)="onAddItem()">
                            <i class="fa fa-spinner fa-spin" [hidden]="status !== PageStatus.LOADING"></i>
                            {{'global.button.save' | translate}}
                        </button>
                        <button *ngSwitchCase="ModalState.UPDATE" role="button" [disabled]="status === PageStatus.LOADING || !recordForm.form.valid" class="btn btn-info" (click)="onUpdateItem()">
                            <i class="fa fa-spinner fa-spin" [hidden]="status !== PageStatus.LOADING"></i>
                            {{'global.button.update' | translate}}
                        </button>
                    </span>
                </div>
            </form>

        </div>
    </div>
</div>

基页list.component.ts

import {PageEvent} from "angular2-datatable-serverpagination";
import {
    OnInit, Component, Input, ViewEncapsulation, ViewChild, Injectable
} from "@angular/core";
import {BasePageService} from "../../service/base-page.service";
import {PaginationResult} from "../../model/pagination-result";
import {BasePageFilter} from "../../model/base-page-filter";
import {BasePageResultItem} from "../../model/base-page-result-item";
import {ModalDirective} from "ng2-bootstrap";
import {AlertService} from "../../../../utils/alert.service";
import * as Models from '../../model/models'
import {NgForm} from "@angular/forms";

export enum PageType {
    DEFAULT,
    TAB
}

export enum PageStatus {
    DONE,
    LOADING,
    EASY_LOADING,
    ERROR
}

export enum ModalState {
    CREATE,
    UPDATE,
    DELETE
}

@Component({
    selector: 'app-base-page-list',
    templateUrl: 'base-page-list.component.html',
    styleUrls: ['../../elements.style.scss'],
    encapsulation: ViewEncapsulation.None
})
export class BasePageListComponent<F extends BasePageFilter, I extends BasePageResultItem, S extends BasePageService<F, I>> implements OnInit {

    @ViewChild("modal")
    modal: ModalDirective;

    @Input("recordForm")
    recordForm: NgForm;

    @Input("formErrors")
    formErrors: any;

    @Input("validationMessages")
    validationMessages: any;

    constructor(public alertService: AlertService) {
    }

    ngOnInit() {
        if (!this.formErrors) {
            this.formErrors = {};

            if (this.validationMessages) {
                for (let key of Object.keys(this.validationMessages)) {
                    this.formErrors[key] = '';
                }
            }
        }
    }

    showModal(state: ModalState, item?) {
        switch (state) {
            case ModalState.CREATE:
                //todo
                break;

            case ModalState.UPDATE:
                //todo
                break;

            case ModalState.DELETE:
                break;

            default:
                state = ModalState.CREATE;
                break;
        }

        this.modalState = state;
        this.modal.show();
    }

}

这就是我从其他页面输入我的输入的例子,例如事件列表 的事件list.component.html

<app-base-page-list  #basepage [parent]="this" [instanceClassName]="'EventModel'" [validationMessages]="formValidator">
    <div class="page-title">
        {{ 'event.list.title' | translate }}
    </div>

    <app-base-page-filter-field
            [filter]="basepage.filter"
            [labelMessageKey]="'global.label.name'"
            [fieldName]="'_id'"
            [fieldType]="'text'">
    </app-base-page-filter-field>

    <table-no-data></table-no-data>

    <table-content>
        <table [ngSwitch]='basepage.filter.orderByDirection' class="table table-striped table-hover"
               [mfData]="basepage.items" #mf="mfDataTable"
               (mfOnPageChange)="basepage.onPageChange($event)"
               [mfAmountOfRows]="basepage.count"
               [mfActivePage]="basepage.pageNumber"
               [mfRowsOnPage]="basepage.pageSize">
            <thead>
            <tr>
                <th class="hidden-sm-down">
                    <app-base-page-sort-field [filter]="basepage.filter"
                                              [sortChangeListener]="basepage"
                                              [labelMessageKey]="'global.label.name'"
                                              [sortProperty]="'name'">
                    </app-base-page-sort-field>
                </th>
                <th class="hidden-sm-down">
                    <app-base-page-sort-field [filter]="basepage.filter"
                                              [sortChangeListener]="basepage"
                                              [labelMessageKey]="'global.label.startDate'"
                                              [sortProperty]="'startDate'">
                    </app-base-page-sort-field>
                </th>
                <th class="hidden-sm-down">
                    <app-base-page-sort-field [filter]="basepage.filter"
                                              [sortChangeListener]="basepage"
                                              [labelMessageKey]="'global.label.endDate'"
                                              [sortProperty]="'endDate'">
                    </app-base-page-sort-field>
                </th>
                <th class="hidden-sm-down">
                    <app-base-page-sort-field [filter]="basepage.filter"
                                              [sortChangeListener]="basepage"
                                              [labelMessageKey]="'global.label.description'"
                                              [sortProperty]="'description'">
                    </app-base-page-sort-field>
                </th>
                <th class="hidden-sm-down">
                    <app-base-page-sort-field [filter]="basepage.filter"
                                              [sortChangeListener]="basepage"
                                              [labelMessageKey]="'global.label.createdDate'"
                                              [sortProperty]="'createdAt'">
                    </app-base-page-sort-field>
                </th>
                <th>
                    {{'global.label.actions' | translate}}
                </th>
            </tr>
            </thead>
            <tbody>
            <tr *ngFor="let item of mf.data"
                [ngClass]="{'hovered': hoveringItem == item}"
                (mouseover)="hoveringItem = item" (mouseleave)="hoveringItem = null">
                <td>{{item.name}}</td>
                <td>{{item.startDate | date: 'dd.MM.yyyy HH:mm'}}</td>
                <td>{{item.endDate | date: 'dd.MM.yyyy HH:mm'}}</td>
                <td>{{item.description}}</td>
                <td>{{item.createdAt | date: 'dd.MM.yyyy HH:mm'}}</td>
                <td>
                    <app-base-page-action-field
                            [item]="item"
                            [holder]="this">
                    </app-base-page-action-field>
                </td>
            </tr>
            </tbody>
            <tfoot>
            <tr>
                <td colspan="12">
                    <mfBootstrapPaginator [rowsOnPageSet]="[5, 10, 15, 25]"></mfBootstrapPaginator>
                </td>
            </tr>
            </tfoot>
        </table>
    </table-content>

    <table-buttons></table-buttons>

    <!-- Modal -->
    <modal-body-content>
        <div class="row">
            <div class="col-lg-12">
                <app-base-page-input-field [labelMessageKey]="'organization.label'"
                                           [holder]="this"
                                           [fieldName]="'organization'"
                                           [fieldType]="'select'"
                                           [ajaxUrl]="'organization/list'"
                                           [item]="instance"
                                           [required]="true"
                                           [columnClass]="'col-md-3'">
                </app-base-page-input-field>
            </div>
        </div>
        <div class="row">
            <div class="col-lg-12">
                <app-base-page-input-field [labelMessageKey]="'global.label.name'"
                                           [holder]="this"
                                           [fieldName]="'name'"
                                           [fieldType]="'text'"
                                           [item]="instance"
                                           [required]="true"
                                           [validationCheckProperty]="'name'"
                                           [columnClass]="'col-md-3'">
                </app-base-page-input-field>
            </div>
        </div>
        <div class="row">
            <div class="col-lg-12">
                <app-base-page-input-field [labelMessageKey]="'global.label.description'"
                                           [holder]="this"
                                           [fieldName]="'description'"
                                           [fieldType]="'textarea'"
                                           [item]="instance"
                                           [columnClass]="'col-md-3'">
                </app-base-page-input-field>
            </div>
        </div>

        <div class="row">
            <div class="col-lg-12">
                <app-base-page-input-field [labelMessageKey]="'global.label.startDate'"
                                           [holder]="this"
                                           [fieldName]="'startDate'"
                                           [fieldType]="'datetime'"
                                           [item]="instance"
                                           [required]="true"
                                           [columnClass]="'col-md-3'">
                </app-base-page-input-field>
            </div>
        </div>

        <div class="row">
            <div class="col-lg-12">
                <app-base-page-input-field [labelMessageKey]="'global.label.endDate'"
                                           [holder]="this"
                                           [fieldName]="'endDate'"
                                           [fieldType]="'datetime'"
                                           [item]="instance"
                                           [required]="true"
                                           [columnClass]="'col-md-3'">
                </app-base-page-input-field>
            </div>
        </div>
    </modal-body-content>

</app-base-page-list>

Everthing呈现完美,但在表单验证方面。它始终启动ng-valid状态。 'app-base-page-input-field'组件创建输入字段。 这是:

<div [ngSwitch]="fieldType" [ngClass]="{'form-group' : true}">

    <label *ngIf="labelMessageKey && (fieldType !== 'singleImage' || fieldType !== 'dynamicRow')" class="col-form-label" [for]="fieldName">
        <strong>{{ labelMessageKey | translate }}</strong>
    </label>
    <p *ngSwitchCase="'static'" class="form-control-static">{{item[fieldName]}}</p>

    <input *ngSwitchCase="'text'" [id]="fieldName"  class="form-control" type="text"
           [(ngModel)]="item[fieldName]"
           placeholder="{{ (placeholder || labelMessageKey) | translate }}"
           [name]="fieldName"
           [attr.required]="required ? 'required' : null">
</div>

任何帮助都会很高兴。谢谢。

0 个答案:

没有答案