最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Knockout.js showhide block visibility pattern - Stack Overflow

programmeradmin3浏览0评论

I have the code duplication problem in the next case. On my page I have a lot of blocks that I need to show / hide by clicking to link:

<div>
    <a data-bind="click: showHiddenFirst, visible: isVisibleFirst"href="#">Show first</a>
    <div data-bind="visible: !isVisibleFirst()" style="display:none">
        hidden content first
    </div>
</div>
<div>
    <a data-bind="click: showHiddenSecond, visible: isVisibleSecond"href="#">Show second</a>
    <div data-bind="visible: !isVisibleSecond()" style="display:none">
        hidden content second
    </div>
</div>

And my JS

var vm = function(){
    this.isVisibleFirst = ko.observable(true);

    this.showHiddenFirst = function(){
        this.isVisibleFirst(false)
    };

    this.isVisibleSecond = ko.observable(true);

    this.showHiddenSecond = function(){
        this.isVisibleSecond(false)
    };
};

ko.applyBindings(new vm());

Here is jsfiddle example /

Question is how to avoid all this show / visible duplication? Maybe I need some custom binding where I put id of my hidden block or smth. else? Any patterns that you can suggest?

I have the code duplication problem in the next case. On my page I have a lot of blocks that I need to show / hide by clicking to link:

<div>
    <a data-bind="click: showHiddenFirst, visible: isVisibleFirst"href="#">Show first</a>
    <div data-bind="visible: !isVisibleFirst()" style="display:none">
        hidden content first
    </div>
</div>
<div>
    <a data-bind="click: showHiddenSecond, visible: isVisibleSecond"href="#">Show second</a>
    <div data-bind="visible: !isVisibleSecond()" style="display:none">
        hidden content second
    </div>
</div>

And my JS

var vm = function(){
    this.isVisibleFirst = ko.observable(true);

    this.showHiddenFirst = function(){
        this.isVisibleFirst(false)
    };

    this.isVisibleSecond = ko.observable(true);

    this.showHiddenSecond = function(){
        this.isVisibleSecond(false)
    };
};

ko.applyBindings(new vm());

Here is jsfiddle example http://jsfiddle/sstude/brCT9/2/

Question is how to avoid all this show / visible duplication? Maybe I need some custom binding where I put id of my hidden block or smth. else? Any patterns that you can suggest?

Share Improve this question edited Feb 18, 2013 at 9:48 Fabian Schmengler 24.6k9 gold badges80 silver badges113 bronze badges asked Feb 18, 2013 at 9:01 Akim KhalilovAkim Khalilov 1,0395 gold badges16 silver badges31 bronze badges
Add a ment  | 

3 Answers 3

Reset to default 4

Here was a thought at encapsulating this functionality pletely in an observable for your specific scenario:

ko.bindingHandlers.clickVisible = {
    init: function(element) {
       var visible = ko.observable(true),
           opposite = ko.puted(function() { return !visible(); }),
           clickHandler = visible.bind(null, false);

        //apply bindings to anchor
        ko.applyBindingsToNode(element, { click: clickHandler, visible: visible });

        var sibling = element.nextSibling;
        //find the div (as text nodes, etc. will show up in nextSibling)
        while (sibling && sibling.nodeType != 1) {
            sibling = sibling.nextSibling;   
        }        

        //apply bindings to div
        if (sibling) {
            ko.applyBindingsToNode(sibling, { visible: opposite });
        }
    }
};

It could be tweaked further, as necessary, if maybe the value passed into the binding should matter.

Example: http://jsfiddle/rniemeyer/gCgy5/

You could use a template together with a separate model for the hidden elements:

HTML

<div data-bind="template: { name: 'hidden-template', data: first }"></div>
<div data-bind="template: { name: 'hidden-template', data: second }"></div>

<script type="text/html" id="hidden-template">
    <a data-bind="click: showHidden, visible: isVisible, text : linkText" href="#"></a>
    <div data-bind="visible: !isVisible(), html: content" style="display:none"></div>
</script>

JS

var hiddenModel = function(linkText, content) {
    this.linkText = linkText;
    this.content = content;
    this.isVisible = ko.observable(true);
    this.showHidden = function(){
        this.isVisible(false)
    };
}

var vm = function() {
    this.first = new hiddenModel('Show first', 'hidden content first');
    this.second = new hiddenModel('Show second', 'hidden content second');
};

Note: for just these two elements this might be too much overhead but as soon as you need more hidden items, it pays off. Any additional element needs just one short line of HTML and JS each.

UPDATE FOR COMPLEX TEMPLATE WITH BINDINGS:

If your HTML content contains bindings itself you can put it into templates too and load these dynamically

Working Example

HTML

<div data-bind="template: { name: 'hidden-template', data: first }"></div>
<script type="text/html" id="content-first">
    test simple content
</script>
<div data-bind="template: { name: 'hidden-template', data: second }"></div>
<script type="text/html" id="content-second">
    test content <a href="#" data-bind="click:testBtn">with binding</a>
</script>

<script type="text/html" id="hidden-template">
    <a data-bind="click: showHidden, visible: isVisible, text : linkText" href="#"></a>
    <div data-bind="visible: !isVisible(), template: { name: content, data: $parent }" style="display:none"></div>
</script>

JS

var hiddenModel = function(linkText, content) {
    this.linkText = linkText;
    this.content = content;
    this.isVisible = ko.observable(true);
    this.showHidden = function(){
        this.isVisible(false)
    };
}

var vm = function() {
    this.testBtn = function(){alert('it works');}
    this.first = new hiddenModel('Show first', 'content-first');
    this.second = new hiddenModel('Show second', 'content-second');
};

content is now the template id instead of an HTML string.

Why not use an observableArray with some ids (one for each checkbox)?

You could then have methods like:

model hideElement = function(id) {
  model.hiddenElements.push(id);
}

model.showElement = function(id) {
  model.hiddenElements.remove(id);
}

And in your binding:

<div data-bind="click: function() { hideElement('two') }, visible: !hiddenElements().contains('one')"></div>

Edit: i updated your fiddle to show a possible implementations: http://jsfiddle/brCT9/4/

发布评论

评论列表(0)

  1. 暂无评论