【问题标题】:Knockout Inline Edit Binding淘汰赛内联编辑绑定
【发布时间】:2012-11-01 12:18:32
【问题描述】:

我去寻找一个淘汰的内联编辑绑定,但我发现的唯一一个有external dependencies,而不是 jQuery,或used more than just a binding

所以我想我会分享我想出的简单答案(当然欢迎其他答案,尤其是那些只使用淘汰赛的答案)。

【问题讨论】:

    标签: jquery knockout.js inline-editing


    【解决方案1】:

    作为替代方案:我用于内联编辑的代码如下所示:

    ko.bindingHandlers.hidden = {
        update: function(element, valueAccessor) {
            ko.bindingHandlers.visible.update(element, function() { return !ko.utils.unwrapObservable(valueAccessor()); });
        }        
    };
    
    ko.bindingHandlers.clickToEdit = {
        init: function(element, valueAccessor) {
            var observable = valueAccessor(),
                link = document.createElement("a"),
                input = document.createElement("input");
    
            element.appendChild(link);
            element.appendChild(input);
    
            observable.editing = ko.observable(false);
    
            ko.applyBindingsToNode(link, {
                text: observable,
                hidden: observable.editing,
                click: observable.editing.bind(null, true)
            });
    
            ko.applyBindingsToNode(input, {
                value: observable,
                visible: observable.editing,
                hasfocus: observable.editing
            });
        }
    };
    

    http://jsfiddle.net/rniemeyer/Rg8DM/

    【讨论】:

    • 这样更干净了,谢谢!唯一的缺点是在段落

      内,因为你没有设置宽度,它不会很好地排列。一个问题:为什么要创建一个隐藏绑定,而不仅仅是visible: !observable.editing

    • 当调用applyBindingsToNode 时,我们需要传递一个可观察/计算的对象,当绑定运行时它会被解包。如果您只是将其传递给!observable.editing(),那么它将是一个永远不会触发更改的静态值。另一种选择是在处理程序中定义一个计算值,它返回与editing 子可观察对象相反的值,例如:jsfiddle.net/rniemeyer/Rg8DM/2。是的,它对宽度没有任何作用。
    • @RPNiemeyer,有没有办法将keyup监听器合并到这个?我想让 Escape 键也取消/结束当前编辑。
    • @Kal_Torak - 我可能会选择类似:jsfiddle.net/rniemeyer/8D5aj
    • 您能否给出一些提示如何改进代码以将更新的数据发送到数据库?
    【解决方案2】:

    这是我的inline edit binding (fiddle),不过它依赖 jQuery 进行一些 DOM 操作。

    HTML:

    <p>
        Set an alarm for <span data-bind="inline: startTime"></span>
        using <span data-bind="inline: snoozeCount"></span> Snooze(s).
    </p>
    

    JS:

    ko.bindingHandlers.inline= {
        init: function(element, valueAccessor) {
            var span = $(element);
            var input = $('<input />',{'type': 'text', 'style' : 'display:none'});
            span.after(input);
    
            ko.applyBindingsToNode(input.get(0), { value: valueAccessor()});
            ko.applyBindingsToNode(span.get(0), { text: valueAccessor()});
    
            span.click(function(){
                input.width(span.width());
                span.hide();
                input.show();
                input.focus();
            });
    
            input.blur(function() { 
                span.show();
                input.hide();
            });
    
            input.keypress(function(e){
                if(e.keyCode == 13){
                   span.show();
                   input.hide();
               }; 
            });
        }
    };
    

    由于Dom Ready上的宽度不可靠,所以在click函数中设置宽度:一半时间它是0。

    我还为切换(布尔值)制作了一个,您只需单击即可切换:

    ko.bindingHandlers.inlineToggle = {
        init: function(element, valueAccessor, allBindingsAccessor) {
    
            var displayType = allBindingsAccessor().type || "bool";
            var displayArray = [];
    
            if (displayType == "bool") {
                displayArray = ["True", "False"];
            } else if (displayType == "on") {
                displayArray = ["On", "Off"];
            } else {
               displayArray = displayType.split("/");
            } 
    
            var target = valueAccessor();  
            var observable = valueAccessor()
            var interceptor = ko.computed(function() {
                return observable() ? displayArray[0] : displayArray[1];
            });
    
            ko.applyBindingsToNode(element, { text: interceptor });
            ko.applyBindingsToNode(element, { click: function(){ target (!target())} });
        }
    };
    

    像这样应用(第二个参数是可选的):

    <span data-bind="inlineToggle: alert, type: 'on'"></span>
    

    小提琴还包含selectmulti-select 的一个,但现在多选会导致显示跳转。我需要解决这个问题。

    【讨论】:

      【解决方案3】:

      尽管答案已经被接受,但我相信我找到了更好的解决方案,所以我想分享它。

      我最终使用了 http://knockoutjs.com/documentation/custom-bindings-controlling-descendant-bindings.html#example-supplying-additional-values-to-descendant-bindings 此处文档中的内容

      并提出了这个装订草稿:

      ko.bindingHandlers['textinlineeditor'] = {
          init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
      
              var observable = valueAccessor();
              var value = ko.utils.unwrapObservable(observable);
      
              var saveHandler = allBindingsAccessor().editorsavehandler || function (newValue) { observable(newValue); return true; };
              var inputType = allBindingsAccessor().editorinputtype || "text";            
      
              var vm = new inlineEditorViewModel({ val: value, saveHandler: saveHandler });
      
              $(element).append("<span data-bind=\"text: editableValue, hidden: editing\"></span>");
              $(element).append("<input type=\"" + inputType + "\" data-bind=\"value: editableValue, visible:editing\"/>");
              $(element).append("<div class=\"pull-right\"></div>");
              $(element).find("div.pull-right").append("<span class=\"glyphicon glyphicon-edit\" aria-hidden=\"true\" data-bind=\"click: edit, hidden: editing\"></span>");
              $(element).find("div.pull-right").append("<span class=\"glyphicon glyphicon-check\" aria-hidden=\"true\" data-bind=\"click: save, visible:editing\"></span>");
              $(element).find("div.pull-right").append("<span class=\"glyphicon glyphicon-remove-circle\" aria-hidden=\"true\" data-bind=\"click: cancelSave, visible:editing\"></span>");
      
              var innerBindingContext = bindingContext.extend(vm);
              ko.applyBindingsToDescendants(innerBindingContext, element);
      
              return { controlsDescendantBindings: true };
          }
      };
      

      我在代码中使用的模型是

      var inlineEditorViewModel = function (init) {
          var self = this;
      
          self.editableValue = ko.observable(init.val);
          self.lastEditableValue = ko.observable(init.val);
      
          self.editing = ko.observable(false);
      
          self.edit = function () {
              self.editing(true);
          };
      
          self.save = function () {
              if (init.saveHandler(self.editableValue())) {
                  self.lastEditableValue(self.editableValue());
                  self.editing(false);
              }
          };
      
          self.cancelSave = function () {
              self.editableValue(self.lastEditableValue());
              self.editing(false);
          };
      };
      

      在您的 html 中,您会像使用它一样

      <div data-bind="textinlineeditor: name, editorsavehandler: saveName"></div>
      

      注意:我使用的是 Bootstrap,所以这是绑定 html 中的图标的来源。

      【讨论】:

      • 与其他解决方案相比,您更喜欢这个怎么样?我认为必须指定保存处理程序会使这更加耦合,而且我看不出它会给你带来什么。
      • 我明白你在说什么。我想对于我的问题,这个解决方案最有意义,因为用户将单独编辑和保存字段,而不是进行更改,然后一次保存表单。我的意思是我更喜欢的部分是在显示和隐藏元素方面使用敲除而不是 jQuery 进行 DOM 操作。
      • 您也可以很容易地将保存处理程序设置为仅更新可观察对象的函数。
      • 接受的答案不使用 jQuery。每个 observable 的订阅将允许您响应它们被更改而无需保存处理程序;常见用例(仅更新可观察对象)不需要。我认为保存处理程序是一个明确的缺点。我看不出有什么好处。
      • 我更新了示例,所以默认行为是更新 observable。是的,就像您说的那样,您现在可以添加订阅以观察 observable 的变化。我不确定我是否理解为什么选择处理保存的函数是一个缺点?在我使用它的方式中,我在用户尝试保存时进行验证,如果该字段无效,我想保持该字段可编辑。你有更好的建议来使用订阅吗?
      猜你喜欢
      • 2011-12-15
      • 1970-01-01
      • 2013-02-04
      • 2013-10-31
      • 2013-08-14
      • 1970-01-01
      • 1970-01-01
      • 2014-04-08
      • 1970-01-01
      相关资源
      最近更新 更多