aralejs / widget Goto Github PK
View Code? Open in Web Editor NEWThe Base Class of Widget
Home Page: aralejs.org/widget/
The Base Class of Widget
Home Page: aralejs.org/widget/
貌似只要是从 https://raw.github.com/aralejs 下加载的都有问题。
SEC7112: https://raw.github.com/aralejs/base/1.0.0/dist/base.js 的脚本因 Mime 类型不匹配而被阻止
应该把文件下载下来就木有问题了
http://msdn.microsoft.com/zh-cn/library/hh180764(v=vs.85).aspx
现在 widget 的 destroy 只清除事件,子模块自己管理,比较乱。而且近期出现了内存泄漏的情况,对这块要进行梳理。
templatable.js中compile方法中下段代码
template || (template = this.template)
model || (model = this.model)
建议修改成下面的样子
template = template || this.get('template')
model = model || this.get('model')
原来的代码在
this.set('model', {})
后在_onChangeModel
调用
this.compile();
model还是初始化时候的model
这样的模板,替换成注释是不是有问题?
<input {{attr}} type="hidden" />
虽然可通过写法解决,但觉得大部分时候是需要继承父类events绑定的,有这方面的考虑不
在使用Widget的时候发现如果实现Template的话,因为使用了另外的model属性,该model属性在改变的时候只能实现粗粒度的变更。
如果将一些配置写在attrs里又没法使用Template里的解析方法。
var Test = Widget.extend({
Implements:[Template],
model:{
title:'Test',
data:'test'
},
template:'<div>{{title}}<p>{{data}}</p></div>'
});
var test = new Test();
调用下面的方法皆不生效:
test.set('title','TEST2'); //推荐的实现调用方式
test.set('model',{
title:'Test2',
data:'test'
});
如果使用attrs的话,又没办法和template建立关联,这个关联必须自己来建立。
这样的话使用模版的话就有点食之无味弃之可惜的感觉~~感觉还是去除model这个配置,放在attrs里比较好,这样可以让使用者自己判断。
widget在初始化属性的时候,判断是否为空的时候建议可以忽略,因为很多时候这些其实应该在渲染的时候判断,因为你无法知道为空或者其他值得时候默认应该做些什么。
if (!isEmptyAttrValue(val)) {
this[m](val, undefined, attr)
}
很多时候我们需要访问到模版添加到dom后的对象,现在的方式是通过以下方式实现的
this.$('[data-role=title]')
可不可以增加一个扩展点,比如以下的方式:
template: <div data-role="title"></div>
通过模版解析后可以通过如下方式访问到该dom,而无需手动通过this.$
方法来访问
this.title
@lifesinger 第二次调用destroy方法时出现错误:“Uncaught TypeError: Cannot call method 'off' of null ”。
康辉之前提到过的给 instance 动态增加方法/属性 的机制
和 YUI3 的插件机制一样,我们做了简化
在 autocomplete 组件下, 实例化为:
var ac = new AutoComplete({
trigger: '#acTrigger4',
template: $('#acTrigger4-template').html(),
dataSource: ['abc', 'abd', 'abe', 'acd'],
data: [], // 为空数组
inputValue: [] // 为空数组
}).render();
出现错误如下:
Uncaught RangeError: Maximum call stack size exceeded
原因是以下几行代码:
if ($.trim(this.$('[data-role=items]').text())) {
this.show();
} else {
this.hide();
}
https://github.com/aralejs/autocomplete/blob/1.2.0/src/autocomplete.js#L200
即, 当实例render()后, data/inputValue 为 [], 会执行 widget 的 _renderAndBindAttrs(), 并执行 autocomplete 的_onRenderData, 在执行到 上述代码后, 碰到 text() 不为空的时候, 执行 this.show(), 之后, overlay 的 show, 因为 此时 this.rendered 为false, 所以再次执行 this.render(), 导致循环.
建议是:
将 widget的 render() 中, this._renderAndBindAttrs() 和 this.rendered = true 换下位置.
https://github.com/aralejs/widget/blob/master/src/widget.js#L241
@lepture 补充下
Widget 对象由 template 初始化的,实例销毁后节点还残留在页面上,应予以清除。
比如:
<div id="t10"><h3>{{title}}</h3><div class="content">{{content}}<img src="{{src}}"></div></div>
在 render 时,会触发一个无效的图片下载。
@lifesinger destroy方法只移除了事件监听和dom的引用,但dom本身仍然留在页面中。设计就是这样的吗?
如果指定的 element 不存在,则不处理这个组件,与 parentNode 保持一致是否更好?
整理过去的一些 issue 和新提的 feature,准备升级一个版本。
data-widget-role
delegateEvents 支持其他 DOM 的事件绑定
this.delegateEvents(this.trigger, 'click p', function() {})
样式方案
在 this.element 外部会包一层容器,保证样式独立。className 为 family-name-version
,css 在编译的时候也会加一层命名空间,如
.family-name-version .ui-dialog {}
set 增加 override
如果是简单对象,默认是用 merge 的方式,override 可以覆盖当前对象
Widget.extend({
attrs: {
a: {b:1, c:2}
}
});
this.set('a', {b:2}); // => {b:2, c:2}
this.set('a', {}, {override: true}); // => {}
before 能阻止原方法
this.before('show', function() {
return false; // 阻止了 show 方法的调用
});
不再使用 this.model 和 this.template,使用的时候需要当作 attribute,如 this.get('model')。
templatable 从 widget 中移除,单独为一个组件,修改的时候需要修改 template 的依赖
"templatable": "arale/templatable/0.9.0/templatable"
注意 attribute 的默认值,当为 '' [] {} 是还是会触发 _onRender
widget 的属性 style
id
className
默认值改为 null
this.template
和 this.model
selector 和 dom element 无问题
目前当属性值在render时的值为空字符串时,不会进入 onRenderXXX 方法。
这块处理是否恰当,要再考虑一下。
https://github.com/aralejs/dialog/blob/1.0.0/src/dialog.js#L87
如题
目前只在Widget中隐式调用,但Widget用例中明确说明是可以直接使用DAParser的公共接口的。
现在 widget 使用全局注册 helper 的方式,render 后再去除 helper。是否可以替换成下面的方式
template(context, {helpers: helpers, partials: partials, data: data})
业务自定义了别名及路径,使用widget的autoRenderAll特性,还是会默认指向base库的dist目录。
这个 issue 应该是同一个问题: alipay/arale#171
接下来我们将使用 handlebars runtime,widget 的 templatable 可以删掉了。
下面介绍一下 runtime 的工作原理:
我们有一个模板:
{{ _ word }}
编译后得到一个 cmd 模块,这个模块返回一个 function
,记为 fn
。然后我们调用这个 fn
:
fn({word: 'hello'}, {
helpers: {
'_': function(key) {
return '你好'
}
}
})
就 ok 了。
把 merge 这步放到 widget 中实现
Hello,我遇到一个接口设计方面的问题,场景是一个倒计时功能组件,我期望UI层的内容展现格式可以通过getter方法来让开发者自定义,但是实践后发现实例化Widget时无法通过传入参数覆写getter方法。
示例场景代码如下:
(希望通过getter实现"小于10时十位补0"这类个性化需求)
// 实例化一个CountDown类(Widget的子类)
new CountDown({
// 下面这个secLast的getter是我想覆写的项,但跑起来后发现没给覆写
secLast : { // 离目标时间剩下的秒数
value : 0,
getter: function( val ){
return val < 10 ? ("0" + val) : val; // 剩余秒数小于10时,十位补0
}
},
element : '#cd',
targetTime : 10000000..
});
期望效果
还剩 03秒
情况如上,请问我的设计合理吗,或者能否通过别的接口满足这个需求?
补充:试了下通过继承CountDown来覆写secLast的getter是可行的,通过继承来实现这类需求才是正确的做法吗?
var MyCountDown = CountDown.extend({
attrs : {
secLast : {
value : 0,
getter: function( val ){
return val;
}
}
}
});
new MyCountDown({
element : '#cd',
targetTime : 10000000..
});
目前 widget 有个潜规则,在实例化的时候会将4个属性 (element
template
model
events
) 拷贝到 this 上。然后就直接操作 this 上的,比如 this.model
。
var widget = new Widget({
model: {},
template: ''
});
console.log(widget.model);
element
和 events
是可以理解的,element 是 widget 的一个核心元素,而 events 根本不应该通过实例化传入。
template
和 model
放到 this 上感觉没什么意义,只是为了操作上的方便。
我觉得使用 attribute 还有 _onRenderXxx
这种功能。比如在 data 变化时能做一些操作。
Widget.extend({
_onRenderData: function(data) {
this.renderPaital(...);
}
})
而且 this.get('model') 和 this.model 使用上也没什么差别。
所以我觉得可以去除将 template
和 model
复制到 this 上的功能。
用来监听每一个 attribute 的变化,类似 @lifesinger 上次介绍的 dom 属性监听功能。
这样可以解决类似 aralejs/select@b944ea7#L2R285 这样的事件。
现在render方法实现的是将element append到parentNode中,可是有时候,我希望能在模型改变后,只刷新这个组件的内部。我应该怎么做呢?
Widget.delegateEvents绑定事件时,在处理函数中获取触发元素有两种办法:
this.$('.classname')
ev.target
第一种方式,影响效率,第二种方式可能获取不准确。
建议增强该特性。
Widget.delegateEvents方法中有该段代码:
var callback = function(ev) {
if (isFunction(handler)) {
handler.call(widget, ev)
} else {
widget[handler](ev)
}
}
// delegate
if (selector) {
widget.element.on(eventType, selector, callback)
}
更改为:
var callback = function(ev) {
var context = this;
if (isFunction(handler)) {
handler.call(widget, ev, context)
} else {
widget[handler](ev, context)
}
}
// delegate
if (selector) {
widget.element.on(eventType, selector, callback)
}
// config 中的这些键值会直接添加到实例上,转换成 properties
propsInAttrs: ['initElement', 'element', 'events'],
这里面的值,最开始是 element, events, model, template
,初衷是提供一种简写方式:
this.element 等价 this.get('element')
this.model 等价 this.get('model')
是一种语法糖。后来因为发现 this.template
、this.model
实际上比较少用,所有在上次修改中去掉了。
今天 CR 代码,看到 events
还在,同时还增加了 initElement
,再仔细回顾『初心』:
propsInAttrs 是一种简化书写的语法糖
越发觉得 propsInAttrs 是个糟糕的设计,是否应该直接都放到 attrs 里就好?
如果要保留的话,也仅保留 element 就行,让 this.element
等价 this.get("element")
,同时在继承时,会自动处理好值的继承?
目前在widget内部处理element相关的dom事件都可以通过delegateEvents接口来解决,
但如果是 element之外的dom事件(比如popup中的trigger等) 却没办法通过delegateEvents接口来注册,
用jquery原生的dom事件注册又要考虑 context及unbind 的问题( 这2个问题在delegateEvents中已经解决 ),每次因为这个原因 多写一堆几乎相同的代码 真心蛋疼
建议: 能否提供一对类似于delegateEvents/undelegateEvents的接口来处理非element内部的dom事件 ,对 保持处理逻辑的一致性及widget的易用性 来说都有很大的帮助:)
数值的初始值为 0,但是 render 时会调用一次。
如果设置成 ''
或 null
不太符合数值初始值。不知道当时是出何考虑?
data-TEMPLATE 在 chrome 被转成了 data-template,所以替换回来的时候失效了
RT,个人觉得 render
和 destory
均为 widget 生命周期的特殊方法,不应该支持多次调用
所以 用 before/after 来限制调用更加适合,也不怕 方法被 override 了
this.before('render', function(){
if(this.rendered) return false;
}).after('render', function(){
this.rendered = true;
});
this.before('destory', function(){
if(!this.rendered || this.destoryed) return false;
}).after('destory', function(){
this.destoryed = true;
});
// 这样的话override方法就不要考虑 rendered 的状态了
render: function(){
// dosomething
Xxx.superclass.render.call(this);
return this;
}
由于 Widget 上存在静态方法 query,该方法依赖与类相关的私有变量 cachedInstances
这导致 Widget 静态类自身也有状态
当一个页面中存在多个 Widget 时,如果存在 auto render,就会出现问题
需要想一个办法解决掉。
API 可以说明下产生的原因,必要性,历史渊源等等,这样可以更有说服力。
如题。比如我让 widget 自动渲染了一个 overlay 组件,稍后我有操作该 overlay 组件的需求。
当前的 widget 文档中好像没有提到自动渲染后,这些类对象存储在何处?
目前widget的自动渲染功能.autoRenderAll(),是异步模块加载的实现机制。调用此方法后可能无法立刻获取到对应组件的实例,需要提供一种回调方式,来监听autoRenderFinish。
目前 data-api 存放在什么元素上,该元素就会被当成 widget 的 element
但 data-api 不一定仅存放在 widget element 上,还有可能存放在其他角色上,比如 trigger
因此可以考虑扩展:
<div data-element-role="{{data-element-role}}" data-xx=".."></div>
上面自动会构建:
new XX({
'{{element-role}}': "该 div 元素",
'xx': ...
})
目前 {{element-role}}
始终是 "element", 改成上面的方式后,就可以自定义了。
并且 data-api 也就可以存放在 widget 的非 element 元素上。
destroy中调用了undelegateEvents方法:
undelegateEvents: function(eventKey) {
var args = {};
// 卸载所有
if (arguments.length === 0) {
args.type = DELEGATE_EVENT_NS + this.cid;
} else {
args = parseEventKey(eventKey, this);
}
this.element.off(args.type, args.selector);
return this;
}
建议加上this.element非空判断
this.element&&this.element.off(args.type, args.selector);
这样在window.unload处理组件销毁时避免报错
示例
WidgetA = widget.extend({
attr:{
a: 1
},
_onRenderA: function(val) {
this.renderPartial(selector);
}
})
当实例化的时候,模版通过 parseElement 渲染后,又会调用 _onRenderA 再渲染一遍。
需求是 attr 改变后能渲染模版。因为实例化已经渲染过一次了,其实不需要再次渲染。
通常会有这种情况
widget.extend({
attrs: {
trigger: {
value: '',
getter: function(val) {
return $(val);
}
}
},
events: {
'click {{attrs.trigger}}': 'trigger_click'
}
})
但上面的代码无法绑定 click 事件,因为 trggier 获取的是 jQuery 对象,delegateEvents 里判断是字符串才会继续执行。
https://github.com/aralejs/widget/blob/master/src/widget.js#L366
像这种情况建议如何写?
还不知道是哪里问题,提在 handlebars 了,可看下这个链接
templatable 的代码中,compile 方法每次都调用 Handlebars.compile(template)(model)
,在每个实例初始化的时候都要编译一遍模板。这对于大部分情况下是很浪费CPU的,特别在ie下,批量渲染 DOM 的时候性能非常差。有改进的空间。
可以做缓存。
如题
paging 组件开发时遇到的问题,需要仔细考虑下。
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.