Git Product home page Git Product logo

Comments (38)

Justineo avatar Justineo commented on July 22, 2024

我认为只要是和用户有交互行为的控件都应该有disabled状态。之前锦囊做权限升级的时候我的做法是渲染完毕以后不响应任何用户操作触发的事件,禁用所有子控件。Label之类的控件disabled状态没任何影响,而Panel这样的控件如果在disable()时递归禁用所有子控件,我感觉还是会有用的。Panel这样的控件是否可以看成一种运行时可能会变成复杂控件的玩意儿?如果这样看,禁用它顺便把内部的其它空间也禁用似乎是很合理的。

2&3 同意 @otakustay

from esui.

otakustay avatar otakustay commented on July 22, 2024

这一点也正是比较纠结的,像Tree和其它的InputControldisabled状态是非常容易理解的,因为本身是与用户有交互的。

但是PanelDialogSidebarTable这些控件,更多的是一个“容器”,即它们的内容是由外部决定的,而非自己控制的。

原则上我比较同意容器类控件把内部所有控件都进行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.

otakustay avatar otakustay commented on July 22, 2024

现在的DOM事件都是框架管理的,那么当控件是disabledreadOnly状态时,默认不触发任何DOM事件,这个合理吗?如果这么做,多数控件就不再需要为这两个状态在各个方法中都有分支了

from esui.

Justineo avatar Justineo commented on July 22, 2024

或者说Panel本身不具有disabled状态,而只作为批量执行disable()的快捷方式?这样就不需要处理内部控件和容器本身disabled状态的同步问题了。
容器内的非控件元素我觉得就不需要交给控件来处理了。

from esui.

otakustay avatar otakustay commented on July 22, 2024

我推荐Panel有个applyAll的方法,签名如下:

{void} applyAll(methodName, arg1, arg2, ... argn)
{void} applyAll(callback)

这样可以panel.applyAll('disable')来使用,也显得通用一些

from esui.

Justineo avatar Justineo commented on July 22, 2024

还有一点,如果子元素也是Panel,那应该继续调subPanel.applyAll('disable')吧。有这种特性的控件是否局限于Panel一种?是否还需要泛化出一个基类呢?

from esui.

otakustay avatar otakustay commented on July 22, 2024

applyAll过程中,如果对象身上还有applyAll就继续调用,或者applyAll添加一个deep参数……

所以这种设计就很纠结,无论是applyAll还是disableAll都会出现这个问题,无论选择哪边都不能适应全部的场景

from esui.

errorrik avatar errorrik commented on July 22, 2024

我认为只要是和用户有交互行为的控件都应该有disabled状态。

赞同这个。

不过,我觉得,disabled切换的时候,对子控件的操作,应该是这样的:

  1. 对控件直接控制行为的子控件,其行为不触发(自己绑的listener,肯定能做到的)
  2. 只直接针对特殊子控件,做disabled状态切换(比如Calendar上面选择年月的Select)
  3. 在2后,不对其它任何子控件进行disabled状态切换

from esui.

otakustay avatar otakustay commented on July 22, 2024

赞同这个。

所以这个状态不应该在Control上,而是需要的控件自己实现(比如InputControl上有)?不然Panel这类不承载交互的会很麻烦。

对控件直接控制行为的子控件,其行为不触发(自己绑的listener,肯定能做到的)

不太理解 控件直接控制行为的子控件 是指啥,比如Dialog下的Button是不是这一类,Panel下的一个Select呢?能更精确地定义一下不,我的理解是 控件源码中会主动去访问和操作的那些子控件

只直接针对特殊子控件,做disabled状态切换(比如Calendar上面选择年月的Select)

如果一个控件自己是交互式的(比如Calendar),那么他的disabled状态切换的逻辑肯定是自己的,里面怎么做完全由实现者负责,我觉得标准不需要在这方面给限制。

from esui.

errorrik avatar errorrik commented on July 22, 2024

所以这个状态不应该在Control上,而是需要的控件自己实现(比如InputControl上有)?不然Panel这类不承载交互的会很麻烦。

不是只有InputControl才有交互的。Label也有hover state,这也是一种交互。对于Panel来说,如果不考虑,其实disabled并不会对Panel的功能带来任何影响。

如果一个控件自己是交互式的(比如Calendar),那么他的disabled状态切换的逻辑肯定是自己的,里面怎么做完全由实现者负责,我觉得标准不需要在这方面给限制。

嗯,是不给限制的。所以,控件disabled切换的时候,基类逻辑不应该对任何子控件进行disabled状态切换。


话说,在1.0的时候,是大家讨论要把disabled状态+到控件级别的。因为,绝大多数控件都有交互(没交互你做控件干嘛啊)。当然我们可以找到其他做控件的理由,比如用于可复用的视图生成

from esui.

otakustay avatar otakustay commented on July 22, 2024

不是只有InputControl才有交互的

必须的,比如TreeView可以不当InputControl用,但肯定有禁用状态,Link也是很经典的例子。

另一个方案自然是Controldisable等方法,Panel之类的作为小众,没有自己具体的实现就是了,这个我可以接受(比上次那个click事件广泛得多)。

所以,控件disabled切换的时候,基类逻辑不应该对任何子控件进行disabled状态切换

@Justineo 更多地是在说Panel这个特例怎么处理状态切换,我的意见是不管子类,要有的话使用别的方法来实现。

from esui.

errorrik avatar errorrik commented on July 22, 2024

@Justineo 更多地是在说Panel这个特例怎么处理状态切换,我的意见是不管子类,要有的话使用别的方法来实现。

我觉得也是不管。Panel又没有管子控件的自身交互逻辑。

from esui.

Justineo avatar Justineo commented on July 22, 2024

@otakustay @errorrik
似乎讨论到后面其实是两个问题:

  1. Panel这样的控件disabled状态其实没有意义,其实只需要考虑有交互的控件即可;
  2. 对于Panel这样可以作为其他控件外部容器的控件,是否可以考虑增加对其内部控件做批量处理的接口。

from esui.

errorrik avatar errorrik commented on July 22, 2024

对于Panel这样可以作为其他控件外部容器的控件,是否可以考虑增加对其内部控件做批量处理的接口。

我觉得:

  1. 批量获取控件,可以交给viewContext,或者直接拿children
  2. 批量处理获取到的控件,可以用each。话说我们的lib没有each,是否需要+?

from esui.

otakustay avatar otakustay commented on July 22, 2024

批量获取控件,可以交给viewContext,或者直接拿children

children是没有递归过程的,在children里又遇到一个Panel,还要自己控制递归,所以 @Justineo 会想要一个简便的入口方法。

批量处理获取到的控件,可以用each。话说我们的lib没有each,是否需要+?

不觉得有必要,写了个到处是遍历datasourceSelect控件,感觉没有each挺正常的

from esui.

errorrik avatar errorrik commented on July 22, 2024

拿children是没有递归过程的,在children里又遇到一个Panel,还要自己控制递归,所以 @Justineo 会想要一个简便的入口方法。

那+哪比较合适?我怎么都觉得别扭

from esui.

otakustay avatar otakustay commented on July 22, 2024

现在的DOM事件都是框架管理的,那么当控件是disabled或readOnly状态时,默认不触发任何DOM事件,这个合理吗?如果这么做,多数控件就不再需要为这两个状态在各个方法中都有分支了

希望可以重点讨论一下这个问题,会涉及到核心的部件。关于容器的disable的问题,应该算是一种添加而非修改,我们还有些时间可以认真思考。

from esui.

wurongyao avatar wurongyao commented on July 22, 2024

我觉得控件被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.

otakustay avatar otakustay commented on July 22, 2024

我觉得控件被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.

wurongyao avatar wurongyao commented on July 22, 2024

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.

otakustay avatar otakustay commented on July 22, 2024

先记录子控件原始状态到某个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.

errorrik avatar errorrik commented on July 22, 2024

现在的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.

otakustay avatar otakustay commented on July 22, 2024

这个ignoreState应该放在控件上作为某个属性更合适吧,或者控件提供一个ignore-event的state,helper只通过hasState('ignore-event')来判断是不是触发,该状态的同步由控件在add/removeState中实现

from esui.

errorrik avatar errorrik commented on July 22, 2024

我觉得控件被disabled或者enable时,它的子控件也必须同步,即使它的子控件是否disabled都不发生变化。

这点我是不赞同的。

上面的讨论大多数用的是panel。但是,panel本身disabled状态其实是没有任何意义的。

from esui.

errorrik avatar errorrik commented on July 22, 2024

这个ignoreState应该放在控件上作为某个属性更合适吧,或者控件提供一个ignore-event的state,helper只通过hasState('ignore-event')来判断是不是触发,该状态的同步由控件在add/removeState中实现

放在某个属性上更合理。但为啥是ignoreEvent,不是ignoreState?还是两个都需要?

from esui.

otakustay avatar otakustay commented on July 22, 2024

如果它是一个属性,用来 说明哪些state下事件要不触发 则叫ignoreState属性

如果它是一个状态,用来说明 有这个状态时事件不要触发 则这个状态叫ignore-state

from esui.

errorrik avatar errorrik commented on July 22, 2024

如果它是一个属性,用来 说明哪些state下事件要不触发 则叫ignoreState属性

如果它是一个状态,用来说明 有这个状态时事件不要触发 则这个状态叫ignore-state

没理解

from esui.

otakustay avatar otakustay commented on July 22, 2024

就是说有2种方案:

第一种,所有控件有一个ignoreStates属性,类型是数组,默认是['disabled'],当DOM事件触发时,遍历这个属性,如果其中有一项表示的状态这个控件有,则不触发事件

第二种,所有控件有一个叫ignore-event的状态,当disable()调用时,自动设置该状态,DOM事件触发时,检查hasState('ignore-event')如果为true则不触发事件

from esui.

errorrik avatar errorrik commented on July 22, 2024

第一种好了,更直观。我觉得无谓再做状态关联了。那ignoreEvents要有不?

from esui.

otakustay avatar otakustay commented on July 22, 2024

我在实现的过程中,发现一个问题:状态这东西,即是状态又是属性。如一个控件初始化时,可以传disabled: true让它进入禁用状态,也可以之后使用setProperties({ disabled: true }),也可以setDisabled(true),也可以addState('disabled')

这种情况导致状态属性的入口过多,甚至很难去做到同步,比如addState('disabled')的时候,就不会把disabled属性变为true,未来大范围渲染的时候极易出错。

这个事暂时没想到比较好的解决方案,个人建议如下:

  1. 添加或移除状态时,控制控件相应的属性为truefalse
  2. 所有的状态,在处理状态的方法中不做相应的操作,继续把所有任务导到repaint上,保持控件的一切核心丢在这个函数上面,以避免过多入口导致错乱,保证无论有多少入口,最终都会在一个终点相遇
  3. Control默认提供disabledhiddenpainterInputControl默认提供readOnlypainter,如Select控件则可以在默认painter之外再提供自己关于这几个属性的painter,这就要求painters模块能够让一个属性有多个painter依次处理

因为这东西太重要了,时间紧迫,保留2天,没意见的话直接实行,请 @errorrik 参考

from esui.

firede avatar firede commented on July 22, 2024

@otakustay
API设计觉得有个别扭的地方:

setDisabled(false) 参数直接传 truefalse 虽说也能理解,但若非已经习惯了,直觉上还是要想一下是禁用还是取消禁用。不如 addState('disabled') 看起来舒服。

无伤大雅的一个小问题哈,我就吐槽一下。

from esui.

errorrik avatar errorrik commented on July 22, 2024

添加或移除状态时,控制控件相应的属性为true或false

是在addState/removeState里做,还是在setDisabled里做

from esui.

otakustay avatar otakustay commented on July 22, 2024
  • addState/removeState在加上/去掉对应的class后,会调用set('$stateName', true / false)
  • disableenable调用addStateremoveState,没有其它逻辑
  • setsetProperties处理已知状态去调用addStateremoveState,并且有“值是否变化”的判断,不然会导致addState -> set -> addState的无限循环
  • repaint里处理$stateName的变化控制交互和展现(不包括加class的事)

我能想到的只有这样了

from esui.

errorrik avatar errorrik commented on July 22, 2024

@otakustay

整理了下,相比现在的流程,原先的实现里缺了:

  • addState/removeState时对set方法的调用
  • set方法里没有“值是否变化”的判断

我觉得新的流程没问题。

from esui.

errorrik avatar errorrik commented on July 22, 2024

@firede

我觉得,setDisabled方法不是给人用的,而是保持一致性用的。给人用的时disable和enable方法。

from esui.

firede avatar firede commented on July 22, 2024

setDisabled方法不是给人用的,而是保持一致性用的

那我木有问题了~

from esui.

otakustay avatar otakustay commented on July 22, 2024

set方法的值是否变化控制是有的,现在看起来加上addState/removeStateset的调用就OK,那么我先这么尝试一下

from esui.

otakustay avatar otakustay commented on July 22, 2024

85b5f3c 搞了一下,主要有:

  • addState调用setProperties,注意不能调set会挂掉
  • painters增加修改state的painter
  • helper.createRepaint支持调用父类的repaint方法

Select试了下(这货有个弹层要同时关心readOnlydisabledhidden状态),看上去没问题。

现在状态控制也统一了,关于Panel是不是调子元素的问题新开讨论更好,这个Issue到此吧

from esui.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.