Angular4自制一个市县二级联动组件

简介:

最近遇到了不少问题,真的是命运多舛。Angular真是让人又爱又恨的框架,恨的是资料太少,遇到问题无从下手。爱的是许多其他框架难以做到的功能,angular却可以轻松做到。

话不多说,最近遇到了一个旧项目改造的问题。拿到前同事做的页面效果:

a2cc5cb8a3f21eb3ecf29a5326be968530f5d947

第一眼就看到了这三个下拉框,按捺不住好奇心的我点了点。原来,第一个下拉框可以选择市属和省属,如果选择市属,那么后面就会出现市、县级两个下拉框,如果是省属,那就隐藏了,这个挺容易的。然后就是要选择市之后,区下拉框要有对应区县选项。emmmm,很典型的二级联动,不过既然分析完了思路,那就开始做吧!首先呢,数据肯定要从后端同事那里拿,调用他的接口把数据填充进去。看看数据是什么样子的:

a7c36be01cfe63d6f63ba21b355a6d492ceda8bc

数据略多,就不全部贴出来了。把实体bean创建一下,

Typescript code

// 市级实体类
export class City {

    // 市级id
    cityId: string;

    // 所属类型(0.市属 1.省属)
    cityType: number;

    // 市级名称(可选属性,若cityType为1时,可不填)
    cityName: string;

    // 所属区县
    counties?: Array<Country>;

}

// 区县级实体类
export class Country {

    // 区县id
    countryId: string;

    // 区县名称
    countryName: string;

}

// 填写市县类
export class CityAndCountry {

    // 市级id
    cityId: string;

    // 县级id
    countryId: string;

    // 市级类型
    cityType: number;

    // 市县级实体构造器 
    constructor() {
        // 给市级id赋予一个真实城市的id初始值
        this.cityId = '***';
        // 同上
        this.countryId = '***';
        // 同上
        this.cityType = 0;
    }
}

实体完成了,开始准备获取数据并填充至实体:

Typescript code

// 二级联动组件
export class CityAreaComponent implements OnInit, OnDestroy {

    // 结果码 (用于页面处理显示标识)
    result_code: number;

    // 市级实体声明
    city: City[];

    // 县区级实体声明
    country: Country[];

    // 市县、区级填写实体声明
    cac: CityAndCountry;

    // 声明订阅对象
    subscript: Subscription;

    /**
    * 构造器
    * @param {CityService} service 注入服务
    */
    constructor (private service: CityService) {
        // 结果码 (-1.网络或其他异常 0.无内容 1.请求成功 2.请等待)
        this.result_code = 2;
        // 初始化填写市区、县级填写实体
        cac = new CityAndCountry();
        // 初始化数组(这步很重要,有很多人说使用数组相关函数会报未定义异常,是因为没有初始化的原因)
        this.city = [];
        this.country = [];
        // 初始化订阅对象
        this.subscript = new Subscription();
    }

    /**
    * 生命周期初始化钩子(生命周期尽量按执行顺序来写,养成好习惯)
    */
    ngOnInit(): void {
        this.getCityArea();
    }

    /** 获取市县数据 */
    getCityArea() {
        /** 将请求交付服务处理(service代码比较简单,就不贴了) */
        this.subscript = this.service.getCityArea().subscribe(res => {
            /** 获取json请求结果 */
            const result = res.json();
            /** 判断结果返回码 */
            switch (result['code']) {
                /** 请求成功,并且有值 */
                case 200:
                /** 改变初始返回码 */
                this.result_code = 1;
                /** 获取并填充数据 */
                this.city = json['city'];
                break;
                /** 其他情况不重复赘述 */
            }
        }, err => {
            /** 显示预设异常信息提示给用户 */
            this.result_code = -1;
            /** 打印log,尽量使用日志产出 */
            console.error(err);
        });
    }

    /** 生命周期销毁钩子 */
    ngOnDestroy(): void {
        /** 取消订阅 */
        this.subscript.unsubscribe();
    }
}

由于此处是单服务请求,为了让代码比较清晰直观,这里我就不做封装处理了。数据获取了之后就该填充到展示界面了:

Html code

<!-- 所属类型(此处固定,一般为获取后端数据字典数据) -->
<select class="city_type" [value]="cac.cityType" [(ngModel)]="cac.cityType">
    <!-- 所传内容为整数型 -->
    <option value=0>市属</option>
    <option value=1>省属</option>
</select>

<!-- 市级选择(类型为省属时隐藏) -->
<select class="city" [value]="cac.cityId" [(ngModel)]="cac.cityId" *ngIf="city.cityType==0">
    <!-- 遍历城市数组 -->
    <option *ngFor="let opt of option" [value]="opt.cityId">{{opt.cityName}}</option>
</select>

这时候,我们发现县级获取起来好像并不能直接获取,怎么办呢?我突然想到,我在ts里面声明一个变量获取市级选择的id号,然后再拿id去找下属县区,这样就可以轻松拿到了。既然要实时获取变化,那我们就实现检测变化钩子:

Typescript code

// 二级联动组件
export class CityAreaComponent implements OnInit, OnDestroy, DoCheck{
    // 声明县区级数组
    country: Array<Country>;

    constructor() {
        /** 重复代码不赘述 */
        /** 初始化数组 */
        country = [];
    }   

    /** 生命周期检测变化钩子 */
    ngDoCheck(): void {
        /** 遍历市级数组 */
        for (let i = 0; i < this.city.length; i++) {
            /** 若选择的市级id和市级数组中的id相吻合 */
            if (this.city[i].id == this.cac.cityId) {
                /** 将该索引下的counties数组赋予给区县级数组 */
                this.country = this.city[i].counties;
            }
            /** 我们无法避免直辖市的情况,所以多一重判断 */
            if (this.country.length > 0) {
                /** 为了用户体验,我们要尽量在用户选择市级城市后,默认选择一个区县级城市 */
                this.cac.country.id = this.country[0].id;
            }
        }
    }
}

最后再补上区县级下拉框:

Html code

<!-- 区县级下拉框 -->
<select [value]="cac.countryId" [(ngModel)]="cac.countryId" *ngIf="cac.cityType==0 && country.length > 0">
    <option *ngFor="let count of country" [value]="count.id">{{count.name}}</option>
</select>

到此为止,大功告成,再也不用去依赖别人的库了。


原文发布时间为:2017/11/21
原文作者: 落青
本文来源: 开源中国 如需转载请联系原作者
目录
相关文章
|
10天前
|
JavaScript
Angular使用@Input和@Output实现父子组件互相传参(类似Vue的props和this.emit)
Angular使用@Input和@Output实现父子组件互相传参(类似Vue的props和this.emit)
|
3月前
|
前端开发 JavaScript
Angular 组件模版代码里使用 ngIf 进行条件渲染的例子
Angular 组件模版代码里使用 ngIf 进行条件渲染的例子
21 0
|
4月前
Angular多个页面引入同一个组件报错The Component ‘MyComponentComponent‘ is declared by more than one NgModule怎么办?
Angular多个页面引入同一个组件报错The Component ‘MyComponentComponent‘ is declared by more than one NgModule怎么办?
|
4月前
快速创建Angular组件并定义传参、绑定自定义事件的方法
快速创建Angular组件并定义传参、绑定自定义事件的方法
|
8月前
|
JavaScript 定位技术
Angular1.x入门级自定义组件(导航条)
Angular1.x入门级自定义组件(导航条)
|
12月前
|
资源调度 JavaScript 容器
Angular封装WangEditor富文本组件
Angular封装WangEditor富文本组件
202 0
|
12月前
|
资源调度 前端开发 Java
使用Angular CDK实现一个Service弹出Toast组件
使用Angular CDK实现一个Service弹出Toast组件
87 0
|
12月前
|
JavaScript 前端开发 API
让Angular自定义组件支持form表单验证
让Angular自定义组件支持form表单验证
110 0
Angular组件传参
Input 是属性装饰器,用来定义组件内的输入属性。在实际应用场合,我们主要用来实现父组件向子组件传递数据。Angular 应用是由各式各样的组件组成,当应用启动时,Angular 会从根组件开始启动,并解析整棵组件树,数据由上而下流下下一级子组件。
|
资源调度
angular使用ngx-vant组件库
angular使用ngx-vant组件库