Knockout.js ObservableArray not binding - javascript

I have this code as global for the whole page:
<script type="text/javascript">
var data = [];
var VM_FiltroSeguros =
{
seguros: ko.observableArray(data)
};
ko.applyBindings(VM_FiltroSeguros.seguros);
</script>
Then when a succesfull ajax call is made, executed this:
function okFiltrarSeguros(data)
{
var parsedData = parse(data);
if (parsedData.Ok)
{
toastr.success('Se encontraron ' + parsedData.Value.length.toString() + ' Seguros.');
$('#liResultsFiltroSeguro').show();
VM_FiltroSeguros.seguros = parsedData.Value;
};
The Html is these:
<table class="table table-hover">
<thead>
<tr>
<th>Ramo</th>
<th>Poliza</th>
</tr>
</thead>
<tbody data-bind="foreach: seguros">
<tr>
<td><span data-bind="text: NroRamo"></span></td>
<td><span data-bind="text: NroSeguro"></span></td>
</tr>
</tbody>
</table>
After VM_FiltroSeguros.seguros = parsedData.Value; is executed I can see in the debugger that the viewModel is filled whith objects, but the is never updated.
What could be wrong? Thanks!!!!

There's a couple of things you're doing wrong here. First, you need to bind the entire ViewModel:
var data = [];
var VM_FiltroSeguros =
{
seguros: ko.observableArray(data)
};
ko.applyBindings(VM_FiltroSeguros);
Then you need to add data to the 'seguros' property with a function call like this:
VM_FiltroSeguros.seguros(parsedData.Value);

Related

Is it possible to pass a Knockout.JS variable into a onclick function WITHOUT binding?

Is it possible to pass a Knockout.JS variable into an onclick function WITHOUT binding the function to the view model?
Below, i'm trying to pass the username parameter for the particular row below:
<tbody data-bind="foreach: viewModel">
<tr>
<td data-bind="text: UserId"></td>
<td><button onclick="alertRowName($element.UserName)"></button></td>
</tr>
</tbody>
<script>
function alertRowName(string){
alert(string);
}
//in my example the model is from a c# viewmodel..
viewModel = ko.mapping.fromJS(model);
var jsModel = ko.toJS(viewModel);
ko.applyBindings(viewModel);
</script>
You can do it something like this.
EDIT
hmmmm.. just noticed the Without binding. Can you clarify why without binding?
var app = Window.app || {};
app.model = [{
UserId: 1,
UserName: "User Name Here",
}];
app.ViewModel = function ViewModel(model){
var self = this;
self.data = ko.mapping.fromJS(model);
};
app.ViewModel.prototype.alertRowName = function alertRowName(user) {
alert(user.UserName());
};
ko.applyBindings(new app.ViewModel(app.model));
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.min.js"></script>
<table>
<tbody data-bind="foreach: data">
<tr>
<td data-bind="text: UserId"></td>
<td><button data-bind="click: $root.alertRowName, text: UserName"></button> </td>
</tr>
</tbody>
</table>
BEWARE...HERE BE DRAGONS
You can do the following, but I would strongly recommend not doing this.
This is attaching a function to the Window object making it a Global function. each event handler generally has an event object available that you can use.
var app = Window.app || {};
app.model = [{
UserId: 1,
UserName: "User Name Here",
}];
app.ViewModel = function ViewModel(model){
var self = this;
self.data = ko.mapping.fromJS(model);
};
app.ViewModel.prototype.alertRowName = function alertRowName(user) {
alert(user.UserName());
};
ko.applyBindings(new app.ViewModel(app.model));
Window.myButtonClick = function(){
var item = ko.dataFor(event.currentTarget);
alert(item.UserName());
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.min.js"></script>
<table>
<tbody data-bind="foreach: data">
<tr>
<td data-bind="text: UserId"></td>
<td><button onclick="Window.myButtonClick()" data-bind="text: UserName"></button> </td>
</tr>
</tbody>
</table>

How to refresh bootstrap wizard

I have a jsp with a bootstrap wizard, like this:
http://s3.amazonaws.com/creativetim_bucket/products/22/original/wizard_template.png?1407329794
With this wizard, I can add the employees element collected in Javascript array (I also use AngularJS).
After that, in the last step of wizard there is the summary of the employees shown in a table.
For each row of the table, I have been added an href link to delete the current employee element. This href link calls a function managed by AngularJS.
Ok, it work. But, after the deletion, the table is not refresh. And the deleted element is present in table yet, but not in array.
So, how can I refresh the table?
Here's the code of the table:
<table class="table table-bordered table-hover table-condensed">
<thead>
<tr>
<td>#</td>
<td>Nome</td>
<td>Cognome</td>
<td>Matricola</td>
</tr>
</thead>
<tr ng-repeat="employee in listaDipendenti track by $index">
<td>{{$index + 1}}</td>
<td>{{employee.nome}}</td>
<td>{{employee.cognome}}</td>
<td>{{employee.matricola}}</td>
<td><a ng-click="DeleteEmployees($index)" href="#" class="btn btn-simple btn-xs" role="button" style="color: green">Delete</a></td>
</tr>
</table>
Here's the code in JS:
//Classe di tipo Employee
function Employee(nome, cognome, matricola) {
this.nome = nome;
this.cognome = cognome;
this.matricola = matricola;
}
var listEmployees = [];
var nDip = 0;
function Controller($scope) {
$scope.DeleteEmployees = function (n) {
if (n > -1) {
listEmployees.splice(n, 1);
}
};
}
for #charlietfl
It's not right, look the function used to add an employee in JS array.
$scope.AddInList = function () {
var nome = $("#nome").val();
var cognome = $("#cognome").val();
var matricola = $("#matricola").val();
$("#nome").val("");
$("#cognome").val("");
$("#matricola").val("");
nDip = nDip + 1;
listEmployees.push(new Employee(nome, cognome, matricola));
$("#cont").text(nDip);
$scope.listaDipendenti = JSON.parse(JSON.stringify(listEmployees));
};

When clicking a row, KnockoutJS returns an empty JSON object

My problem is that when I click a row that is data-bound in KnockoutJS, the data that is sent to my selectItem function is empty and only displays {"data":{}} in the Firefox Web Console.
What I do not understand is how to get the values of my table cells so that I can refer to the sender portion of my JSON object? At the moment, it is empty.
KnockoutJS:
function ServiceViewModel() {
var self = this;
self.rows = ko.observableArray();
$.ajax({
method: "GET",
url: "URL",
success: function(data) {
var observableData = ko.mapping.fromJSON(data);
var array = observableData();
self.rows(array);
}
});
self.computedRows = ko.computed(function() {
if(!self.query()) {
return self.rows();
} else {
return ko.utils.arrayFilter(self.rows(), function(row) {
return row.sender() == self.query();
});
}
});
self.selectedItem = ko.observable();
self.selectItem = function(data) {
self.selectedItem(data);
console.log(JSON.stringify(self.selectedItem()));
};
};
$(document).ready(function() {
var svc = new ServiceViewModel();
ko.applyBindings(svc);
});
JSON:
[{"statusmsg":"OK","data":{"status":"running"},"sender":"hostname","statuscode":0}]
HTML:
<div class="table-responsive">
<table class="table table-condensed table-striped table-bordered table-hover">
<thead>
<tr>
<th>Host</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody data-bind="foreach: computedRows().sort(function(l, r) { return l.sender() > r.sender() ? 1 : -1})">
<tr data-bind="click: $root.selectItem">
<td data-bind="text: sender"></td>
<td><span data-bind="text: data.status"></span></td>
<td>
<div class="btn-group">
<button data-bind="click: $root.selectItem">Start</button>
<button>Stop</button>
</div>
</td>
</tr>
</tbody>
</table>
</div>
You need to use ko.toJSON to convert your knockout model to json instead of JSON.stringify as knockout uses functions for the observables.
console.log(ko.toJSON(self.selectedItem()));

Knockout click data binding

I have an issue in below example
HTML CODE:
<table id="items">
<thead>
<tr>
<td>ToDo List</td>
</tr>
</thead>
<tbody data-bind="foreach: toDoItems">
<tr>
<td><label data-bind="text: toDoItem"></label></td>
<td>Remove</td>
</tr>
</tbody>
</table>
Add item: <input type="text" id="newitem" />
<button data-bind="click: addnewItem">Add</button>
This is my JS code:
$(function () {
var MetalViewModel = function() {
var self = this;
self.toDoItems = ko.observableArray();
self.update = function() {
self.toDoItems.removeAll();
self.toDoItems.push(
new metals({"Task":"This is urgent task."}),
new metals({"Task":"You need to do it also."})
);
}
self.addnewItem = function () {
alert( $("#newitem").val() );
self.toDoItems.push(
new metals({"Task":$("#newitem").val()})
);
};
self.removeToDoItem = function(item) {
self.toDoItems.remove(item);
};
};
var MetalViewModel = new MetalViewModel();
var y = window.setInterval(MetalViewModel.update,1000);
ko.applyBindings(MetalViewModel, document.getElementById("items"));
});
var metals = function (data) {
return {
toDoItem: ko.observable(data.Task)
};
};
Everything is working fine. Listing, removing ....
The only issue I am facing is that when I add new item, function is totally not working... means even if I alert click binding does not reach there.
You bound only the table with your vewmodel (id=items):
ko.applyBindings(MetalViewModel, document.getElementById("items"));
And your button is outside the table so it is not related to your viewmodel.
As a solution, you can bind to the common parent or to the whole page:
ko.applyBindings(MetalViewModel);
Here're two issues:
invalid binding, instead of ko.applyBindings(MetalViewModel, document.getElementById("items")); should be ko.applyBindings(MetalViewModel);
invalid data-bind function name instead of additemToAdd should be addToDoItem
here's working sample

Knockout.js Pre-seed ObservableArray

I'm not sure if I'm trying to do too much here, but here is the scenario. I have an asp.net mvc page that, on the first time loading, returns a table of data in a view using the standard foreach mechanisms in the mvc framework. If the user has javascript enabled, I want to use knockout to update the table going forward. Is there a way to have knockout read the data from the dom table and use that data as the initial observable collection. From then on out, I would use knockout and ajax to add, edit, or delete data.
In a nutshell, I need to parse an html table into a knockout observable collection.
I've had a go at coding this up:
Here's the basic markup:
<table id="table" data-bind="template: { name: 'table-template' }">
<thead>
<tr>
<th>Name</th>
<th>Surname</th>
</tr>
</thead>
<tbody>
<tr>
<td>Richard</td>
<td>Willis</td>
</tr>
<tr>
<td>John</td>
<td>Smith</td>
</tr>
</tbody>
</table>
<!-- Here is the template we'll use for re-building the table -->
<script type="text/html" id="table-template">
<thead>
<tr>
<th>Name</th>
<th>Surname</th>
</tr>
</thead>
<tbody data-bind="foreach: data">
<tr>
<td data-bind="text: name"></td>
<td data-bind="text: surname"></td>
</tr>
</tbody>
</script>
Javascript:
(function() {
function getTableData() {
// http://johndyer.name/html-table-to-json/
var table = document.getElementById('table');
var data = [];
var headers = [];
for (var i = 0; i < table.rows[0].cells.length; i++) { 
headers[i] = table.rows[0].cells[i].innerHTML.toLowerCase().replace(/ /gi, '');
}
// go through cells
for (var i = 1; i < table.rows.length; i++) {
var tableRow = table.rows[i];
var rowData = {};
for (var j = 0; j < tableRow.cells.length; j++) {
rowData[headers[j]] = tableRow.cells[j].innerHTML;
}
data.push(rowData);
}
return data; 
}
var Vm = function () {
this.data = ko.observableArray(getTableData());
};
ko.applyBindings(new Vm(), document.getElementById('table'));
})();
You can extend this concept using the mapping plugin to create observables for each row: http://knockoutjs.com/documentation/plugins-mapping.html
View a demo here: http://jsfiddle.net/CShqK/1/
EDIT: I'm not saying this is the best approach, as it can be costly to traverse a large table to get the data. I would probably just output the JSON in the page as suggested by others in this thread.
How about just feeding your observable array with the data instead of parsing the html table
myArray: ko.observableArray(#Html.Raw(Json.Encode(Model.myTableData)))
If you really need to go the parsing the html way, you can use the following code
var tableData = $('#myTable tr').map(function(){
return [
$('td,th',this).map(function(){
return $(this).text();
}).get()
];
}).get();
$(document).ready(function() {
var myData = JSON.stringify(tableData);
alert(myData)
});
Here is a fiddle showing the code in action:
http://jsfiddle.net/FWCXH/

Categories

Resources