Angular 2 + 折腾记 :(6) 动手实现只有年月的小组件

前言

这个组件实现并不是很复杂,我会尽量注释; 这货诞生的理由就是项目刚好有一个地方必须只能选择年月,而githubng2+日期组件都涉及到年月日或时分秒; 效果用gifcam录制的,色彩有些失真,将就吧。。动手动手出真理


效果图


实现思路

  1. 月份只有十二个,只要把个位数做好补位就好
  2. 年份的生成是可控的(传参),当前年份往前推几年和往后推几年构成;
  3. 数据遍历结构很简单
{
    date:2017, // 月份是字符串,年份是数字
    active:false,
    type:'year' // 月份是'month'
}复制代码

页面样式就自行折腾啦,我这里只是用了最直白粗暴的下拉滑动

在ng4写的。动画模块需要单独引入,可以看我的其他文章有介绍


实现代码

  • html
<div class="only-year-month-select">
  <div class="select" (click)="isExpand = !isExpand">
    <input type="text" [placeholder]="placeholder" class="form-control" [(ngModel)]="selected" [disabled]="true">
  </div>
  <div class="list-menu" *ngIf="isExpand" [@fadeIn]="true">
    <div class="sub-list1">
      <p class="title">年份</p>
      <ul class="year-wrap">
        <li *ngFor="let i of selectYearRange;let index = index" [class.active]="i.active" (click)="actValue(selectYearRange,index)"
          (dblclick)="emitResult(i)">{{i.date}}</li>
      </ul>
    </div>
    <div class="sub-list2">
      <p class="title">月份</p>
      <ul class="month-wrap">
        <li *ngFor="let i of selectMonthRange;let index = index" [class.active]="i.active" (click)="actValue(selectMonthRange,index)"
          (dblclick)="emitResult(i)">{{i.date}}</li>
      </ul>
    </div>
  </div>
</div>复制代码
  • components
import { Component, OnInit, Input, Output, EventEmitter, HostListener, ElementRef } from '@angular/core';

// 动画
import { fadeIn } from '../../animation/fadeIn';

@Component({
  selector: 'app-only-year-month-select',
  templateUrl: './only-year-month-select.component.html',
  styleUrls: ['./only-year-month-select.component.scss'],
  animations: [fadeIn]
})
export class OnlyYearMonthSelectComponent implements OnInit {
  @Input() public placeholder: string;
  @Input() public range: any;
  @Output() public result = new EventEmitter();
  public isExpand = false;
  public selectYear: any;
  public selectMonth: any;
  public selectYearRange: Array<any> = [];
  public selectMonthRange: Array<any> = [];
  public selected: any;
  constructor(
    private _el: ElementRef
  ) { }

  ngOnInit() {
    this.getCurrentDate();
  }

  // 获取当前的年月
  getCurrentDate(): void {
    const TODAY = new Date();
    this.selectYear = TODAY.getFullYear();
    this.selectMonth = TODAY.getMonth() < 9 ? '0' + (TODAY.getMonth() + 1) : String(TODAY.getMonth() + 1);
    this.selectYearRange.push(
      {
        date: this.selectYear,
        active: true,
        type: 'year'
      }
    );
    this.selectMonthRange.push(
      {
        date: this.selectMonth,
        active: true,
        type: 'month'
      }
    );
    this.selectYearRange = this.getRangeYear(this.selectYear, this.range.before, this.range.after);
    this.selectMonthRange = this.getRangeMonth(this.selectMonth);
    console.log(this.selectYearRange, this.selectMonthRange);
  }

  // 需要生成的日期范围
  getRangeYear(year: number, before: number = 5, after: number = 10): any {
    // console.log(year, before, after);

    let _beforeYear = year;
    let _afterYear = year;

    for (let i = 0; i < before; i++) {
      this.selectYearRange.unshift(
        {
          date: --_beforeYear,
          active: false,
          type: 'year'
        }
      );
    }
    for (let j = 0; j < after; j++) {
      this.selectYearRange.push(
        {
          date: ++_afterYear,
          active: false,
          type: 'year'
        }
      );
    }
    return this.selectYearRange;
  }

  // 月份范围
  getRangeMonth(month): any {
    for (let i = 0; i < 12; i++) {
      const temp = i < 9 ? '0' + (i + 1) : '' + (i + 1);
      if (month !== temp) {
        this.selectMonthRange.push(
          {
            date: temp,
            active: false,
            type: 'month'
          });
      }
    }
    this.selectMonthRange.sort(this.compare('date'));
    return this.selectMonthRange;
  }

  // 数组对象排序
  compare(property) {
    return function (a, b) {
      const value1 = a[property];
      const value2 = b[property];
      return value1 - value2;
    };
  }

  // 年份或者月份选择
  actValue(list: any, index: number) {

    list.forEach((v, i) => {
      if (i === index) {
        v.active = true;
        if (v.type === 'year') {
          console.log(v.date);
          this.selectYear = v.date;
        }
        if (v.type === 'month') {
          console.log(v.date);
          this.selectMonth = v.date;
        }

      } else {
        v.active = false;
      }
    });
    this.emitResult();
  }



  // 获取结果集
  emitResult(e?: any) {
    if (e) { // 双击则关闭弹出
      this.isExpand = false;
    }
    this.selected = this.selectYear + '-' + this.selectMonth;
    this.result.emit(this.selected);

  }


  // 监听全局点击事件
  @HostListener('document:click', ['$event.target'])
  public onClick(targetElement) {
    const clickedInside = this._el.nativeElement.contains(targetElement);
    if (!clickedInside) {
      this.isExpand = false;
      this.emitResult();
    }

  }
}复制代码
  • fadeIn.ts
// 动画的效果很简单,就是把css3的效果用js来实现,具体的效果就是渐现
// 放在突然出来一块区域的地方,触发看起来会比较顺眼,有个过渡

import { trigger, state, style, transition, animate, keyframes } from '@angular/animations';
//transition(':enter', [ ... ]); // void => *
//transition(':leave', [ ... ]); // * => void
export const fadeIn = trigger('fadeIn', [
  state('in', style({ display: 'none' })),
  transition('void => *', [
    animate(200, keyframes([
      style({ height: '0', opacity: 0, offset: 0 }),
      style({ height: '*', opacity: 1, offset: 1 })
    ]))
  ]),
  transition('* => void', [
    animate(200, keyframes([
      style({ height: '*', opacity: 1, offset: 0 }),
      style({ height: '0', opacity: 0, offset: 1 })
    ]))
  ]),
]);

// 200 是过渡时间,毫秒
// offset相当于 css3 中keyframes的百分比,,控制动画的进度的。。0 -> 1 就相当于 0% -> 100%复制代码
  • 封装成一个模块给其他使用
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';

import { OnlyYearMonthSelectComponent } from './only-year-month-select.component';

@NgModule({
  imports: [
    CommonModule, // 用用到一些内置的指令必须依赖这个,比如*ngIF, *ngFor
    FormsModule // 有用到表单元素必须引入这货
  ],
  declarations: [OnlyYearMonthSelectComponent], // 声明年月组件
  exports: [OnlyYearMonthSelectComponent] // 导出年月组件
})
export class OnlyYearMonthSelectModule { }复制代码

组件使用

温馨提示:

  • 若不是以模块的方式到处,只要在使用的模块引入组件声明下就能使用
  • 反之则需要引入这个模块,方可使用

局部代码

  • module
// 在要使用的模块中引入
// 公用组件
import { MitEhartsModule } from '../../../widgets/mit-echarts/mit-echarts.module';
import { MitDataTableModule } from '../../../widgets/mit-data-table/mit-data-table.module';
import { MitLoadingModule } from '../../../widgets/mit-loading/mit-loading.module';
import { OnlyYearMonthSelectModule } from '../../../share/only-year-month-select/only-year-month-select.module';
import { DepartmentSelectModule } from '../../../share/department-select/department-select.module';
const mitModule = [
  MitEhartsModule,
  MitDataTableModule,
  MitLoadingModule,
  DepartmentSelectModule,
  OnlyYearMonthSelectModule
]
@NgModule({
  imports: [
    CommonModule,
    FormsModule,
    NgbDatepickerModule,
    ...mitModule,
    DailyCarRoutes
  ],
  providers: [DailyCarService],
  declarations: [...page]
}复制代码
  • html
<div class="form-group condition-wrapper">
  <label class="form-control-label">月份</label>
  <app-only-year-month-select class="form-control" [placeholder]="'yyyy-mm'" (result)="Time = $event" [range]="{before:5,after:10}"></app-only-year-month-select>
</div>复制代码

总结

文章有错误之处亦或者有更好写法和建议的请留言指出,会及时改进和跟进...; 下一篇文章说下指令或者动画相关的。。。。整理下思路

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏hbbliyong

Android studio删除工程项目

 本新手最近学Android都是用的eclipse。其实个人觉得eclipse不错,可能接触Android不久,倒也不觉得它慢还是怎样。对于Google的And...

3838
来自专栏我有一个梦想

Python 项目实践一(外星人入侵小游戏)第三篇

接着上节的继续学习, 一 重构:模块game_functions 在大型项目中,经常需要在添加新代码前重构既有代码。重构旨在简化既有代码的结构,使其更容易扩展。...

3259
来自专栏Kiba518

【我们一起写框架】MVVM的WPF框架(三)—数据控件

这世上,没人能一次性写出完美无缺的框架;因为,任何一个框架都需要项目的淬炼,然后才能升华,趋近完美。

2023
来自专栏iOSDevLog

更多关于CocoaScript目录

3796
来自专栏程序员的SOD蜜

“老坛泡新菜”:SOD MVVM框架,让WinForms焕发新春

火热的MVVM框架 最近几年最热门的技术之一就是前端技术了,各种前端框架,前端标准和前端设计风格层出不穷,而在众多前端框架中具有MVC,MVVM功能的框架成为耀...

4086
来自专栏向治洪

MobX 在 React Native开发中的应用

MobX 是一款精准的状态管理工具库,如果你在 React 和 React Native 应用中使用过 Flux、Alt、Redux 和 Reflux,那毫不犹...

5278
来自专栏Flutter&Dart

Flutter之drawer详细分析(你要的操作都有)

可以看到,根据我们对drawer的认识,并不是想要的结果,所以这个drawer并不完整,然后我们继续添加代码,修改drawer

2722
来自专栏hightopo

基于 HTML5 Canvas 绘制的电信网络拓扑图

2533
来自专栏Material Design组件

Human Interface Guidelines —— Pickers

1022
来自专栏守候书阁

vue快速入门的三个小实例

用vue做项目也有一段时间了,之前也是写过关于vue和webpack构建项目的相关文章,大家有兴趣可以去看下webpack+vue项目实战(一,搭建运行环境和相...

1271

扫码关注云+社区

领取腾讯云代金券