Comments (38)
我认为只要是和用户有交互行为的控件都应该有disabled
状态。之前锦囊做权限升级的时候我的做法是渲染完毕以后不响应任何用户操作触发的事件,禁用所有子控件。Label
之类的控件disabled
状态没任何影响,而Panel
这样的控件如果在disable()
时递归禁用所有子控件,我感觉还是会有用的。Panel
这样的控件是否可以看成一种运行时可能会变成复杂控件的玩意儿?如果这样看,禁用它顺便把内部的其它空间也禁用似乎是很合理的。
2&3 同意 @otakustay。
from esui.
这一点也正是比较纠结的,像Tree
和其它的InputControl
有disabled
状态是非常容易理解的,因为本身是与用户有交互的。
但是Panel
、Dialog
、Sidebar
、Table
这些控件,更多的是一个“容器”,即它们的内容是由外部决定的,而非自己控制的。
原则上我比较同意容器类控件把内部所有控件都进行disabled
,但这里显然还有几个问题:
控件和非控件
<div data-ui="type: Panel;">
<a href="#/user/list">用户列表</a>
</div>
当这个Panel
进入disabled
状态后,里面的链接是否应该也disabled
,这里的链接是个普通的HTML元素,不是Link
元素。这可能导致 内部的元素是不是控件将产生不同的行为 ,也是很难一致化的问题。
取消disabled时的行为
<div data-ui="type: Panel;">
<a data-ui="type: Link; disabled: true;" href="#/user/list">用户列表</a>
</div>
一个子控件原本就是disabled
状态,父控件进行disabled
后再enable()
,则这个子控件应该在什么状态呢?
子控件状态
当一个父控件在disabled
状态时:
- 里面的子控件是否允许
enable()
呢? - 新加入到父控件的子控件,默认是否要进入
disabled
状态呢?
前面这些问题,我觉得无论选择怎么样的方案,都只能满足部分的业务场景,基本很难有一个完美的方法……因此我才从实践的角度认为不如不去管父子状态的同步
from esui.
现在的DOM事件都是框架管理的,那么当控件是disabled
或readOnly
状态时,默认不触发任何DOM事件,这个合理吗?如果这么做,多数控件就不再需要为这两个状态在各个方法中都有分支了
from esui.
或者说Panel
本身不具有disabled
状态,而只作为批量执行disable()
的快捷方式?这样就不需要处理内部控件和容器本身disabled
状态的同步问题了。
容器内的非控件元素我觉得就不需要交给控件来处理了。
from esui.
我推荐Panel
有个applyAll
的方法,签名如下:
{void} applyAll(methodName, arg1, arg2, ... argn)
{void} applyAll(callback)
这样可以panel.applyAll('disable')
来使用,也显得通用一些
from esui.
还有一点,如果子元素也是Panel
,那应该继续调subPanel.applyAll('disable')
吧。有这种特性的控件是否局限于Panel
一种?是否还需要泛化出一个基类呢?
from esui.
applyAll
过程中,如果对象身上还有applyAll
就继续调用,或者applyAll
添加一个deep
参数……
所以这种设计就很纠结,无论是applyAll
还是disableAll
都会出现这个问题,无论选择哪边都不能适应全部的场景
from esui.
我认为只要是和用户有交互行为的控件都应该有disabled状态。
赞同这个。
不过,我觉得,disabled切换的时候,对子控件的操作,应该是这样的:
- 对控件直接控制行为的子控件,其行为不触发(自己绑的listener,肯定能做到的)
- 只直接针对特殊子控件,做disabled状态切换(比如Calendar上面选择年月的Select)
- 在2后,不对其它任何子控件进行disabled状态切换
from esui.
赞同这个。
所以这个状态不应该在Control
上,而是需要的控件自己实现(比如InputControl
上有)?不然Panel
这类不承载交互的会很麻烦。
对控件直接控制行为的子控件,其行为不触发(自己绑的listener,肯定能做到的)
不太理解 控件直接控制行为的子控件 是指啥,比如Dialog
下的Button
是不是这一类,Panel
下的一个Select
呢?能更精确地定义一下不,我的理解是 控件源码中会主动去访问和操作的那些子控件
只直接针对特殊子控件,做disabled状态切换(比如Calendar上面选择年月的Select)
如果一个控件自己是交互式的(比如Calendar
),那么他的disabled
状态切换的逻辑肯定是自己的,里面怎么做完全由实现者负责,我觉得标准不需要在这方面给限制。
from esui.
所以这个状态不应该在Control上,而是需要的控件自己实现(比如InputControl上有)?不然Panel这类不承载交互的会很麻烦。
不是只有InputControl才有交互的。Label也有hover state,这也是一种交互。对于Panel来说,如果不考虑,其实disabled并不会对Panel的功能带来任何影响。
如果一个控件自己是交互式的(比如Calendar),那么他的disabled状态切换的逻辑肯定是自己的,里面怎么做完全由实现者负责,我觉得标准不需要在这方面给限制。
嗯,是不给限制的。所以,控件disabled切换的时候,基类逻辑不应该对任何子控件进行disabled状态切换。
话说,在1.0的时候,是大家讨论要把disabled状态+到控件级别的。因为,绝大多数控件都有交互(没交互你做控件干嘛啊)。当然我们可以找到其他做控件的理由,比如用于可复用的视图生成
from esui.
不是只有InputControl才有交互的
必须的,比如TreeView
可以不当InputControl
用,但肯定有禁用状态,Link
也是很经典的例子。
另一个方案自然是Control
有disable
等方法,Panel
之类的作为小众,没有自己具体的实现就是了,这个我可以接受(比上次那个click事件广泛得多)。
所以,控件disabled切换的时候,基类逻辑不应该对任何子控件进行disabled状态切换
@Justineo 更多地是在说Panel
这个特例怎么处理状态切换,我的意见是不管子类,要有的话使用别的方法来实现。
from esui.
@Justineo 更多地是在说Panel这个特例怎么处理状态切换,我的意见是不管子类,要有的话使用别的方法来实现。
我觉得也是不管。Panel又没有管子控件的自身交互逻辑。
from esui.
@otakustay @errorrik
似乎讨论到后面其实是两个问题:
Panel
这样的控件disabled
状态其实没有意义,其实只需要考虑有交互的控件即可;- 对于
Panel
这样可以作为其他控件外部容器的控件,是否可以考虑增加对其内部控件做批量处理的接口。
from esui.
对于Panel这样可以作为其他控件外部容器的控件,是否可以考虑增加对其内部控件做批量处理的接口。
我觉得:
- 批量获取控件,可以交给viewContext,或者直接拿children
- 批量处理获取到的控件,可以用each。话说我们的lib没有each,是否需要+?
from esui.
批量获取控件,可以交给viewContext,或者直接拿children
拿children
是没有递归过程的,在children
里又遇到一个Panel
,还要自己控制递归,所以 @Justineo 会想要一个简便的入口方法。
批量处理获取到的控件,可以用each。话说我们的lib没有each,是否需要+?
不觉得有必要,写了个到处是遍历datasource
的Select
控件,感觉没有each
挺正常的
from esui.
拿children是没有递归过程的,在children里又遇到一个Panel,还要自己控制递归,所以 @Justineo 会想要一个简便的入口方法。
那+哪比较合适?我怎么都觉得别扭
from esui.
现在的DOM事件都是框架管理的,那么当控件是disabled或readOnly状态时,默认不触发任何DOM事件,这个合理吗?如果这么做,多数控件就不再需要为这两个状态在各个方法中都有分支了
希望可以重点讨论一下这个问题,会涉及到核心的部件。关于容器的disable
的问题,应该算是一种添加而非修改,我们还有些时间可以认真思考。
from esui.
我觉得控件被disabled或者enable时,它的子控件也必须同步,即使它的子控件是否disabled都不发生变化。
可以交给contrl的setDisabled,由它来做,enable 和 disable都由它来。
setProperties也需要对disable做特殊处理,让它调用setDisabled
(现在是反过来,setDisabled调用setProperties)。
setDisabled方法除了改变控件自己的状态,还需要修改控件的子控件的状态,
修改的方式也是调用子控件的setDisabled,然后就这样递归完成了。
一般情况下控件不需要去覆盖setDisabled方法,除非它要自己控制自己的子控件状态。
(如某些控件希望自己enable时,不直接enable子控件,而是由某种逻辑来判断子控件是否enable)
对于事件,disabled状态直接决定是否触发我觉得是合理的,至于触发后是否做事,则由handler自己来处理。
我觉得不应该区分控件类型,控件自己区分自己,它觉得它特殊就覆盖setDisabled。
任何控件都可以有disabled状态,但是在这个状态下控件的行为是否发生变化,则由控件自己决定。
至于控件和非控件的问题,如果是控件开发者引入的,他就需要去考虑这个问题。
如果希望自动disabled,就使用子控件,如果子控件太多添加麻烦,而这些东西又都是字符串拼出来的,
那控件开发者则需要自己处理,自己在字符串中加。统一约定一下就好。
最麻烦的是用户引入的非控件,控件开发者控制不了,如table中的常见的编辑,删除链接。
如果用户使用的是控件提供给它的绑定方式,如使用控件的事件委托,或者使用Command,控件则依然可以控制。
我觉得做到这里就可以,如果用户就是不用,也不自己去disable,那也没有办法。
from esui.
我觉得控件被disabled或者enable时,它的子控件也必须同步,即使它的子控件是否disabled都不发生变化。
所以前面提的一个问题还是没办法解决,控件结构如下:
- Panel (enabled)
- TextBox (disabled)
- Select (enabled)
使用代码:
panel.disable();
panel.enable();
在这样的操作之后,上面的那个TextBox
应该处于什么样的状态,enabled
吗?
现在是反过来,setDisabled调用setProperties
其实这是我改出来的,因为我想所有的东西最终都到repaint
那里去,比如下面的代码:
var dialog = new Dialog({ ... });
dialog.appendTo(container);
dialog.setProperties({ title: 'new title', disabled: true });
如果disabled
状态最后由setDisabled
接管,则可能title
的修改和disabled
状态的修改不能使用一次DOM的reflow完成(无论如何setDisabled
是独立的,单独造成一次属性、样式的修改,造成一次reflow)。
当然对于多数控件来说,性能上是没有区别的,repaint
是对disabled
的处理也是加class和属性。只有Dialog
这种比较复杂的控件,对于多数属性的变更是直接重新拼HTML串来实现的,才会有reflow数量的影响。
综上,我认同让状态的同步回到setDisabled
统一处理,有特殊需求的控件自己把setDisabled
重写导回到repaint
去得了。
对于事件,disabled状态直接决定是否触发我觉得是合理的,至于触发后是否做事,则由handler自己来处理。
这句话感觉不是很理解,如果disabled
状态决定触发事件,则禁用的控件不会触发任何DOM事件(addDOMEvent
接管),则后面的“触发后是否做事”就不需要处理了。
最麻烦的是用户引入的非控件,控件开发者控制不了,如table中的常见的编辑,删除链接。
如Table
中的<a>
元素,这是一个非常典型的案例。当Table
控件被禁用时,事实上这些<a>
元素应该加上disabled="disabled"
属性,但遗憾的是现在的设计下,用户没有办法加。
在使用控件后,我们的原则是不让用户接触控件管理的DOM,以避免发生混乱,因此才会有Command
这样的设计。那么,当一个<a>
元素被Table
管理后,用户不应该直接访问(通过getElementsByTagName
之类的)它并修改它的属性。然而除此之外,还有什么办法去修改它呢?
我希望有这样一个设计,在状态改变的时候会触发事件,这样用户自然可以通过监听事件来处理状态的切换。但即便如此,在发现控件被禁用时,由于不能去访问main
元素,也无法从中找到对应的<a>
元素,无法加上属性,依旧无能为力。
这个问题如何解决,我觉得可以继续深入探讨(事实上这也是为了效率,控件没有形成完整的树模型加上TableRow
之类的而造成的,是一种不得以的取舍)。
from esui.
Panel的子控件状态问题,我觉得覆盖Panel的setDisabled可以做到。
我不确定Panel子控件处于一种什么状态比较合理,先假设下面的逻辑是合理的。
状态控制逻辑:Panel disable的时候,它的子控件全部disable,它enable的时候,控件恢复原始状态。
为了实现上面的状态控制逻辑,在Panel的setDisabled方法中做个判断,
当自己被Disabled的时候,先记录子控件原始状态到某个map里,然后全部设置为disabled
当自己被Enable的时候,先看一下那个map里有没有东西,
没有就什么都不做,有就按照原始状态给子控件分别设置状态值。
对于事件,disabled状态直接决定是否触发我觉得是合理的,至于触发后是否做事,则由handler自己来处理。
关于这一点,我解释一下,我认为Disabled的时候,不触发事件是合理的。
但是Enable的时候呢,如果这时候某些子控件依旧是Disabled状态,是否触发事件?
我觉得还是触发,反正父控件通知到子控件,这时候,Disabled状态的判断应该放在handler里。
当然,这种子控件把事件绑定到父控件的情况应该很少。可能Calendar中的Select是这样。
from esui.
先记录子控件原始状态到某个map里,然后全部设置为disabled
这个的代价挺大的,还要考虑动态加入的控件,且动态加入的控件的禁用状态是否和当前状态同步,如这样的结构
- Panel (panel1)
- Panel (panel2)
- TextBox (textbox)
以下代码:
panel1.disable(); // panel2和textbox全禁用了
panel2.addChild(new Select());
新加的这个Select
要不要禁用呢?
我们可以做禁用状态的递归同步,但前提是对这些问题有一个统一的标准。
关于这一点,我解释一下,我认为Disabled的时候,不触发事件是合理的。
但是Enable的时候呢,如果这时候某些子控件依旧是Disabled状态,是否触发事件?
事件会分为控件事件和DOM事件。我所提的是DOM事件的部分,当一个控件有一个子控件时,它一定不会去触发子控件的DOM事件,因为控件的DOM是归自己管理的,外部不应该知道。
父控件有可能触发子控件的控件事件(但强烈不建议这么做,一般有这需求的时候你的设计就是有问题的),但控件事件我认为不应该在禁用时默认不触发。如TextBox
在禁用时,通过setValue
改变值之后,应当会触发change
事件。
from esui.
现在的DOM事件都是框架管理的,那么当控件是disabled或readOnly状态时,默认不触发任何DOM事件,这个合理吗?如果这么做,多数控件就不再需要为这两个状态在各个方法中都有分支了
希望可以重点讨论一下这个问题,会涉及到核心的部件。关于容器的disable的问题,应该算是一种添加而非修改,我们还有些时间可以认真思考。
我觉得,这个事情可以做。但是,disabled可以由公共方法控制不触发任何dom事件,readOnly不行。原因是:disabled是所有控件都具有的方法,但readOnly不是。
所以,我觉得这个esui层面的事件绑定公共方法可以放在controlHelper上:
// element默认为控件的main
// ignoreState默认为['disabled']
controlHelper.addDOMEventListener({Control}control, {Function} listener, {HTMLElement=} main, {Array=} ignoreState)
from esui.
这个ignoreState
应该放在控件上作为某个属性更合适吧,或者控件提供一个ignore-event
的state,helper
只通过hasState('ignore-event')
来判断是不是触发,该状态的同步由控件在add/removeState
中实现
from esui.
我觉得控件被disabled或者enable时,它的子控件也必须同步,即使它的子控件是否disabled都不发生变化。
这点我是不赞同的。
上面的讨论大多数用的是panel。但是,panel本身disabled状态其实是没有任何意义的。
from esui.
这个ignoreState应该放在控件上作为某个属性更合适吧,或者控件提供一个ignore-event的state,helper只通过hasState('ignore-event')来判断是不是触发,该状态的同步由控件在add/removeState中实现
放在某个属性上更合理。但为啥是ignoreEvent,不是ignoreState?还是两个都需要?
from esui.
如果它是一个属性,用来 说明哪些state下事件要不触发 则叫ignoreState
属性
如果它是一个状态,用来说明 有这个状态时事件不要触发 则这个状态叫ignore-state
from esui.
如果它是一个属性,用来 说明哪些state下事件要不触发 则叫ignoreState属性
如果它是一个状态,用来说明 有这个状态时事件不要触发 则这个状态叫ignore-state
没理解
from esui.
就是说有2种方案:
第一种,所有控件有一个ignoreStates
属性,类型是数组,默认是['disabled']
,当DOM事件触发时,遍历这个属性,如果其中有一项表示的状态这个控件有,则不触发事件
第二种,所有控件有一个叫ignore-event
的状态,当disable()
调用时,自动设置该状态,DOM事件触发时,检查hasState('ignore-event')
如果为true
则不触发事件
from esui.
第一种好了,更直观。我觉得无谓再做状态关联了。那ignoreEvents要有不?
from esui.
我在实现的过程中,发现一个问题:状态这东西,即是状态又是属性。如一个控件初始化时,可以传disabled: true
让它进入禁用状态,也可以之后使用setProperties({ disabled: true })
,也可以setDisabled(true)
,也可以addState('disabled')
这种情况导致状态属性的入口过多,甚至很难去做到同步,比如addState('disabled')
的时候,就不会把disabled
属性变为true
,未来大范围渲染的时候极易出错。
这个事暂时没想到比较好的解决方案,个人建议如下:
- 添加或移除状态时,控制控件相应的属性为
true
或false
- 所有的状态,在处理状态的方法中不做相应的操作,继续把所有任务导到
repaint
上,保持控件的一切核心丢在这个函数上面,以避免过多入口导致错乱,保证无论有多少入口,最终都会在一个终点相遇 Control
默认提供disabled
和hidden
的painter
,InputControl
默认提供readOnly
的painter
,如Select
控件则可以在默认painter
之外再提供自己关于这几个属性的painter
,这就要求painters
模块能够让一个属性有多个painter
依次处理
因为这东西太重要了,时间紧迫,保留2天,没意见的话直接实行,请 @errorrik 参考
from esui.
@otakustay
API设计觉得有个别扭的地方:
setDisabled(false)
参数直接传 true
或 false
虽说也能理解,但若非已经习惯了,直觉上还是要想一下是禁用还是取消禁用。不如 addState('disabled')
看起来舒服。
无伤大雅的一个小问题哈,我就吐槽一下。
from esui.
添加或移除状态时,控制控件相应的属性为true或false
是在addState/removeState里做,还是在setDisabled里做
?
from esui.
addState/removeState
在加上/去掉对应的class后,会调用set('$stateName', true / false)
disable
和enable
调用addState
和removeState
,没有其它逻辑set
和setProperties
处理已知状态去调用addState
和removeState
,并且有“值是否变化”的判断,不然会导致addState
->set
->addState
的无限循环repaint
里处理$stateName
的变化控制交互和展现(不包括加class的事)
我能想到的只有这样了
from esui.
整理了下,相比现在的流程,原先的实现里缺了:
- addState/removeState时对set方法的调用
- set方法里没有“值是否变化”的判断
我觉得新的流程没问题。
from esui.
我觉得,setDisabled方法不是给人用的,而是保持一致性用的。给人用的时disable和enable方法。
from esui.
setDisabled方法不是给人用的,而是保持一致性用的
那我木有问题了~
from esui.
set
方法的值是否变化控制是有的,现在看起来加上addState/removeState
对set
的调用就OK,那么我先这么尝试一下
from esui.
在 85b5f3c 搞了一下,主要有:
addState
调用setProperties
,注意不能调set
会挂掉painters
增加修改state的painter
helper.createRepaint
支持调用父类的repaint
方法
用Select
试了下(这货有个弹层要同时关心readOnly
、disabled
、hidden
状态),看上去没问题。
现在状态控制也统一了,关于Panel
是不是调子元素的问题新开讨论更好,这个Issue到此吧
from esui.
Related Issues (20)
- 日程投放控件 HOT 4
- InputCollection 应当继承 ControlCollection HOT 1
- Table 的 overflowX 属性为非 hidden 的时候多出一个横向滚动条 HOT 3
- BoxGroup有一处事件没使用addDOMEvent绑定
- 解决set和setProperties触发change的问题
- Table的依赖不全
- 控件初始化子控件时的valueReplacer管理
- 控件初始化子控件时的valueReplacer管理
- 控件初始化子控件时的valueReplacer管理
- 指定元素的销毁子控件
- Select 控件对于value比较判断的兼容性问题 HOT 2
- 希望能添加一些布局相关的组件 HOT 4
- 对于带有数据源的控件是否应该支持外部不提供数据源的场景的表决 HOT 19
- addChild的时候添加校验 HOT 6
- viewContext的疑问 HOT 3
- 加个Lisence HOT 1
- MonthView的年月下拉框格式可调 HOT 1
- 渐变背景的问题
- Panel控件的addContent方法不适用table布局 HOT 2
- 关于拓展组件
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from esui.