關於angular自義定radio組合實現學習總結

自己用了ng-zorro的組件去寫。
在這裏插入圖片描述

<!------------------------------------------------------------------------------------------------------------------>
              <nz-radio-group *ngSwitchCase="'dateChoice'" [(ngModel)]="tdItem.value" name="customDate"
                (ngModelChange)="customTime($event)">
                <label nz-radio nzValue="day">本日</label>
                <label nz-radio nzValue="week">本週</label>
                <label nz-radio nzValue="month">本月</label>
                <label nz-radio nzValue="timeByMySelf">自定義</label>
                <ng-container *ngIf="showCustomTime">
                  <nz-date-picker [nzDisabledDate]="disabledStartDate" nzFormat="yyyy-MM-dd" [(ngModel)]="startValue"
                    nzPlaceHolder="開始時間" (ngModelChange)="onStartChange($event)">
                  </nz-date-picker>~
                  <nz-date-picker [nzDisabledDate]="disabledEndDate" nzFormat="yyyy-MM-dd" [(ngModel)]="endValue"
                    nzPlaceHolder="結束時間" (ngModelChange)="onEndChange($event)">
                  </nz-date-picker>
                </ng-container>
              </nz-radio-group>
              <!------------------------------------------------------------------------------------------------------------------->
// -------------------------自定義時間查詢zjy-----------------------------------------------------------------------------------
  //是否展示自定義時間框
  showCustomTime: boolean = false;
  //獲得日期範圍的工具類
  utilDate: UtilDate = new UtilDate();
  //自定義的日期開始||結束值
  startValue: Date | null = null;
  endValue: Date | null = null;
  customTime($event) {
    if ($event == 'timeByMySelf') {
      this.showCustomTime = true;
      this.searchAll.startPublishDate = '';
      this.searchAll.endPublishDate = '';
      // this.postsearch.emit(this.searchAll);
    } else if ($event == 'day') {
      this.getDayDate()
      this.showCustomTime = false;
      this.startValue = null;
      this.endValue = null;
    } else if ($event == 'week') {
      this.getWeekDate()
      this.startValue = null;
      this.endValue = null;
      this.showCustomTime = false;
    } else if ($event == 'month') {
      this.getMonthDate()
      this.startValue = null;
      this.endValue = null;
      this.showCustomTime = false;
    }
  }
  //查詢本日的產品列表,通過獲取本日的時間範圍
  getDayDate() {
    let search: TableSearch = new TableSearch();
    search.startPublishDate = this.utilDate.getDayAndWeek('day').begin;
    search.endPublishDate = search.startPublishDate;
    this.searchAll.startPublishDate = search.startPublishDate;
    this.searchAll.endPublishDate = search.endPublishDate;
    this.postsearch.emit(search);
    console.log('day', search);
  }
  //查詢本週的產品列表,通過獲取本日的時間範圍
  getWeekDate() {
    let search: TableSearch = new TableSearch();
    search.startPublishDate = this.utilDate.getDayAndWeek('week').begin;
    search.endPublishDate = this.utilDate.getDayAndWeek('week').over;
    this.searchAll.startPublishDate = search.startPublishDate;
    this.searchAll.endPublishDate = search.endPublishDate;
    this.postsearch.emit(search);
    console.log('week', search);
  }
  //查詢本月的產品列表,通過獲取本日的時間範圍
  getMonthDate() {
    let search: TableSearch = new TableSearch();
    search.startPublishDate = this.utilDate.getMonth().begin;
    search.endPublishDate = this.utilDate.getMonth().over;
    this.searchAll.startPublishDate = search.startPublishDate;
    this.searchAll.endPublishDate = search.endPublishDate;
    this.postsearch.emit(search);
    console.log('month', search);
  }
  disabledStartDate = (startValue: Date): boolean => {
    if (!startValue || !this.endValue) {
      return false;
    }
    return startValue.getTime() > this.endValue.getTime();
  };
  disabledEndDate = (endValue: Date): boolean => {
    if (!endValue || !this.startValue) {
      return false;
    }
    return endValue.getTime() <= this.startValue.getTime();
  };
  onStartChange(date: Date): void {
    this.startValue = date;
    if (this.endValue == null && date != null) {
      this.endValue = null;
      this.selfTimeChange()
    }
    else if (this.startValue == null && this.endValue == null) {
      this.searchAll.startPublishDate = '';
      this.searchAll.endPublishDate = '';
      this.postsearch.emit(this.searchAll);
    } else if (this.startValue == null) {
      this.searchAll.startPublishDate = '';
      // this.endValue = null;
      this.postsearch.emit(this.searchAll);
    } else if (this.startValue && this.endValue) {
      this.selfTimeChange()
    }
  }
  onEndChange(date: Date): void {
    this.endValue = date;
    if (this.startValue == null && date != null) {
      this.startValue = null;
      this.selfTimeChange()
    }
    else if (this.startValue == null && this.endValue == null) {
      this.searchAll.startPublishDate = '';
      this.searchAll.endPublishDate = '';
      this.postsearch.emit(this.searchAll);
    } else if (this.endValue == null) {
      this.searchAll.endPublishDate = '';
      // this.startValue = null
      this.postsearch.emit(this.searchAll);
    } else if (this.startValue && this.endValue) {
      this.selfTimeChange()
    }
  }
  /**
   * 查詢自定義時間範圍的產品列表數據
   */
  selfTimeChange() {
    let search: TableSearch = new TableSearch();
    if (this.startValue) {
      let arr = (this.startValue.toLocaleDateString() + '').split('/')
      search.startPublishDate = arr[0] + '-' + arr[1] + '-' + arr[2];
    }
    if (this.endValue) {
      let arr = (this.endValue.toLocaleDateString() + '').split('/')
      search.endPublishDate = arr[0] + '-' + arr[1] + '-' + arr[2];
    }
    if (this.endValue == null) {
      search.endPublishDate = ''
    }
    if (this.startValue == null) {
      search.startPublishDate = ''
    }
    this.searchAll.startPublishDate = search.startPublishDate;
    this.searchAll.endPublishDate = search.endPublishDate;
    this.postsearch.emit(search);
    console.log('timeByself', search);
  }
  // -----------------------------------------------------------------------------------------------------------------------------

在這裏插入圖片描述

export class UtilDate {
    getCurrentDate() {
        let date = new Date();
        let year = date.getFullYear(); //獲取年
        let month = date.getMonth() + 1; //獲取月
        let day = date.getDate(); //獲取日
        let weekDay = date.getDay(); // 星期
        let timesStamp = date.getTime(); //getTime() 方法可返回距 1970 年 1 月 1 日之間的毫秒數。
        return {
            year,
            month,
            day,
            weekDay,
            //沒用到
            timesStamp
        }
    }
    /**
     * 
     * @param date 傳入DayAndWeek(string)和getMonth(object) 
     * @param separator 可以自定義所匹配的格式 eg:// YYYY-MM-DD 2019-01-10
     */
    myformatStr(date, separator?) {
        let year;
        let month;
        let day;
        if (typeof date == 'string') {
            year = date.split('/')[0];
            month = date.split('/')[1];
            day = date.split('/')[2];
        } else if (typeof date == 'object') {
            year = date.year;
            month = date.month;
            day = date.day;
        }
        let mStr = month < 10 ? '0' + month : month + '';
        let dStr = day < 10 ? '0' + day : day + '';
        if (separator) {
            return year + separator + mStr + separator + dStr;
        }
        return year + '-' + mStr + '-' + dStr;
    }
    getDayAndWeek(type){
        let date = new Date();
        let pre = 0;
        let next = 0;
        let startTime = '';
        let endTime = '';
        let begin;
        let over;
        switch (type) {
            case 'day':
                next++;
                break;
            case 'week':
                pre = 1 - date.getDay();
                next = 7 - date.getDay() ;
                break;
            default:
        }
        startTime = new Date(date.getTime() + 24 * 60 * 60 * 1000 * pre).toLocaleDateString();
        endTime = new Date(date.getTime() + 24 * 60 * 60 * 1000 * next).toLocaleDateString();
        begin = this.myformatStr(startTime);
        over = this.myformatStr(endTime);
        return { begin, over };
    }
    /**
     * 獲取本月日期範圍
     */
    getMonth() {
        let date = this.getCurrentDate();
        let monthStart = {};
        let monthEnd = {};
        let begin;
        let over;
        if (date.month == 12) {
            monthEnd = { year: date.year + 1, month: date.month + 1, day: 1 };
        } else {
            monthEnd = { year: date.year, month: date.month + 1, day: 1 };
        }
        monthStart = { year: date.year, month: date.month, day: 1 };
        begin = this.myformatStr(monthStart)
        over = this.myformatStr(monthEnd)
        return { begin, over }
    }
    getTimeByMyself(time, id: number) {
        let date = this.getCurrentDate();
        let selfTimeStart = {};
        let selfTimeEnd = {};
        let begin;
        let over;
        let endDay;
        let condition;
        let cond;
        date.year = time.split('/')[0];
        date.month = time.split('/')[1];
        date.day = time.split('/')[2];
        if (id == 2) {
            endDay = Number(date.day) + 1
        }
        selfTimeStart = { year: date.year, month: date.month, day: date.day };
        selfTimeEnd = { year: date.year, month: date.month, day: endDay};
        condition =  { year: date.year, month: date.month, day: endDay - 1};
        begin = this.myformatStr(selfTimeStart)
        over = this.myformatStr(selfTimeEnd)
        cond = this.myformatStr(condition)
        return { begin, over,cond }
    }
}

在這裏插入圖片描述
橋豆麻袋。。。。這個插件還不對勁,根據要求是要再次點擊radio就要清空查詢條件的。
So不用能ng-zorro的組件了。大佬寫了自定義的組件。(組件是大佬寫的=。。=)

在這裏插入圖片描述
自定義插件如下:
在這裏插入圖片描述

<span class="sl-radio">
  <input #inputElement type="radio" class="sl-radio-input" [attr.name]="name" [value]>
  <i class="sl-radio-inner app-icon" [class.radio-blank]="!isSelected" [class.radio-selected]="isSelected"
    [class.radio-selected-disabled]="isDisabled&&isSelected" [class.radio-disabled]="isDisabled&&!isSelected"></i>
</span>
<span class="sl-radio-content">
  <ng-content></ng-content>
</span>

ts

import { Component, OnInit, ElementRef, Renderer2, Input, ViewChild, HostListener, Output, EventEmitter, ChangeDetectorRef, forwardRef } from '@angular/core';
import { Subject } from 'rxjs';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';

@Component({
  selector: '[sl-radio]',
  templateUrl: './radio-tpl.component.html',
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => RadioTplComponent),
      multi: true
    }
  ],
})
export class RadioTplComponent implements OnInit, ControlValueAccessor {
  name: string;
   isMultiple: boolean; // 是否支持多選
  @Input() isCanCancel: boolean;
  @Input() isSelected: boolean;
  @Input() isDisabled: boolean;
  @Input() slValue: any;
  onChange: (_: boolean) => void = () => null;
  onTouched: () => void = () => null;
  @ViewChild('inputElement', { static: false }) inputElement: ElementRef;
  select$ = new Subject<RadioTplComponent>();
  touched$ = new Subject<void>();
  constructor(
    private cdr: ChangeDetectorRef,
    private elementRef: ElementRef,
    private renderer: Renderer2,
  ) {
    this.renderer.addClass(elementRef.nativeElement, 'sl-radio-wrapper');
  }
  ngOnInit() {
  }
  @HostListener('click', ['$event'])
  onClick(event: MouseEvent): void {
    event.stopPropagation();
    event.preventDefault();
    if (!this.isDisabled && !this.isSelected) {
      this.select$.next(this);
      this.isSelected = true;
      this.onChange(true);
    } else if (!this.isDisabled && this.isSelected && this.isCanCancel) {
      this.select$.next(this);
      this.isSelected = false;
      this.onChange(false);
    }
  }

  markForCheck(): void {
    this.cdr.markForCheck();
  }
  writeValue(value: any): void {
    this.isSelected = value;
    this.cdr.markForCheck();
  }
  registerOnChange(fn: any): void {
    this.onChange = fn;
  }
  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }
  setDisabledState?(isDisabled: boolean): void {
    this.isDisabled = isDisabled;
    this.cdr.markForCheck();
  }
}

在這裏插入圖片描述
通過 ElementRef 我們就可以封裝不同平臺下視圖層中的 native 元素 (在瀏覽器環境中,native 元素通常是指 DOM 元素),最後藉助於 Angular 提供的強大的依賴注入特性,我們就可以輕鬆地訪問到 native 元素。
在這裏插入圖片描述

在這裏插入圖片描述
關於ControlValueAssessor的認識
在這裏插入圖片描述

import { Component, OnInit, Renderer2, ElementRef, ChangeDetectorRef, ContentChildren, QueryList, forwardRef, Input, AfterContentInit, OnDestroy, OnChanges, SimpleChanges } from '@angular/core';
import { RadioTplComponent } from './radio-tpl.component';
import { isNotNil, isString } from '../../utils/convert';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { startWith, takeUntil } from 'rxjs/operators';
import { Subject, Subscription, merge } from 'rxjs';

@Component({
  selector: 'sl-radio-group',
  templateUrl: './radio-group-tpl.component.html',
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => RadioGroupTplComponent),
      multi: true
    }
  ],
})
export class RadioGroupTplComponent implements AfterContentInit, ControlValueAccessor, OnDestroy, OnChanges {
  @Input() isCanCancel: boolean = false;
  @Input() isDisabled: boolean;
  @Input() slName: string;
  @Input() isMultiple: boolean; // 是否支持多選
  @ContentChildren(forwardRef(() => RadioTplComponent), { descendants: true }) radios: QueryList<RadioTplComponent>;
  onChange: (_: string) => void = () => null;
  onTouched: () => void = () => null;
  private value: any;
  private destroy$ = new Subject();
  private selectSubscription: Subscription;
  private touchedSubscription: Subscription;
  constructor(private cdr: ChangeDetectorRef, renderer: Renderer2, elementRef: ElementRef) {
    renderer.addClass(elementRef.nativeElement, 'sl-radio-group-wrapper');
  }
  updateChildrenStatus(): void {
    if (this.radios) {
      Promise.resolve().then(() => {
        this.radios.forEach(radio => {
          radio.isSelected = radio.slValue == this.value;
          radio.isCanCancel = this.isCanCancel;
          radio.isMultiple = this.isMultiple;
          if (isNotNil(this.isDisabled)) {
            radio.isDisabled = this.isDisabled;
          }
          if (this.slName) {
            radio.name = this.slName;
          }
          radio.markForCheck();
        })

      })
    }
  }

  ngOnChanges(changes: SimpleChanges): void {
    if (changes.isDisabled || changes.slName) {
      this.updateChildrenStatus();
    }
  }
  ngOnDestroy(): void {
    this.destroy$.next();
    this.destroy$.complete();
  }
  writeValue(value: any): void {
    this.value = value;
    this.updateChildrenStatus();
    this.cdr.markForCheck();
  }
  registerOnChange(fn: any): void {
    this.onChange = fn;
  }
  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }
  setDisabledState?(isDisabled: boolean): void {
    this.isDisabled = isDisabled;
    this.cdr.markForCheck();
  }
  ngAfterContentInit(): void {
    this.radios.changes
      .pipe(
        startWith(null),
        takeUntil(this.destroy$)
      ).subscribe(() => {
        this.updateChildrenStatus();
        if (this.selectSubscription) {
          this.selectSubscription.unsubscribe();
        }
        this.selectSubscription = merge(...this.radios.map(radio => radio.select$))
          .pipe(takeUntil(this.destroy$))
          .subscribe(radio => {
            if (this.isMultiple) { // 支持多選
              let vals = this.value && isString(this.value) ? this.value.split(',') : [];
              let index = vals.findIndex(ele => ele == radio.slValue);
              if (index != -1) {
                vals.splice(index, 1);
              } else {
                vals.push(radio.slValue);
              }
              let val = '';
              vals.forEach(ele => {
                val += ele + ',';
              })
              this.value = val ? val.substring(0, val.length - 1) : val;
              this.onChange(vals);
            } else {
              if (this.value !== radio.slValue) {
                this.value = radio.slValue;
                this.updateChildrenStatus();
                this.onChange(this.value);
              } else {
                if (this.isCanCancel) {
                  this.value = null;
                  this.updateChildrenStatus();
                  this.onChange(this.value);
                }
              }
            }
          });
        if (this.touchedSubscription) {
          this.touchedSubscription.unsubscribe();
        }
        this.touchedSubscription = merge(...this.radios.map(radio => radio.touched$))
          .pipe(takeUntil(this.destroy$))
          .subscribe(() => {
            Promise.resolve().then(() => this.onTouched());
          });
      });
  }
}

不是自己寫的看起來好難。
在這裏插入圖片描述
在這裏插入圖片描述

<ng-content></ng-content>

radio組件用到的樣式

.sl-radio-wrapper {
  box-sizing: border-box;
  margin: 0 8px 0 0;
  padding: 0;
  color: rgba(0, 0, 0, .65);
  font-size: 14px;
  font-variant: tabular-nums;
  line-height: 1.5;
  list-style: none;
  font-feature-settings: 'tnum';
  position: relative;
  display: inline-block;
  white-space: nowrap;
  cursor: pointer;
}

.sl-radio {
  width: 16px;
  height: 16px;
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  color: rgba(0, 0, 0, .65);
  font-size: 14px;
  font-variant: tabular-nums;
  list-style: none;
  font-feature-settings: 'tnum';
  position: relative;
  display: inline-block;
  line-height: 1;
  white-space: nowrap;
  vertical-align: sub;
  outline: 0;
  cursor: pointer;

  .sl-radio-input {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    cursor: pointer;
    opacity: 0;
  }

  .sl-radio-inner {
    position: absolute;
    top: -1px;
  }
}

.sl-radio-content {
  padding-right: 8px;
  padding-left: 8px;
}

關於radio-group組件的各種組合

<div class="usage radio-usage">
  <h2>單個 radio狀態</h2>
  <h3>可取消選中</h3>
  <label sl-radio [isSelected]="true" [isCanCancel]="true" slValue="A">A</label>
  <h3>不可取消選中</h3>
  <label sl-radio>E</label>
  <label sl-radio slValue="B">B</label>
  <label sl-radio [isDisabled]="true" slValue="C">C</label>
  <label sl-radio [isSelected]="true" slValue="D" [isDisabled]="true">D</label>

  <h2>多個radio互斥,可選中1個,可1個不選</h2>
  <sl-radio-group [(ngModel)]="radioValue" [isCanCancel]="true">
    <label sl-radio slValue="A">A</label>
    <label sl-radio slValue="B">B</label>
    <label sl-radio slValue="C">C</label>
    <label sl-radio slValue="D">D</label>
    <sl-button [btnType]="'confirm'" (btnClick)="getRadioValue(radioValue)"></sl-button>
  </sl-radio-group>
  <h2>多個radio互斥,要麼不選,要麼選中1個</h2>
  <sl-radio-group [(ngModel)]="radioValue2">
    <label sl-radio slValue="A">A</label>
    <label sl-radio slValue="B">B</label>
    <label sl-radio slValue="C">C</label>
    <label sl-radio slValue="D">D</label>
    <sl-button [btnType]="'confirm'" (btnClick)="getRadioValue(radioValue2)"></sl-button>
  </sl-radio-group>

  <h2>多個radio 不互斥,可多選,也可不選</h2>
  <sl-radio-group [(ngModel)]="radioValue3" [isCanCancel]="true" [isMultiple]="true">
    <label sl-radio slValue="A">A</label>
    <label sl-radio slValue="B">B</label>
    <label sl-radio slValue="C">C</label>
    <label sl-radio slValue="D">D</label>
    <sl-button [btnType]="'confirm'" (btnClick)="getRadioValue(radioValue3)"></sl-button>&nbsp;&nbsp;
    <sl-button [btnType]="'danger'" (btnClick)="radioValue3=''">清除</sl-button>
  </sl-radio-group>
</div>

在這裏插入圖片描述
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章