前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Magicodes.WeiChat——自定义knockoutjs template、component实现微信自定义菜单

Magicodes.WeiChat——自定义knockoutjs template、component实现微信自定义菜单

作者头像
雪雁-心莱科技
发布于 2018-12-27 06:08:43
发布于 2018-12-27 06:08:43
94900
代码可运行
举报
文章被收录于专栏:magicodesmagicodes
运行总次数:0
代码可运行

本人一向比较喜欢折腾,玩了这么久的knockoutjs,总觉得不够劲,于是又开始准备折腾自己了。

最近在完善Magicodes.WeiChat微信开发框架时,发现之前做的自定义菜单这块太不给力了,而各种第三方平台在这一块做得也比较渣,功能不全不说,界面还很不友好,于是决心重整一版,以满足需求。

下面先上图,新的UI界面如下所示:

如何实现这个功能呢?下面请等我一一道来吧。

左侧树形结构绑定

HTML模板如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<div class="dd" id="nestable2">
    <ol class="dd-list" data-bind="foreach:Menus()">
                                    <li class="dd-item lv1">
                                        <div class="dd-handle">
                                            <span class="pull-right">
                                                <i class="fa fa-plus" data-bind="click:$root.AddClick"></i> &nbsp;&nbsp;
                                                <i class="fa fa-times" data-bind="click:$root.RemoveItem"></i>&nbsp;&nbsp;
                                                <i class="fa fa-pencil" data-bind="click:$root.ItemClick"></i>
                                            </span>
                                            <span>
                                                <span class="label label-info"><i class="fa" data-bind="css:$root.getIconCssByType(type)"></i></span><span data-bind="text:name,click:$root.ItemClick"></span>
                                            </span>
                                        </div>
                                        <!-- ko if:$data.sub_button !== undefined  -->
                                        <ol class="dd-list" data-bind="foreach:$data.sub_button">
                                            <li class="dd-item lv2" data-id="2">
                                                <div class="dd-handle">
                                                    <span class="pull-right">
                                                        <i class="fa fa-times" data-bind="click:$root.RemoveItem"></i>&nbsp;&nbsp;
                                                        <i class="fa fa-pencil" data-bind="click:$root.ItemClick"></i>
                                                    </span>
                                                    <span class="label label-success"><i class="fa" data-bind="css:$root.getIconCssByType(type)"></i></span> <span data-bind="text:name"></span>
                                                </div>
                                            </li>
                                        </ol>
                                        <!-- /ko -->
                                    </li>
                                </ol>
</div>

这里我解释一下,上述模板用到了两个foreach循环,以便绑定这个两级列表。实际上如果数据结构支持的话,ko是可以递归的绑定的。ko的强大性是毋庸置疑的。然后注意这个注释:“<!-- ko if:$data.sub_button !== undefined  -->”,这个真的不是注释,这个是有用的。为了不产生脏元素,ko支持这种绑定写法。这里先用if做了判断,然后再绑定子集。其余的,就是简单的data-bind语法了。

通过上述模板,我们注意到数据结构中两个关键点:Menus和sub_button,那我们就来看看viewModel。viewModel中定义了Menus = ko.observableArray([]),然后使用Ajax获取数据来填充:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//初始化,加载数据
            this.Init = function () {
                mwc.ui.setBusy();
                self.Api.request('GET', {
                    url: '/api/Menus',
                    func: function (data) {
                        mwc.ui.clearBusy();
                        $.each(data, function (i, v) {
                            if (v.sub_button) {
                                $.each(v.sub_button, function (i1, v1) {
                                    v.sub_button[i1] = $.extend(self.getModelTpl(), v1);
                                })
                            }
                            data[i] = $.extend(self.getModelTpl(), v);
                        });
                        self.Menus(ko.mapping.fromJS(data));
                    }
                });
            };

注意,因为方便,这里使用了knockout.mapping js,请注意ko.mapping.fromJS方法。

右侧编辑模板绑定

这块无疑是比较复杂的一块,我们先进行肢解:

  1. 通用模块:顶部按钮组、名称输入框、保存按钮
  2. 模板(按微信类型加载不同模板)

我们先来看看整体的编辑模板:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<div class="ibox-title">
                    <h5>按钮其他参数 </h5>
                </div>
                <div class="ibox-content" data-bind="with:EditModel" style="min-height: 600px;">
                    <form class="form-horizontal">
                        <!-- ko if:type() != 'empty' -->
                        <buttonschoices params="SelectsModel: $root.SelectTypes,SelectValue:type"></buttonschoices>
                        <div class="hr-line-dashed"></div>
                        <div class="form-group">
                            <label class="col-sm-2 control-label">名称</label>
                            <div class="col-sm-10">
                                <input type="text" class="form-control" data-bind="value:name" required>
                            </div>
                        </div>
                        <div class="hr-line-dashed"></div>
                        <!-- /ko -->
                        <div data-bind="template:{name:$root.GetEditTemplateName,data:$root.EditModel,afterRender:$root.afterEditTemplateRender}">
                        </div>
                        <!-- ko if:type() != 'empty'  -->
                        <div>
                            <button class="btn btn-primary pull-right" type="button" data-bind="click:$root.Save">
                                <i class="fa fa-save"></i>
                                <strong>保存</strong>
                            </button>
                        </div>
                        <!-- /ko -->
                    </form>
                </div>

由模板可知,整个编辑模块由类型按钮组、名称框、动态模板、保存按钮组成。接下来我就先介绍下类型按钮组的定义与绑定:

类型按钮组——knockout component

如上述代码中,使用了html标签buttonschoices。而这个标签就是我定义的knockout compoent。使用knockout compoent能做什么呢?就如上述代码中,我们可以知道以下几点:

  • 返回HTML模板
  • 传递参数,绑定compoent ViewModel

那么封装knockout compoent,有助于我们封装一些通用UI组件,就比如按钮组类型选择。我们先来一览代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//按钮组选择组件
ko.components.register('buttonschoices', {
    viewModel: function (params) {
        var self = this;
        //所选值
        this.SelectValue = ko.observable();
        //text:文本
        //value:值
        //icon:图标
        //des:描述
        this.SelectItem = ko.observable({ text: "", value: "", icon: "", des: "" });
        //选择模型
        this.SelectsModel = ko.observableArray([]);
        if (params && typeof (params.SelectsModel()) != "undefined") {
            self.SelectsModel(params.SelectsModel());
            if (typeof (params.SelectValue()) != "undefined") {
                self.SelectValue(params.SelectValue());
                self.SelectItem($.grep(self.SelectsModel(), function (v, i) { return v.value == self.SelectValue() })[0]);
            }
        }
        this.GetActiveCss = function (item) {
            return item.value == self.SelectValue() ? "active btn-primary" : "";
        }
        this.buttonClick = function (item) {
            self.SelectValue(item.value);
            self.SelectItem(item);
            params.SelectValue(item.value);
        }
    },
    template: '<div class="btn-group" data-bind="foreach: SelectsModel">' +
                    '<button class="btn btn-white" data-bind="css:$parent.GetActiveCss($data),click:$parent.buttonClick"><i class="fa" data-bind="css:icon"></i>&nbsp;<span data-bind="text:text"></span></button>' +
                '</div>' +
                '<div class="well" data-bind="with:SelectItem">' +
                    '<span data-bind="text:des"></span>' +
                '</div>'
});

整个组件代码很简洁明了,通过ko.components.register注册组件,buttonschoices为组件名称,整个组件由两部分组成:

  • viewModel:视图模型
  • template:模板

其中,viewModel接收了传入参数,并且进行了处理。我们来依次解析这个viewModel:

  • SelectValue:所选指。这个所选指会根据传入参数(还记得前面的“<buttonschoices params="SelectsModel: $root.SelectTypes,SelectValue:type"></buttonschoices>”吗,其中SelectValue:type就是传入了参数SelectValue)进行赋值,如右侧代码:self.SelectValue(params.SelectValue())。
  • SelectItem:所选项。项结构为{ text: "", value: "", icon: "", des: "" },分别代表文本、值、图标和描述。
  • SelectsModel:选择模型,就是列表模型。有多少个按钮,就看其有多少个项了。传入参数见“SelectsModel: $root.SelectTypes”。我们来看看这个$root.SelectTypes是怎么定义的:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//类型选择
            this.SelectTypes = ko.observableArray([
                { text: "点击推事件", value: "click", icon: "fa-font", des: "用户点击此类型按钮后,微信服务器会通过消息接口推送消息类型为event    的结构给开发者(参考消息接口指南),并且带上按钮中开发者填写的key值,开发者可以通过自定义的key值与用户进行交互" },
                { text: "跳转URL", value: "view", icon: "fa-link", des: "用户点击此类型按钮后,微信客户端将会打开开发者在按钮中填写的网页URL,可与网页授权获取用户基本信息接口结合,获得用户基本信息。" },
                { text: "扫码推事件", value: "scancode_push", icon: "fa-qrcode", des: "用户点击按钮后,微信客户端将调起扫一扫工具,完成扫码操作后显示扫描结果(如果是URL,将进入URL),且会将扫码的结果传给开发者,开发者可以下发消息。" },
                { text: "扫码推事件且弹出“消息接收中”提示框", value: "scancode_waitmsg", icon: "fa-qrcode", des: "用户点击按钮后,微信客户端将调起扫一扫工具,完成扫码操作后,将扫码的结果传给开发者,同时收起扫一扫工具,然后弹出“消息接收中”提示框,随后可能会收到开发者下发的消息。" },
                { text: "弹出系统拍照发图", value: "pic_sysphoto", icon: "fa-camera", des: "用户点击按钮后,微信客户端将调起系统相机,完成拍照操作后,会将拍摄的相片发送给开发者,并推送事件给开发者,同时收起系统相机,随后可能会收到开发者下发的消息。" },
                { text: "弹出拍照或者相册发图", value: "pic_photo_or_album", icon: "fa-camera", des: "用户点击按钮后,微信客户端将弹出选择器供用户选择“拍照”或者“从手机相册选择”。用户选择后即走其他两种流程。" },
                { text: "弹出微信相册发图器", value: "pic_weixin", icon: "fa-picture-o", des: "用户点击按钮后,微信客户端将调起微信相册,完成选择操作后,将选择的相片发送给开发者的服务器,并推送事件给开发者,同时收起相册,随后可能会收到开发者下发的消息。" },
                { text: "弹出地理位置选择器", value: "location_select", icon: "fa-map-marker", des: "用户点击按钮后,微信客户端将调起地理位置选择工具,完成选择操作后,将选择的地理位置发送给开发者的服务器,同时收起位置选择工具,随后可能会收到开发者下发的消息。" },
                { text: "下发消息(除文本消息)", value: "media_id", icon: "fa-newspaper-o", des: "用户点击按钮后,微信服务器会将开发者填写的永久素材id对应的素材下发给用户,永久素材类型可以是图片、音频、视频、图文消息。请注意:永久素材id必须是在“素材管理/新增永久素材”接口上传后获得的合法id。" },
                { text: "跳转图文消息URL", value: "view_limited", icon: "fa-envelope", des: "用户点击按钮后,微信客户端将打开开发者在按钮中填写的永久素材id对应的图文消息URL,永久素材类型只支持图文消息。请注意:永久素材id必须是在“素材管理/新增永久素材”接口上传后获得的合法id。" }
            ]);

众所周知,微信自定义菜单支持10中类型的按钮,那么这里是其类型的定义。这也说明,这个按钮组是完全通用的,你只要给予与上述结构一致的数据,其就能显示成当前效果。

  • GetActiveCss:获取当前所选样式。选中返回选中样式,否则返回空。
  • buttonClick:按钮点击事件,这里拿到的是数据项,ko就是这么方便。然后值得注意的是,参数是双向的,我们可以利用“params.SelectValue(item.value);”来回写值,这样编辑模型的类型值才会产生改变。

viewModel很简单,template也很简单,就是将刚才所说的viewModel绑定,用到了BootStrap按钮组样式“btn-group”,用foreach绑定SelectsModel,然后逐个绑定。

注意:

$parent表示父级对象,即乃父,因为foreach之后,其实对象已经指定到了乃父的儿子(SelectsModel)的某个儿子($data)上,而GetActiveCss是viewModel的女儿,自然要通过乃父来获取了,毕竟其乃父的儿子的子孙并不是她。

$data表示当前项,即乃父的儿子的某个儿子,用于循环中获取当前项数据。

with类似于using命名空间一样,用了它,下面的元素都可以省却改命名空间了。

是不是很简单的样子。我们再来说说模板:

动态加载模板

首先,我们先聚焦到以下代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<div data-bind="template:{name:$root.GetEditTemplateName,data:$root.EditModel,afterRender:$root.afterEditTemplateRender}">
                        </div>

首先我们得明确以下内容:

template语法用于绑定模板,其中name用于指定模板名称,这里绑定了$root.GetEditTemplateName方法,data用于指定模板的viewModel。

然后我们再来看看GetEditTemplateName怎么回事?如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//根据类型获取编辑模板
            this.GetEditTemplateName = function (data) {
                switch (data.type()) {
                    case "empty":
                        return "emptyTemplate";
                    case "media_id":
                    case "view_limited":
                        return "media_idTemplate";
                    case "view":
                        return "urlTemplate"
                    default:
                        return "keyTemplate";
                }
            };

看起来也蛮简单的样子,就返回了一个模板名称,那我们再继续来看看这些模板。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<script id="emptyTemplate" type="text/html">
    <div class="well">
        <h3>注意事项:</h3>
        创建自定义菜单后,由于微信客户端缓存,需要24小时微信客户端才会展现出来。测试时可以尝试取消关注公众账号后再次关注,则可以看到创建后的效果。
    </div>
</script>
<script id="keyTemplate" type="text/html">
    <div class="form-group" id="buttonDetails_url_area">
        <label class="col-sm-2 control-label">关键字</label>
        <div class="col-sm-10">
            <input type="text" class="form-control" data-bind="value:key" />
        </div>
    </div>
</script>

<script id="urlTemplate" type="text/html">
    <div class="form-group" id="buttonDetails_url_area">
        <label class="col-sm-2 control-label">链接</label>
        <div class="col-sm-10">
            <input type="url" class="form-control" data-bind="value:url" />
        </div>
    </div>
</script>
<script id="media_idTemplate" type="text/html">
    <news-choice-button params="value: media_id"></news-choice-button>
</script>
<div data-bind="with:EditModel">
    <news-choice-modal params="value: media_id"></news-choice-modal>
</div>

模板的定义也蛮简单的,id和上面的字符串是一致的,类型必须为text/html。上面模板分别为空模板,关键字模板,链接模板和素材模板。

其中素材模板里面使用了自定义的component,和之前的buttonschoices一样,封装了多图文选择代码。

由于组件news-choice-button和news-choice-modal需要讲解的篇幅比较长,这里就暂不介绍了。

至于增删改查,对于ko来说,都是操作数据模型。比如左侧树形结构的增删,则是对Menus数组的增减操作,而编辑,则需要更新数组中的数据项。viewModel的修改,ko会自动重绘UI。这里就不多介绍了。

总结

通过使用knockoutjs 的动态模板,我们可以很方便的根据需要加载不同的模板进行绑定显示。而通过knockoutjs component的封装,我们可以很方便的实现对业务或者通用UI组件的封装,以达到重复使用的目的。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2015-09-25 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
Magicodes.WeiChat——媒体资源选择组件之media-choice(开源)
mediaType:默认选择的媒体类型,其中(0:图片;1,语音;2,视频;5,图文)
雪雁-心莱科技
2018/12/27
4220
使用knockout-sortable实现对自定义菜单的拖拽排序
关于自定义菜单的实现,这里就不多说了,需要了解的请访问:http://www.cnblogs.com/codelove/p/4838766.html
雪雁-心莱科技
2018/12/27
1K0
微信公众号-自定义菜单
自定义菜单能够帮助公众号丰富界面,让用户更好更快地理解公众号的功能。开启自定义菜单后,公众号界面如图所示
星哥玩云
2022/09/14
3.3K0
微信公众号-自定义菜单
Knockout.Js官网学习(监控属性Observables)
  现在已经知道如何创建一个简单的view model并且通过binding显示它的属性了。但是KO一个重要的功能是当你的view model改变的时候能自动更新你的界面。当你的view model部分改变的时候KO是如何知道的呢?答案是:你需要将你的model属性声明成observable的, 因为它是非常特殊的JavaScript objects,能够通知订阅者它的改变以及自动探测到相关的依赖。
aehyok
2018/09/11
1.5K0
Knockout.Js官网学习(监控属性Observables)
微信小程序【常用组件及自定义组件】
微信小程序入门的文章,其实暂时就更新到这里了,还有一个生命周期的内容,感觉当初整理的不是很好就不更新了,学完了 WXML、WXSS、一些常用组件,还有事件绑定,其实一个较为不错的页面已经能自己搭建出来了,如果你还想继续往后面研究,我给一下方向就是,可以开始看 与后台交互的内容了,发送异步请求,对数据进行回显等,回显内容时,你就又可以开始考虑用什么别的组件,可以让页面的数据加载或者观看更加顺滑,美观,当然有很多小伙伴都是后端,所以,可以自己搭个后台,试着弄用户登录授权这块,这块算是小程序一个重要的点,要好好研究一下,以后要做的事情,就和传统的后端基本一样了。我的文章都是根据官网写的原生写法,当然你也可以看一下一些小程序框架,或者看一看 ES6 的内容,毕竟我也是个后端,也没细细研究前端的内容,有什么想法或者意见,可以在公众号菜单页面直接给后台发文字,或者发送邮件:ideal_bwh@163.com
BWH_Steven
2020/09/18
1.9K0
MVVM(Knockout.js)的新尝试:多个Page,一个ViewModel
对于面向数据的Web应用来说,MVVM模式是一项不错的选择,它借助JS框架提供的“绑定”机制是我们无需过多关注UI(HTML)的细节,只需要操作绑定的数据源。MVVM最早被微软应用于WPF/SL的开发,所以针对Web的MVVM框架来说,Knockout.js(以下简称KO)无疑是“根正苗红”。在进行基于KO的Web应用开发时,我们一般会为具体的Web页面定义针对性的ViewModel,但是在很多情况下很多页面具有相同的UI结构和操作行为,考虑到重用和封装,我们是否为它们创建一个共享的ViewModel呢。最
蒋金楠
2018/01/15
2.9K0
MVVM(Knockout.js)的新尝试:多个Page,一个ViewModel
Knockout.Js官网学习(创建自定义绑定)
你可以创建自己的自定义绑定 – 没有必要非要使用内嵌的绑定(像click,value等)。你可以你封装复杂的逻辑或行为,自定义很容易使用和重用的绑定。例如,你可以在form表单里自定义像grid,tabset等这样的绑定。
aehyok
2018/09/11
8350
Knockout.Js官网学习(创建自定义绑定)
KnockoutJS语法
  假设我们的页面输入区域有一个div用来展示一件物品的名字,同时有一个输入框用来编辑这件物品的名字
javascript.shop
2019/09/04
2.4K0
KnockoutJS语法
基于Webpack, KnockoutJs,esyui,koeasyui实现类vue-cli生成的模板框架
      前后端分离的开发机制,基本上是开发现代业务系统的标配。可在国内某些特殊领域还是存在大量的以JQuery走天涯的现象,但其中也不泛有追求技术者,如不才的鄙人。不才的本人曾以JQuery走天涯;后又接受了Knockoutjs,被他MVVM思想所吸引;也学习和了解过vue,他确实很棒,很了不起,但他不兼容ie8-(基于他的几款UI框架对低本的IE支持都不友好)。所以我产生了Knokcoutjs + easyui + webpack = ?的想法。有了想法就要开撸,终于在今年5月份完成了koeasyui
sam dragon
2018/11/08
1.2K0
Vue03基础语法--Vue组件+样式绑定+修饰符+常用控件+自定义指令+自定义事件
定义语法:new Vue({el:'#d1',components:{组件名:{配置选项}}})
天蝎座的程序媛
2022/11/18
1.3K0
KnockoutJS的基础用法
如今,各种前端框架应接不暇,令人眼花缭乱,有时不得不感叹作为程序猿也真是苦逼,总有学不完的技术,何时是尽头,除非你转化!苦海无边,回头是不是岸,由你决定!
javascript.shop
2019/09/04
5.7K0
KnockoutJS的基础用法
knockoutjs 上自己实现的flux
在knockoutjs 上实现 Flux 单向数据流 状态机,主要解决多个组件之间对数据的耦合问题。 一、其实简单 flux的设计理念和实现方案,很大程度上人借鉴和参考了Vuex的实现,只是简化了某些
sam dragon
2018/01/17
9770
knockoutjs 上自己实现的flux
Knockout.Js官网学习(click绑定)
click绑定在DOM元素上添加事件句柄以便元素被点击的时候执行定义的JavaScript 函数。大部分是用在button,input和连接a上,但是可以在任意元素上使用。
aehyok
2018/09/11
3.1K0
Knockout.Js官网学习(click绑定)
Knockout.Js官网学习(event绑定、submit绑定)
event绑定在DOM元素上添加指定的事件句柄以便元素被触发的时候执行定义的JavaScript 函数。大部分情况下是用在keypress,mouseover和mouseout上。
aehyok
2018/09/11
2.7K0
Knockout.Js官网学习(event绑定、submit绑定)
vue常见操作使用手法
webpack + vue + element(mint-ui, etc...) + axois (vue-resource) + less-loader+ ...
用户2736476
2018/07/25
1.6K0
vue常见操作使用手法
ASP.NET MVC5+EF6+EasyUI 后台管理系统(74)-微信公众平台开发-自定义菜单
1、click:点击推事件用户点击click类型按钮后,微信服务器会通过消息接口推送消息类型为event的结构给开发者(参考消息接口指南),并且带上按钮中开发者填写的key值,开发者可以通过自定义的key值与用户进行交互;
用户1149182
2019/09/12
1.3K0
ASP.NET MVC5+EF6+EasyUI 后台管理系统(74)-微信公众平台开发-自定义菜单
Knockout.Js官网学习(value绑定)
value绑定是关联DOM元素的值到view model的属性上。主要是用在表单控件<input>,<select>和<textarea>上。
aehyok
2018/09/11
2.3K0
Knockout.Js官网学习(value绑定)
Magcodes.WeiChat——自定义CustomCreationConverter之实现微信自定义菜单的序列化
微信自定义菜单接口是一个比较麻烦的接口,往往开发的小伙伴们看到下面的这段返回JSON,整个人就会不好了:
雪雁-心莱科技
2018/12/27
6090
微信公众平台开发 自定义菜单
自定义菜单能够帮助公众号丰富界面,让用户更好更快地理解公众号的功能。开启自定义菜单后,公众号界面如图所示:
庞小明
2018/09/19
2K0
微信公众平台开发 自定义菜单
MVC3.0+knockout.js+Ajax 实现简单的增删改查
    自从到北京入职以来就再也没有接触MVC,很多都已经淡忘了,最近一直在看knockout.js 和webAPI,本来打算采用MVC+knockout.js+webAPI来实现这个小DEMO的,无奈公司用的开发环境是VS2010只安装了MVC3.0。那就先练习一下MVC和knockout吧。博客园里有很多这样的文章,但是觉得还是要自己亲自动手写一下。
写代码的猿
2019/04/11
2.5K0
MVC3.0+knockout.js+Ajax 实现简单的增删改查
推荐阅读
相关推荐
Magicodes.WeiChat——媒体资源选择组件之media-choice(开源)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验