Search code examples
javascripthtmlknockout.jsevent-bubblingknockout-3.0

Knockout Model: Challenge in Knockout Checkbox


I have an Knockout model. I wish to select checkbox and then Tick mark should appear on checkbox. Till now I achieved clicking on Row and Ticking the checkbox but when I just click on Checkbox the Row is highlighted but Checkbox is not checked.

Please find the fiddle I built till now Fiddle

Code fore Reference.

<input type="text" id="searchboxSample" name="lname" required
                                                   placeholder="searchboxSample" />
<table id="devtable">
  <thead>
    <tr>
      <th>ID</th>
      <th>Name</th>
      <th>Status</th>
    </tr>
  </thead>
  <tbody data-bind="foreach: paginated" >

    <tr data-bind="click: $parent.select, css: {flash: $parent.selected() === $data}">
    <td width="15"><input type="checkbox" data-bind="checkedValue: $data, checked: $parent.selected() === $data" /></td>
      <td data-bind="text: ID"></td>
      <td data-bind="text: Name"></td>
      <td data-bind="text: Status"></td>
    </tr>
  </tbody>
</table>ID :
<input type="text" name="ID" data-bind="value: selectedID, enable: enableEdit" />
<br>Name :
<input type="text" name="Name" data-bind="value: selectedName, enable: enableEdit" />
<br>Status :
<input type="text" name="Status" data-bind="value: selectedStatus, enable: enableEdit" />
<br>
<input type="button" value="Send" disabled/>

<input type="button" value="ChangeModelData" data-bind="click: changeTableData">
<input type="text" id="lname" name="lname" required
                                                   placeholder="Nachname" data-bind="value: acceptedSymptom , event: {keyup: showMessage}"/>
<div class="pager">
 <a href="#" class="firstpage" data-bind="click: firstpage, visible: hasPrevious">&le;</a>
    <a href="#" class="previous" data-bind="click: previous, visible: hasPrevious">&le;</a>
    <span class="current" data-bind="text: $root.pageNumber"></span>
    <a href="#" class="next" data-bind="click: next, visible: hasNext">&gt;</a>
  <a href="#" class="lastpage" data-bind="click: lastpage, visible: hasNext">&ge;</a>
</div>

Solution

  • That's happening because the click event 'bubbles' up from the child to the parent. Read about it here.

    To prevent it, you need to add clickBubble binding as well as the click binding with the same function $parent.select.

    Your existing line $parent.check isn't valid as it doesn't point to anything in the JS.

    Here's the JSfiddle, and the implementation here:

    var RowModel = function(id, name, status) {
    this.ID = ko.observable(id);
      this.Name = ko.observable(name);
      this.Status = ko.observable(status);
       
    };
    
    RowModel.fromRawDataPoint = function(dataPoint) {
      return new RowModel(dataPoint.id, dataPoint.name, dataPoint.status);
    };
    
    var myData = [{
      id: "001",
      name: "Jhon",
      status: "Single"
    }, {
      id: "002",
      name: "Mike",
      status: "Married"
    }, {
      id: "003",
      name: "Marrie",
      status: "Complicated"
    },{
      id: "004",
      name: "Jhon",
      status: "Single"
    }, {
      id: "005",
      name: "Mike",
      status: "Married"
    }, {
      id: "006",
      name: "Marrie",
      status: "Complicated"
    }, {
      id: "007",
      name: "Mike",
      status: "Married"
    }, {
      id: "008",
      name: "Marrie",
      status: "Complicated"
    }, {
      id: "009",
      name: "Mike",
      status: "Married"
    }, {
      id: "010",
      name: "Marrie",
      status: "Complicated"
    }
    ];
    
    function MyVM(data) {
      var self = this;
      
      /*************
      Start of logic for Paging
      
      ******/
      
      
      self.items = ko.observableArray();
       this.all =  self.items;
    
     self.pageNumber = ko.observable(0);
        self.nbPerPage = 2;
        
        // I think this is somewhere I am missing the functionality.
        
        this.totalPages = ko.computed(function() {
    		var div = Math.floor(self.all().length / self.nbPerPage);
    		div += self.all().length % self.nbPerPage > 0 ? 1 : 0;
    		return div - 1;
    	});
    
    this.paginated = ko.computed(function() {
            var first = self.pageNumber() * self.nbPerPage;
            return self.all.slice(first, first + self.nbPerPage);
        });
        
        this.hasPrevious = ko.computed(function() {
    		return self.pageNumber() !== 0;
    	});
      
      	this.hasNext = ko.computed(function() {
    		return self.pageNumber() !== self.totalPages();
    	});
      
      this.next = function() {
      	self.deselect();
    		if(self.pageNumber() < self.totalPages()) {
    			self.pageNumber(self.pageNumber() + 1);
    		}
    	}
      
      this.lastpage = function() {
      	self.deselect();
    		if(self.pageNumber() < self.totalPages()) {
    			self.pageNumber(self.totalPages());
    		}
    	}
      
      
      this.firstpage = function() {
      	self.deselect();
    		if(self.pageNumber() != 0) {
    			self.pageNumber(self.pageNumber()-self.pageNumber());
          alert(self.pageNumber());
    		}
    	}
      
      this.previous = function() {
        self.deselect();
    		if(self.pageNumber() != 0) {
    			self.pageNumber(self.pageNumber() - 1);
    		}
    	}  
    
    
    /***********
    End of Logic for Paging
    
    */
    
    	self.loadData(data);
      
      self.deselect = function(){
      	self.selected(null);
        self.enableEdit(false);
      };
            
      self.select = function(item) {
        if (item === self.selected()) {
        self.selected(null);
        self.enableEdit(false);
      } else {
        self.selected(item);
        self.enableEdit(true);
      }   
      }; 
    
      self.acceptedSymptom=ko.observable();
    
    	
      
      self.selected = ko.observable(self.items()[0]);
      self.selectedID=ko.observable(self.items()[0].ID());
      self.selectedName=ko.observable(self.items()[0].Name());
      self.selectedStatus=ko.observable(self.items()[0].Status());
      
      self.selected.subscribe(function(newValue){
        if (newValue === null){
            self.selectedID(null);
            self.selectedName(null);
            self.selectedStatus(null);
            return;
        }
      	if (typeof newValue !== 'undefined'){
        	self.selectedID(newValue.ID());
          self.selectedName(newValue.Name());
          self.selectedStatus(newValue.Status());
        }
      });
      
      
      
      self.FirstName = ko.observable();
    
      self.FirstName.focused = ko.observable();
      
      var i =0;
      self.FirstName.focused.subscribe(function(newValue, data) {
    
    if(newValue){
    i++;
    //alert(newValue + " let's check data " + i);
    }
       if (!newValue) {
           //do validation logic here and set any validation observables as necessary
           if(i>0){
           alert(newValue + " let's check data" + i);
           i=0;
           }    
           
       }  
      
    });
    
     self.enableEdit = ko.observable(false); 
    
     self.changeTableData = function() {
    
          self.loadData([{
            id: "111",
            name: "ABC",
            status: "Single"
          }, {
            id: "222",
            name: "XYZ",
            status: "Married"
          }, {
            id: "333",
            name: "PQR",
            status: "Complicated"
          }, {
            id: "444",
            name: "PQR",
            status: "Complicated"
          }, {
            id: "555",
            name: "PQR",
            status: "Complicated"
          }, {
            id: "666",
            name: "PQR",
            status: "Complicated"
          }, {
            id: "777",
            name: "PQR",
            status: "Complicated"
          }, {
            id: "888",
            name: "PQR",
            status: "Complicated"
          }
          ]);
        }
      
    
     self.showMessage = function(data,event){
    if (event.keyCode !== 13){
    alert("Entered key is not a Enter");
    }else{
          self.loadData([{
            id: "111",
            name: "ABC",
            status: "Single"
          }, {
            id: "222",
            name: "XYZ",
            status: "Married"
          }, {
            id: "3333",
            name: "PQR",
            status: "Complicated"
          }]);
          alert("You are on right Track.");
          alert(self.acceptedSymptom(''))
        }
    
    } 
      
    }
    MyVM.prototype.loadData = function(rawData) {
      this.items(rawData.map(RowModel.fromRawDataPoint));
      this.all(this.items());
      this.pageNumber(0);
    };
    ko.applyBindings(new MyVM(myData));
    #devtable {
        font-family: arial, sans-serif;
        border-collapse: collapse;
        width: 100%;
    }
    
    
    #devtable th {
    border-top: 1px solid #dddddd;
    border-bottom:1px solid #dddddd;
    background-color: #f0f0f5
    
    }
    #devtable td {
    border-bottom:1px solid #dddddd;
    }
    
    
    #devtable td, #devtable th {    
        text-align: left;
        padding: 8px;
    }
    
    .flash { background-color: #b6bcdb; }
    
    .previous{text-decoration: none;}
    
    .previous {
        background-color: #f1f1f1;
        color: black;
    }
    
    
    .next{text-decoration: none; }
    .lastpage{text-decoration: none; }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
    <input type="text" id="searchboxSample" name="lname" required
                                                       placeholder="searchboxSample" />
     
    <input type="checkbox" />
    <table id="devtable">
      <thead>
        <tr>
          <th>ID</th>
          <th>Name</th>
          <th>Status</th>
        </tr>
      </thead>
      <tbody data-bind="foreach: paginated" >
     
        <tr data-bind="click: $parent.select, css: {flash: $parent.selected() === $data}">
        <td width="15"><input type="checkbox" data-bind="checkedValue: $data, checked: $parent.selected() === $data, click: function(){$parent.select($data); return true}, clickBubble: false" /></td>
          <td data-bind="text: ID"></td>
          <td data-bind="text: Name"></td>
          <td data-bind="text: Status"></td>
        </tr>
      </tbody>
    </table>ID :
    <input type="text" name="ID" data-bind="value: selectedID, enable: enableEdit" />
    <br>Name :
    <input type="text" name="Name" data-bind="value: selectedName, enable: enableEdit" />
    <br>Status :
    <input type="text" name="Status" data-bind="value: selectedStatus, enable: enableEdit" />
    <br>
    <input type="button" value="Send" disabled/>
    
    <input type="button" value="ChangeModelData" data-bind="click: changeTableData">
    <input type="text" id="lname" name="lname" required
                                                       placeholder="Nachname" data-bind="value: acceptedSymptom , event: {keyup: showMessage}"/>
    <div class="pager">
     <a href="#" class="firstpage" data-bind="click: firstpage, visible: hasPrevious">&le;</a>
    	<a href="#" class="previous" data-bind="click: previous, visible: hasPrevious">&le;</a>
    	<span class="current" data-bind="text: $root.pageNumber"></span>
    	<a href="#" class="next" data-bind="click: next, visible: hasNext">&gt;</a>
      <a href="#" class="lastpage" data-bind="click: lastpage, visible: hasNext">&ge;</a>
    </div>