Search code examples
asp.net-mvcknockout.jsasp.net-core-mvcknockout-3.0

asp.net mvc core knockout.js multiple nested containers not binding properly


This is the code of the testing application i am creating.

<style>
#scroll-wrapper {
    margin: 5px;
    max-height: 250px;
    overflow: auto;
    vertical-align: top;
}

#thumbnails {
    vertical-align: top;
    padding-bottom: 10px;
    max-height: 500px;
    min-width: 100px;
    overflow-y: hidden;
    overflow-x: scroll;
    white-space: nowrap;
}


.thumbnail-container {
    display: inline-block;
    position: relative;
    line-height: 5px;
    margin: 3px;
    margin-bottom: 15px !important;
}
#abc {
    overflow-y: scroll;
    overflow-x: hidden;
    height: 200px;
}

<form method="post">
    <div data-bind="foreach: lists" id="thumbnails">     
            <div class="thumbnail-container">
                <span data-bind="text:listname"></span><br /><br /><br /><br /><br />
                <div id="abc">                   
                    <ul class="list-group" data-bind="foreach: cardlists">
                        <li class="list-group-item">
                            <span data-bind="text: cardname"></span>
                            <a href="#" data-bind="click: $root.removecard">Del</a>
                        </li>                  
                    </ul>
                    <a href="#" data-bind="click:  $parent.showhideaddcard">Add Card</a><br />                           
                    <div data-bind="visible: $parent.showRenderTimes">
                        <input type="text" data-bind="value: $parent.cardtext" /><br /><br /><br />
                        <input type="button" value="Add" data-bind="click: $parent.addcard" />
                        <input type="button" value="Cancel" data-bind="click: $parent.cancelcard" />
                    </div>   
                </div>             
            </div>
       </div>
    <p>
        <span id="addVar" data-bind="click: addList">Add List</span>
    </p>
    <p class="alignRight">
        <input type="submit" value="Check">
    </p>
</form>


<script type="text/javascript">

        var initialData = [
        {
            listname: "list1",containername :"container1", cardlists: [
            { cardname: "car1111111" },
            { cardname: "caddsdf" }]
        },
        {
            listname: "list2",containername :"container2", cardlists: [
                       { cardname: "card 1" },
                       { cardname: "card 2" }]
        },
        {
            listname: "list3", containername: "container3", cardlists: [
                       { cardname: "card 1" },
                       { cardname: "card 2" }]
            },
        {
            listname: "list4", containername: "container4", cardlists: [
                       { cardname: "card 1" },
                       { cardname: "card 2" }]
            },
        {
            listname: "list5", containername: "container5", cardlists: [
                       { cardname: "card 1" },
                       { cardname: "card 2" }]
            },
        {
            listname: "list6", containername: "container6", cardlists: [
                       { cardname: "card 1" },
                       { cardname: "card 2" }]
            },
        {
            listname: "list7", containername: "container7", cardlists: [
                    { cardname: "card 3" },
                    { cardname: "card 4" },
                    { cardname: "card 5" },
                    { cardname: "card 6" },
                    { cardname: "card 7" },
                    { cardname: "card 8" },
                    { cardname: "card 9" },
                    { cardname: "card 10" },
                    { cardname: "card 11" }]
            },
        { 
            listname: "list6", containername: "container8", cardlists: [
                { cardname: "card 1" },
                { cardname: "card 2" }]
            },
        { 
            listname: "list6", containername: "container9", cardlists: [
                 { cardname: "card 1" },
                 { cardname: "card 2" }]
            },
        {
            listname: "list6", containername: "container10", cardlists: [
                 { cardname: "card 1" },
                 { cardname: "card 2" }]
            },
        { 
            listname: "list6", containername: "container11", cardlists: [
                { cardname: "card 1" },
                { cardname: "card 2" }]
            }
        ];


    var ListModal = function(lists) {
        var self = this;
        self.showRenderTimes = ko.observable(false);
        self.cardtext = ko.observable("ff");
        self.lists = ko.observableArray(ko.utils.arrayMap(lists, function (list) {
            return { listname: list.listname, containername:list.containername, cardlists: ko.observableArray(list.cardlists) };
        }));


        self.addList = function() {
            self.lists.push({
                listname: "abc", 
                containername:"container5",
                cardlists: ko.observableArray([
                    {
                        cardname: "Bungle"
                    },
                    {
                        cardname: "George"
                    },
                    {
                        cardname: "Zippy"
                    }
                ])
            });
        }; 

        self.addcard = function (lists) {
            lists.cardlists.push({
                cardname: self.cardtext             
            });
            self.showRenderTimes(false);
        };
        self.cancelcard = function () {            
            self.showRenderTimes(false);
        };

        self.showhideaddcard = function (lists) {
           self.showRenderTimes(true);
        };

        self.removecard = function (cardlist) {
            $.each(self.lists(), function () { this.cardlists.remove(cardlist) })
        };

        self.save = function ()
        {
        };     
    };
    ko.applyBindings(new ListModal(initialData));
</script>

The code generate this view

enter image description here

Now what should happened that when i click for example third add card in the picture only that textbox should appear but it is opening all the add card textboxes in the application.

apparently the binding is not correct. can someone correct my binding so when i press the no 3 containers add card only no 3 textbox appears. I have the idea that maybe div abc is the problem but i am not able to correct it.


Solution

  • The bindings are working correctly. The problem is that you have only a single parent observable that stores the visible state for all your add-card sections. Naturally if each section is bound to the same showRenderTimes observable then they'll all be hidden/visible at the same time. You need to create an observable for each list that stores its visibility status.

    You could move showRenderTimes from the parent ListModal to the individual lists:

    self.lists = ko.observableArray(ko.utils.arrayMap(lists, function (list) {
        return { listname: list.listname, containername:list.containername, cardlists: ko.observableArray(list.cardlists), showRenderTimes: ko.observable(false) };
    }));
    

    Then you'll need to update the bindings that use it to remove the $parent context:

    <!--<div data-bind="visible: $parent.showRenderTimes">-->
    <div data-bind="visible: showRenderTimes">
    

    And your showHideAddCard and cancelCard functions modify the variable on the list that was clicked and so become:

      self.cancelcard = function (list) {            
        list.showRenderTimes(false);
      };
    
      self.showhideaddcard = function (list) {
        list.showRenderTimes(true);
      };
    

    Example jsFiddle