94

我正在构建权限 UI,我有一个权限列表,每个权限旁边都有一个选择列表。权限由绑定到选择列表的可观察对象数组表示:

<div data-bind="foreach: permissions">
     <div class="permission_row">
          <span data-bind="text: name"></span>
          <select data-bind="value: level, event:{ change: $parent.permissionChanged}">
                   <option value="0"></option>
                   <option value="1">R</option>
                   <option value="2">RW</option>
           </select>
      </div>
 </div>

现在的问题是:第一次填充 UI 时会引发 change 事件。我调用我的 ajax 函数,获取权限列表,然后为每个权限项引发事件。这真的不是我想要的行为。我希望仅当用户真正在选择列表中为权限选择新值时才提出它,我该怎么做?

4

11 回答 11

120

实际上你想知道事件是由用户还是程序触发的,很明显该事件会在初始化时触发。

添加的淘汰赛方法subscription在所有情况下都没有帮助,为什么因为在大多数模型中都会这样实现

  1. 用未定义的数据初始化模型,只是结构 (actual KO initilization)
  2. 使用初始数据更新模型 (logical init like load JSON , get data etc)
  3. 用户交互和更新

我们要捕获的实际步骤是 3 中的更改,但在第二步subscription中会调用,因此更好的方法是添加到事件更改中,例如

 <select data-bind="value: level, event:{ change: $parent.permissionChanged}">

并检测到permissionChanged函数中的事件

this.permissionChanged = function (obj, event) {

  if (event.originalEvent) { //user changed

  } else { // program changed

  }

}
于 2013-12-05T10:35:48.810 回答
32

这只是一个猜测,但我认为它正在发生,因为它level是一个数字。在这种情况下,value绑定将触发一个change事件以level使用字符串值进行更新。因此,您可以通过确保level以字符串开头来解决此问题。

此外,更“淘汰”的做法是不使用事件处理程序,而是使用可观察对象和订阅。level创建一个可观察对象,然后为其添加订阅,该订阅将在更改时运行level

于 2012-06-20T01:51:00.930 回答
4

这是一个可能有助于解决这种奇怪行为的解决方案。我找不到比放置一个按钮来手动触发更改事件更好的解决方案。

编辑:也许像这样的自定义绑定可能会有所帮助:

ko.bindingHandlers.changeSelectValue = {

   init: function(element,valueAccessor){

        $(element).change(function(){

            var value = $(element).val();

            if($(element).is(":focus")){

                  //Do whatever you want with the new value
            }

        });

    }
  };

并在您的选择数据绑定属性中添加:

changeSelectValue: yourSelectValue
于 2012-06-18T09:04:38.750 回答
3

我使用这个自定义绑定(基于RP Niemeyer 的这个小提琴,请参阅他对这个问题的回答),它确保数值从字符串正确转换为数字(如 Michael Best 的解决方案所建议的那样):

Javascript:

ko.bindingHandlers.valueAsNumber = {
    init: function (element, valueAccessor, allBindingsAccessor) {
        var observable = valueAccessor(),
            interceptor = ko.computed({
                read: function () {
                    var val = ko.utils.unwrapObservable(observable);
                    return (observable() ? observable().toString() : observable());
                },
                write: function (newValue) {
                    observable(newValue ? parseInt(newValue, 10) : newValue);
                },
                owner: this
            });
        ko.applyBindingsToNode(element, { value: interceptor });
    }
};

示例 HTML:

<select data-bind="valueAsNumber: level, event:{ change: $parent.permissionChanged }">
    <option value="0"></option>
    <option value="1">R</option>
    <option value="2">RW</option>
</select>
于 2013-06-13T10:12:25.830 回答
2

如果您使用可观察对象而不是原始值,则选择不会在初始绑定时引发更改事件。你可以继续绑定到 change 事件,而不是直接订阅 observable。

于 2013-01-25T17:51:01.333 回答
1

快速而肮脏,使用一个简单的标志:

var bindingsApplied = false;

var ViewModel = function() {
    // ...

    this.permissionChanged = function() {
        // ignore, if flag not set
        if (!flag) return;

        // ...
    };
};

ko.applyBindings(new ViewModel());
bindingsApplied = true; // done with the initial population, set flag to true

如果这不起作用,请尝试将最后一行包装在 setTimeout() 中 - 事件是异步的,因此当 applyBindings() 已经返回时,最后一个可能仍然处于挂起状态。

于 2012-06-18T07:11:56.767 回答
0

我有一个类似的问题,我只是修改了事件处理程序来检查变量的类型。类型仅在用户选择一个值后设置,而不是在页面首次加载时设置。

self.permissionChanged = function (l) {
    if (typeof l != 'undefined') {
        ...
    }
}

这似乎对我有用。

于 2014-05-30T15:41:13.107 回答
0

试试这个:

self.GetHierarchyNodeList = function (data, index, event)
{
    debugger;
    if (event.type != "change") {
        return;
    }        
} 

event.type == "change"
event.type == "load" 
于 2020-01-30T09:52:02.330 回答
-1

如果您正在使用 Knockout,请使用可观察功能敲除的关键功能。
在该函数中使用ko.computed()方法并执行和触发 ajax 调用。

于 2017-04-26T13:35:05.640 回答
-1

用这个:

this.permissionChanged = function (obj, event) {

    if (event.type != "load") {

    } 
}
于 2019-05-17T12:36:47.173 回答
-1

创建js组件

define([
    'Magento_Ui/js/form/element/select',
    'mage/translate'
], function (AbstractField, $t) {
    'use strict';

    return AbstractField.extend({
        defaults: {
            imports: {
                update: 'checkout.steps.shipping-step.shippingAddress.shipping-address-fieldset.country_id:value'
            },
            modules: {
                vat_id: '${ $.parentName }.vat_id'


            }
        },

        /**
         * Initializes UISelect component.
         *
         * @returns {UISelect} Chainable.
         */
        initialize: function () {
            this._super();
            this.vat_id().visible(false);
            return this;
        },
        update: function (value) {
            if(value == 'GB'){
                this.vat_id().visible(true);
            }else{
                this.vat_id().visible(false);

            }
        }
        
        
            
         
      
    });
});
于 2021-01-20T12:49:15.117 回答