MVC 框架中以数据驱动 UI 的笔记

MVC 框架中以数据驱动 UI 的笔记

在开发公司的公共组件的时候,遇到了一个切换 tab 的功能:当点击某一 tab 的时候,为其添加.active class ,并且它所有的兄弟元素移除掉 .active class。而由于不想操作 dom 便有了这样的笔记,记录自己解决问题的过程。

功能背景

这样的功能在许多场景都可以用到,包括不限于目前本公司项目组件中的日期选择功能、单选/多选功能、手风琴功能等。它们实现的逻辑是相同的。都是使用某一值对 item(可以是 tabs 中的某个tab,也可以是单选中的某一项等) 的状态进行管理。根据这个提示,再根据下面的具体例子,思考下具体实现:
线上体验
经过一番思考,我们的大致思路就很清楚了,让我们再次来捋一捋

实现逻辑

在这些 item 的共同父级,如果在组件中,那就是在这些 item 的共同父组件中,保存了一个类型为基础类型或者是 object 类型的变量,其具体的值是某一 item 的 name 或 id 或能标示 item 唯一性的值,当点击某一 item 的时候,通过事件传递修改这个变量值,然后再将这个变量中的值与 item 中的值进行比较,如果相同那就添加.active class,不相同则移除.active class.
到现在,实现逻辑大致应该就是这样的,但是,很明显,上代码才是最实在的事情:

代码展示

notes:这部分的代码演示基础是基于 ember.js 框架。其后可能会添加 react.js 框架的相关代码。但实现逻辑应该大致相同。
首先来看我们要实现的示例:

实现结果
每点击一个水果,就为此水果 item 添加 .active class,在这里的表现就是 border 的颜色从#eee更改为 lightgreen.(gif 以及更好看的示例或许会更新)。 那就开始做:

创建子组件(item组件)

在 ember 项目中,通过 ember-cli 创建名称为 fruit-item 的子组件,它的 component.js 以及 template.hbs 文件为:

/**
 * fruit-item/component.js
 */
import Component from '@ember/component';
import { computed }  from '@ember/object';

export default Component.extend({
    tagName: 'li',
    classNames: ['item'],
    localClassNames: 'item', // 因为使用了 css-module 插件
    classNameBindings: ['active'],
    active: computed('choosed',function() {
        let {fruit,choosed}  = this.getProperties('fruit','choosed');

        if(fruit.id === choosed.id) {
            return true
        }
        return false
    })
});

{{!-- fruit-item/template.hbs --}}
{{fruit.name}} and count is {{fruit.count}}

通过上面的 hbs文件我们可以看出此子组件只是简单的展示水果以及数量。而在component.js 文件中,我们使用了 classitemli标签来进行页面渲染,通过 ember 的 computed来计算当前的 fruit 变量与用户点击的 choosed 变量之间的异同,如果相同,我们返回 true,通过 ember 的 classNameBindingsactive 属性值为 true 的时候,为此组件添加名为 active 的类。如果返回 false ,那目前不添加任何其他类。子组件的功能展示目前就是这些了,那我们需要一个共同的父组件来管理这些 item。

创建父组件

继续通过 ember-cli 来生成名为 fruit-list 的组件,它的 component.js 以及 template.hbs 文件为:

/**
 * fruit-list/component.js
 */
import Component from '@ember/component';
import {A} from '@ember/array';
import EmberObject from '@ember/object';

export default Component.extend({
    choosed: EmberObject.create({name:'orange',value:1,id:'item2',count: 21}),
    tagName:'ul',
    items: A([
        {name:'apple',value:0,id:'item1',count: 3},
        {name:'orange',value:1,id:'item2',count: 21},
        {name:'banana',value:2,id:'item3',count: 1},
        {name:'watermelon',value:3,id:'item4',count: 4},
        {name:'cherry tomato',value:4,id:'item5',count: 4},
        {name:'nectarine',value:5,id:'item6',count: 9},

    ])
});

{{!-- fruit-list/template.hbs --}}
{{#each items as |item|}}
    {{fruit-item fruit=item choosed=choosed}}
{{/each}}

这里可以看到我们通过 handlebars 的循环语法- eash as 来遍历了我们在 component.jsmock 的水果数据,并将每条水果的数据传递给了 fruit-item 组件,同时还有我们预设定的选中水果- choosed 变量。至此我们就可以看到上面我们要实现的样子了。

添加点击事件

但是,现在我们点击每个水果,它们的 active 状态并不会改变,我们开始着手添加这个功能。首先修改 fruit-itemcomponent.js 文件,问其添加 click 事件,并通过闭包将事件传递给上一层的调用者,也就是这里的 fruit-list

/**
 * fruit-item/component.js
 */
import Component from '@ember/component';
import { computed }  from '@ember/object';

export default Component.extend({
    tagName: 'li',
    classNames: ['item'],
    localClassNames: 'item', // 因为使用了 css-module 插件
    classNameBindings: ['active'],
    active: computed('choosed',function() {
        let {fruit,choosed}  = this.getProperties('fruit','choosed');

        if(fruit.id === choosed.id) {
            return true
        }
        return false
    }),
    onClick() { },
    click() {
        let fruit = this.get('fruit'),
            action = this.get('onClick');

        action(fruit);
    }
});

可以看到在事件传递的过程中我们将当前水果 fruit 的值作为实参进行传递。
而在 fruit-list 中我们接受这个事件,并拿到用户当前点击的水果的值- fruit

{{!-- fruit-list/template.hbs --}}
{{#each items as |item|}}
    {{fruit-item fruit=item choosed=choosed onClick=(action (mut choosed))}}
{{/each}}

这里可以看到在点击后我们拿到 fruit 的值后将其复制给 choosed 变量。
mut 语法。
这样我们的当前功能就完成了。

总结

以数据驱动 UI 的最基本的操作应该就是这样了,掌握了这个思想,那么其他的组件包括上文提到的 radio/checkbox(array) 也可以按照这样的思想进行设计。
同时本文中应用到的技术大多是与 emberjs 框架相关,其运用到的相关知识点有:

  • css-module 插件的使用;
  • ember 组件中 classNameBindings 的用法;
  • computed 方法的使用;
  • handlebars 语法;
  • ember 中父组件向子组件传值/子组件向父组件传值;
  • ember template 中 mut helper 的使用;

这篇文章到此就结束了,如有疑问,欢迎留言评论。

Written by Frank Wang.

发布了37 篇原创文章 · 获赞 12 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/peng_9/article/details/100148525