【问题标题】:Knockout: Best way to bind visibility to both item and a parent property?淘汰赛:将可见性绑定到项目和父属性的最佳方式?
【发布时间】:2015-02-09 23:19:42
【问题描述】:

我正在创建一个编辑屏幕,我希望人们在其中从列表中删除项目。列表正常显示,直到“控制器”对象进入编辑模式。然后用户可以删除项目。项目应该被标记为删除并显示,然后当用户保存编辑时,它们被删除并通知服务器。

我实际上已经完成了这一切,但我能做到的唯一方法是在绑定中使用文字条件,这看起来很难看,我不太喜欢。有没有更好的方法?

工作小提琴:http://jsfiddle.net/L1e7zwyv/

标记:

<div id="test">
        <a data-bind="visible: IsViewMode, click: edit">Edit</a>
        <a data-bind="visible: IsEditMode, click: cancel">Cancel</a>
        <hr/>
        <ul data-bind="foreach: Items">
                <li data-bind="css: CssClass">
                        <span data-bind="visible: $parent.IsViewMode() || $data._Deleting(), text: Value"></span>
                        <!-- ko if: $parent.IsEditMode() && !$data._Deleting() -->
                                <input type="text" data-bind="value: Value"/>
                                <a data-bind="click: $parent.deleteItem">Del</a>
                        <!-- /ko -->
                </li>
        </ul>
</div>

代码:

function ItemModel(val)
{
        var _this = this;

        this.Value = ko.observable(val);
        this._Deleting = ko.observable();

        this.CssClass = ko.computed(
                function()
                {
                        return _this._Deleting() ? 'deleting' : '';
                }
        );
}

function ManagerModel()
{
        var _this = this;

        this.Items = ko.observableArray([
                new ItemModel('Hell'),
                new ItemModel('Broke'),
                new ItemModel('Luce')
        ]);

        this.IsEditMode = ko.observable();
        this.IsViewMode = ko.computed(function() { return !_this.IsEditMode(); });

        this.edit = function(model, e)
        {
                this.IsEditMode(true);
        };

        this.cancel = function(model, e)
        {
                for(var i = 0; i < _this.Items().length; i++)
                        _this.Items()[i]._Deleting(false);

                this.IsEditMode(false);
        };

        this.deleteItem = function(model, e)
        {
                model._Deleting(true);
        };
}

ko.applyBindings(new ManagerModel(), document.getElementById('test'));

【问题讨论】:

    标签: javascript knockout.js


    【解决方案1】:

    你可以:

    • 环绕另一个跨度来分隔绑定,但这会降低效率。
    • 在同一元素上同时使用visible:if: 绑定来实现相同的功能,
    • 在 itemModel isVisible() 上编写一个函数,接受父级作为参数,使您的绑定 visible: $data.isVisible($parent)
    • 事后思考:如果这出现在多个地方,您可以编写一个辅助函数来组合可见性绑定
      // reprisent variables from models
      var v1 = false;
      var v2 = false;
      var v3 = false;
      
      
      // Helper functions defined in main script body - globally accessible
      function VisibilityFromAny() {
        var result = false;
        for(var i = 0; i < arguments.length; i++ ) result |= arguments[i];
        return Boolean(result);
      }
      
      function VisibilityFromAll() {
        var result = true;
        for(var i = 0; i < arguments.length; i++ ) result &= arguments[i];
        return Boolean(result);
      }
      
      
      // represent bindings
      alert(VisibilityFromAny(v1, v2, v3));
      alert(VisibilityFromAll(v1, v2, v3));

    第三个选项是像你这样的 MVVM 爱好者最流行的技术,用于在单个绑定中组合变量,从我所见,它是有意义的,并且使所有逻辑远离视图模型中的视图标记。

    就我个人而言,我喜欢您目前使用的语法,(尽管我也将自己算作 MVVM 狂热团伙)这在视图标记中清楚地表明该元素的可见性绑定到 2 个项目,而不是隐藏这些细节在一个函数中。

    【讨论】:

    • 在同一个元素上的可见绑定和 if 绑定是一个有趣的想法,但我不能同时使用“if”和“text”,所以需要进行一些更改,我认为你是对的,除了我所拥有的东西之外,任何东西都会严重地掩盖显示逻辑。我确实想到了使用几个 条件的多个条件元素,但出于类似的原因放弃了这个想法。这是迄今为止最有用的答案。
    【解决方案2】:

    我尝试将视图模型视为我的视图的模型,而不仅仅是逻辑所在的地方。如果可能的话,我还会尝试将复杂的逻辑移回视图模型并为我的变量使用描述性名称,这样代码就更具可读性。

    我建议将此添加到您的视图模型中 -

    var isViewable = ko.computed(function () { return IsViewMode() || _Deleting(); });
    var isEditable = ko.computed(function() { return IsEditMode() && !_Deleting(); });
    

    在你看来——

    <li data-bind="css: CssClass">
            <span data-bind="visible: isViewable, text: Value"></span>
            <!-- ko if: isEditable -->
                    <input type="text" data-bind="value: Value"/>
                    <a data-bind="click: $parent.deleteItem">Del</a>
            <!-- /ko -->
    </li>
    

    这会清理绑定并允许您更轻松地调整逻辑,而无需在视图和视图模型中进行许多健全性检查。此外,我个人将返回布尔值的变量命名为 isWhatever,以帮助更具描述性。

    好处是,随着您的视图和视图模型变得越来越大,您可以保持 DOM 整洁,并且您的视图模型也变得可测试

    这是您添加的小提琴的“代码完整”版本 -

    http://jsfiddle.net/L1e7zwyv/3/

    【讨论】:

    • 我不认为您可以直接从视图模型中引用 $parent/$data 变量?我的任何项目都没有升级到 KO3.0
    • @Dead.Rabit 是的,我们无法访问这些,除非我们使用绑定处理程序,据我所知,我们可以通过 bindingcontext 访问。
    • :) @Dead.Rabit 我直接引用了您输入的代码,但您是正确的,您需要提取 $parent 引用并简单地使用您的变量,这似乎是一个微不足道的问题。跨度>
    • 老实说,不确定你要去哪里;您在计算中的代码没有使用“this”引用函数,因此无论如何这都会产生运行时错误,并且在我的代码中 IsViewMode 位于 _Deleting 的所有者对象上,因此如果不重构以传递所有者,无论如何都无法在计算中引用在创建项目时。
    • @Whelkaholism 检查我刚刚添加的小提琴。您可以使用多种方法来执行此操作,例如传递引用或使用 apply 或 call 固有地这样做。就我个人而言,我更喜欢清晰的代码,所以我传递了一个参考,但可以随意实现它如何最适合您的架构
    猜你喜欢
    • 2011-12-05
    • 1970-01-01
    • 2018-06-21
    • 1970-01-01
    • 1970-01-01
    • 2012-09-23
    • 2013-03-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多