I slimmed down my actual code but I can't get this work. I am using knockoutjs and bootstrap with inline knockout templates. I use to just put a bunch of input's inside a div but I changed it to a table for alignment reasons. I know the property names are correct and the javascript console doesn't show any errors at all for bad variables or binding issues. I am putting the foreach in a TR tag instead of the TBODY tag because I don't know how many checkboxes I will have every time and I don't want them in rows exactly, just one TR element and a bunch of TD cells inside that one TR tag for now. How can I make this work??
<div id="Counties" class="well well-large checkbox inline">
<table class="table table-condensed">
<tbody>
<tr data-bind="foreach: { data: counties
}">
<td><input type="checkbox" data-bind="attr: { value: $data.sid }" />$data.name
</td>
</tr>
</tbody>
</table>
</div>
Here are my viewModels :
function searchVm() {
var self = this;
self.counties = ko.observableArray([]); //array of jurisItem
}
function jurisItem(name, sid) {
var self = this;
self.name = name;
self.sid = sid;
}
Edit :
I also tried this based on knockoutjs documentation and it doesn't work. I know I can do this in other ways using jquery but I would prefer knockout template syntax...
<table class="table table-condensed">
<tbody>
<tr>
<!-- ko foreach: $root.counties -->
<td>
<input type="checkbox" data-bind="attr: { value: $data.sid }" />$data.name
</td>
<!-- /ko -->
</tr>
</tbody>
</table>
I am not sure what are You trying to do. I made some sample.
html:
<div id="Counties" class="well well-large checkbox inline">
<table class="table table-condensed">
<tbody>
<tr data-bind="foreach: counties">
<td>
<input type="checkbox" data-bind="attr: { value: sid }" />
<span data-bind="text: name" />
</td>
</tr>
</tbody>
</table>
</div>
javascript:
$(function () {
var SearchVm = function () {
var self = this;
self.counties = ko.observableArray([]);
};
var JurisItem = function (name, sid) {
var self = this;
self.name = name;
self.sid = sid;
}
var item1 = new JurisItem("TestName1", "TestSid1");
var item2 = new JurisItem("TestName2", "TestSid2");
var searchViewModel = new SearchVm();
searchViewModel.counties.push(item1);
searchViewModel.counties.push(item2);
ko.applyBindings(searchViewModel);
})
Does this work for You:
http://jsfiddle.net/PVMjy/41/
Related
I have an existing jQuery dataTables object in my html page.
I need to update a few <a href> elements in all <td>s on the first <tr> of the datatables by clicking on a refresh button which triggers an Ajax call to retrieve the new data in JSON.
The <a href> elements are dynamically constructed with the hyper links retrieved by Ajax, so I need to have the html for each <a href> element.
<tr id="LoJXi76DH3" role="row" class="odd">
<td><a data-transition="pop" data-mini="true" data-position-to="window" data-rel="popup" href="#deleteThisRunModel" onclick="copyRunModelObjId("LoJXi76DH3");" title="Delete this job"><img width="16" height="16" src="css/img/Remove24.png"></a></td>
<td><span>LoJXi76DH3</span></td>
<td><span>500</span></td>
<td><span>Completed</span></td>
<td><span>Firstname Lastname</span></td>
<td><span>9/12/2015, 1:07:39 PM</span></td>
<td><span>9/12/2015, 1:18:47 PM</span></td>
<td><span>Successful</span><span> </span><a title="Details" class="my-tooltip-btn ui-btn ui-alt-icon ui-nodisc-icon ui-btn-inline ui-icon-info ui-btn-icon-notext" data-transition="pop" data-rel="popup" href="#popupRMDetails_LoJXi76DH3">Details</a></td>
<td><a target="_blank" href="View.jsp?res=500&url=myImage.png">View</a><span> </span>Download</td>
</tr>
Just wondering which function/api should I use to get this done?
If you want to replace an entire <tr>..</tr> with a brand new or modified <tr>, you can do the following.
First locate the row you want to replace in jQuery, either with some id selector or through DOM traversal from an event like this:
var $row = $(this).closest("tr")
Let's say you have an brand new HTML row that you'd like to replace it with. This could come from an AJAX call, somewhere on the page, or a modified version of the existing row, or just straight HTML:
var newRow = $("<tr> <td>1</td> <td>Bob</td> <td><i>23</i></td> <tr>
If this was a plain HTML table, you could just do .replaceWith() like this:
$row.replaceWith($(newRow))
Buutttt, then DataTables doesn't know about it, so the next time you call $dt.draw(), it'll change back. Instead, you have to pass the new info into the DataTable by locating the row in DataTables and then handing it the new info.
row().data() - data represents an array of string values that are the innerHTML of each td
So we need to convert the above row to something like this:
["1","Bob","<i>23</i>"]
Here's a function that converts a row to a data table array:
function TrToData(row) {
return $(row).find('td').map(function(i,el) {
return el.innerHTML;
}).get();
}
So the whole thing will look something like this:
$('.replace').click(function () {
var $row = $(this).closest("tr")
var newRow = $("#newRow").find("tr")[0].outerHTML
var newRowDataArray = TrToData(newRow)
$dt.row($row).data(newRowDataArray).draw();
});
Demo in jsFiddle
Demon in Stack Snippets
$(function() {
// initialize table
var $dt = $('#example').DataTable({
paging: false,
bFilter: false,
bInfo: false
});
// add row
$('#addRow').click(function () {
//$dt.row.add( [1,2,3] ).draw();
var newRow = $("#newRow").find("tr")[0].outerHTML
$dt.row.add($(newRow)).draw();
});
// replace row
$('.replace').click(function () {
var $row = $(this).closest("tr")
var newRow = $("#newRow").find("tr")[0].outerHTML
var newRowDataArray = TrToData(newRow)
//$row.replaceWith($(newRow))
//data represents an array of string values that are the innerHTML of each td
$dt.row($row).data(newRowDataArray).draw();
});
function TrToData(row) {
return $(row).find('td').map(function(i,el) {
return el.innerHTML;
}).get();
}
});
<link href="https://cdnjs.cloudflare.com/ajax/libs/datatables/1.10.12/css/jquery.dataTables.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/datatables/1.10.12/js/jquery.dataTables.js"></script>
<table id="example" class="display" cellspacing="0" >
<thead>
<tr>
<th>Hidden</th>
<th>Name</th>
<th>Age</th>
<th>Replace</th>
</tr>
</thead>
<tbody>
<tr>
<td>Line 1 <input type="hidden" value="1"/> </td>
<td>Bob <input type="hidden" value="Bob"/> </td>
<td><input type="text" value="18"/> </td>
<td><input type="button" value="Replace" class="replace"/> </td>
</tr>
</tbody>
</table>
<br/>
<button id="addRow">Add New Row</button>
<table id="newRow" style="display:none">
<tbody>
<tr >
<td>Line 2 <input type="hidden" value="2"/> </td>
<td>Ann <input type="hidden" value="Ann"/> </td>
<td><input type="text" value="21"/> </td>
<td><input type="button" value="Replace" class="replace"/> </td>
</tr>
</tbody>
</table>
you can use jQuery for updating a specified row. for this you need to define unique id for each row. then by the following id, you can get the object of the table element and update it by ajax call. let me explain by code. here also shown how to manipulate dynamic links.
function updateJobStatus() {
$("#data-table tr.running").each(function() {
var obj = $(this);
var id = $(this).find('td:eq(0)').text();
//var id1 = $(this).attr('id');
$.ajax({
type: 'GET',
dataType: 'json',
url: 'ajaxGetPrintJob.html',
data: 'id=' + id,
success: function(responseData,textStatus) {
if (textStatus == 'success' && responseData.length > 0) {
var id = responseData[0].id;
var tagId = responseData[0].voterListTagId;
var createdBy = responseData[0].createdByName;
var locationType = responseData[0].locationType;
var totalJobCount = responseData[0].totalJobCount;
var successCount = responseData[0].successCount;
var failedCount = responseData[0].failedCount;
var status = responseData[0].status;
$(obj).find('td:eq(0)').html(id);
$(obj).find('td:eq(1)').html('<input name="app_id" id="row'+id+ '" type="checkbox" value="'+id+'" class="case"/>');
$(obj).find('td:eq(2)').html(''+responseData[0].name+'');
$(obj).find('td:eq(3)').html(createdBy);
$(obj).find('td:eq(4)').html(totalJobCount);
$(obj).find('td:eq(5)').html(successCount);
$(obj).find('td:eq(6)').html(failedCount);
$(obj).find('td:eq(7)').html(status);
}
}, error: function(responseData) {
unblockView();
}
});
});
setTimeout(updateJobStatus, 20000);
}
here updateJobStatus() function will fire every 20 sec by ajax calling getting data and also manipulate.
here data-table refers the table id.
<table summary="search result" id="data-table" class="search-result" cellspacing="0" style="">
and the table row should be like,
<tr class="<c:if test="${loop.count % 2 !=0}"> odd-row </c:if> <c:if test="${result.status eq 'INITIALIZING'}"> running </c:if>" >
I have the following in my View:
<div data-bind='foreach: providers'>
<div data-bind='text: name'></div>
<button data-bind='click: model.addToCart'>Show</button>
<table class="table">
<thead>
<th style="width: 300px">Name</th>
<th>Price</th>
<th></th>
</thead>
<tbody data-bind='foreach: items'>
<tr>
<td data-bind='text: name'></td>
<td data-bind='text: price'></td>
<td>
<input type="button" data-bind='click: model.add' value="Add">
</td>
</tr>
</tbody>
</table>
</div>
And the following in my model:
function model() {
var self = this;
self.providers = data; //some random array data
self.addToCart = function(place) {
console.log(place);
}
};
I have used the normal click binding in the Knockout.js.
When I run the code, it seems that the addToCart function is not even being called. I get no output from console.log() for any value.
What have I missed? I'm a beginner. Please help out.
Once inside a foreach loop, your scope is the object you're currently iterating on. So, to refer to a function in the parent or root scope you're gonna have to use $root (although $parent is the same in this case):
<button data-bind='click: $root.addToCart'>Show</button>
See Documentation
your parent View Model should have
var self = this;
self.providers = ko.observableArray();
self.model = new Model();
then in your foreach loop you can use
$root.model.addToCart()
ok so i have my view model
function viewModel(calendarData) {
var self = this;
self.Calendars = ko.mapping.fromJS(calendarData);
self.ViewedCalendar = {};//what can/should i set this to be on initial creation?
self.DisplayCalendar = function (calendar) {
self.ViewedCalendar = calendar;
};
};
I then have my html:
<div data-bind="visible: Calendars().length > 0">
<h2>You have <span data-bind="text: Calendars().length"></span> calendars</h2>
<table class="table">
<thead>
<tr>
<th>Calendars</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody data-bind="foreach: Calendars">
<tr>
<td data-bind="text: Name"></td>
<td><span data-bind="text: Events().length"></span> events</td>
<td><a href='#' data-bind='click: $root.DisplayCalendar'>View</a></td>
</tr>
</tbody>
</table>
</div>
<div data-bind="visible: ViewedCalendar() !== null">
Your are viewing <span data-bind="text: ViewedCalendar.Name"></span><br />
</div>
What I am trying to achieve is when the user clicks View for a given calendar, that via DisplayCalendar() I set the property ViewedCalendar to be set to the given calendar.
This then shows my div that contains the label stating what calendar is being viewed.
This is all rough code at the minute just to get the basic functionality in place but I'm new to knockout so could use some help.
I'm getting TypeError: ViewedCalendar is not a function or ViewedCalendar is undefined.
Any help would be much appreciated.
The ViewedCalendar property needs to be an observable for knockout to reach to changes in it's value. You defined it like this:
self.ViewedCalendar = {};
Which is an (empty) object literal and not a function (as the error message correctly stated). What you need is:
self.ViewedCalendar = ko.observable(); // empty () give you an empty observable - no calendar selected yet
And then you can update it in your click handler with:
self.ViewedCalendar(calendar);
Here's a working full example:
function viewModel(calendarData) {
var self = this;
self.Calendars = ko.mapping.fromJS(calendarData);
self.ViewedCalendar = ko.observable();
self.DisplayCalendar = function (calendar) {
self.ViewedCalendar(calendar);
};
};
ko.applyBindings(new viewModel([{Name:'some calendar', Events: []}, {Name:'another calendar', Events: []}]));
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.js"></script>
<div data-bind="visible: Calendars().length > 0">
<h2>You have <span data-bind="text: Calendars().length"></span> calendars</h2>
<table class="table">
<thead>
<tr>
<th>Calendars</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody data-bind="foreach: Calendars">
<tr>
<td data-bind="text: Name"></td>
<td><span data-bind="text: Events().length"></span> events</td>
<td><a href='#' data-bind='click: $root.DisplayCalendar'>View</a></td>
</tr>
</tbody>
</table>
</div>
<div data-bind="with: ViewedCalendar">
Your are viewing <span style="font-weight: bold" data-bind="text: Name"></span><br />
</div>
Uncaught ReferenceError: Unable to process binding "foreach: function (){return Educations }"
Uncaught ReferenceError: Unable to process binding "foreach: function (){return WorkExperience }"
I couldn't figure out why the binding is failing.
i have the following two tables one for Education and other for Work Experience, They give the error when i'm trying to bind the both table in one view, If i remove the binding (JS + HTML code) it works fine
HTML:
<div id=divtable1 class="widget widget-simple widget-table">
<table id="Table1" class="table table-striped table-content table-condensed boo-table table-hover">
<thead>
<tr id="Tr1" class="filter">
<th>University<span class="required">*</span></th>
<th>Location <span class="required">*</span></th>
<th></th>
</tr>
</thead>
<tbody data-bind='foreach: Educations'>
<tr>
<td><input type="text" class='span11 required' data-bind="value: SchoolName" /></td>
<td><input type="text" class='span11 required' data-bind="value: Location" /></td>
<td><a href='#' data-bind='click: $root.removeEducation'>Delete</a></td>
</tr>
</tbody>
</table>
<button data-bind='click: $root.addEducation' class="btn btn-blue">Add Education</button>
</div>
<div id="divtable2">
<table id="Table2">
<thead>
<tr id="Tr2" class="filter">
<th>Employer Name<span class="required">*</span></th>
<th>EmployerAddress <span class="required">*</span></th>
<th></th>
</tr>
</thead>
<tbody data-bind='foreach: WorkExperience'>
<tr>
<td><input type="text" class='span11 required' data-bind="value: EmployerName" /></td>
<td><input type="text" class='span11 required' data-bind="value: EmployerAddress" /></td>
<td><a href='#' data-bind='click: $root.removeWorkExperience'>Delete</a></td>
</tr>
</tbody>
</table>
<button data-bind='click: $root.addWorkExperience' class="btn btn-blue">Add Work Experience</button>
</div>
Java Script:
<script type="text/javascript">
var Educations = function (educations) {
var self = this;
self.Educations = ko.mapping.fromJS(educations);
self.addEducation = function () {
self.Educations.push({"SchoolName": ko.observable(""), "Location": ko.observable("")});
};
self.removeEducation = function (education) {
self.Educations.remove(education);
};
};
var viewModel = new Educations(#Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model.Educations)));
ko.applyBindings(viewModel);
</script>
<script type="text/javascript">
var WorkExperience = function (workexperiences) {
var self = this;
self.WorkExperience = ko.mapping.fromJS(workexperiences);
self.addWorkExperience = function () {
self.WorkExperience.push({ "EmployerName": ko.observable(""), "EmployerAddress": ko.observable("")});
};
self.removeWorkExperience = function (workexperience) {
self.WorkExperience.remove(workexperience);
};
};
var viewModel = new WorkExperience(#Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model.WorkExperience)));
ko.applyBindings(viewModel);
</script>
Also I tried to bind Table1 but it didn't work
ko.applyBindings(viewModel, $('#Table1')[0]);
try adding this <pre data-bind="text: ko.toJSON($data, null, 2)"></pre> to your view. it will output the data that knockout contains in the current context.
Also you have one view and two view models that are trying to bind to it. create one viewmodel with both Educations and WorkExperience as properties.
something like
var vm = {
Educations : educationViewModel,
WorkExperience: workExperienceViewModel
}
ko.applyBindings(vm);
If you want to bind two view-models separately, you must define which section of your view to bind to. You do this by providing the element to ko.applyBindings as the second parameter.
ko.applyBindings(viewModel, document.getElementById("divtable1"));
In my Razor view I have a table with 3 non displayed properties. Id_Loaction, Id_Level & Id_Section
Above this table I have 3 lists of checkboxes for every location, level & section with the appropriate Ids as the value.
#foreach (var section in Model.Sections)
{
<li>
<input type="checkbox" data-bind="checked: data" value="#Html.Encode(section.Value)"/>#Html.Encode(section.Text)
</li>
}
Note: section.Value is the ID of the element which is also used in the table rows
The table itself is fairly simple built:
#foreach (var item in Model.Offers)
{
<tr data-bind="visible: data.IsChecked == true ">
<td>
#Html.DisplayFor(modelItem => item.Description)
</td>
<td>
#Html.DisplayFor(modelItem => item.Location)
</td>
<td>
#Html.DisplayFor(modelItem => item.Section.Text)
</td>
<td>
#Html.DisplayFor(modelItem => item.Section.Value)
</td>
<td>
#Html.DisplayFor(modelItem => item.Section.IsChecked)
</td>
<td>
#Html.DisplayFor(modelItem => item.Level)
</td>
<td>
#Html.ActionLink("Details", "Details", new { id=item.ID })
</td>
</tr>
}
<script type="text/javascript">
var data = #Html.Raw(Json.Encode(Model.Sections));
function FormViewModel(data) {
this.data = ko.observableArray(data);
}
window.viewModel = new FormViewModel(data);
console.log(viewModel.data());
ko.applyBindings(viewModel);
</script>
/edit:
I have updated the code to my actual mess. The Model.Sections is a ViewModel, which is identical to the ViewModel used for Model.Offers.Section. Both contain a Value, Text and IsChecked property.
How can I compare both those and display only the table rows on which the section is checked?
Please go slow on me.
Kind regards
this is an attempt at using knockout with a trimmed down version of what i am assuming Serv is trying to do. This is an example of a simple filter on a list of objects and can easily be expanded to include more filters and more complex models.
Fiddle http://jsfiddle.net/zTRq2/3/
Simple Filter ViewModel:
function filterViewModel(data) {
var self = this;
self.text = ko.observable(data.text);
self.checked = ko.observable(data.checked);
}
Trimmed down Offer View Model:
function offerViewModel(offer) {
var self = this;
self.description = offer.description;
self.location = offer.location;
}
Main View Model:
function FormViewModel(data) {
var self = this;
// map data from server to locations filter
self.locations = ko.observableArray(ko.utils.arrayMap(data.locations, function (filter) {
return new filterViewModel(filter);
}));
self.offersView = ko.computed(function () {
// get list of offers that were passed in, we'll manipulate filteredOffers from now on
var filteredOffers = data.offers;
// get all selected locations view models
var selectedFilterLocations = ko.utils.arrayFilter(self.locations(), function (location) {
return location.checked();
});
// run through util function to filter only offers that match any selected location
filteredOffers = ko.utils.arrayFilter(filteredOffers, function (offer) {
var matchesLocation = false;
ko.utils.arrayForEach(selectedFilterLocations, function (location) {
if (offer.location === location.text()) matchesLocation = true;
});
return matchesLocation;
});
return filteredOffers;
});
};
HTML:
<!-- list of location checkboxes -->
<ul class="unstyled inline" data-bind="foreach: locations">
<li>
<input type="checkbox" data-bind="checked: checked" /> <span data-bind="text: text"></span>
</li>
</ul>
<!-- table which is bound to the offersView computed observable -->
<!-- offersView is recalculated everytime a selection changes from the filter list -->
<table class="table-bordered">
<tbody data-bind="foreach: offersView">
<tr>
<td data-bind="text: description"></td>
<td data-bind="text: location"></td>
</tr>
</tbody>
</table>
I would not use jQuery's each unless you have to.
knockout provides your view the foreach method. Use that and an if statement or a visible binding.
<ul data-bind="foreach: entity">
<!-- ko if:checked() -->
<!-- display your Li here -->
<li></li>
<!-- /ko -->
</ul>
alternatively you can remove the ko if container less statement and use the following instead
<li data-bind="visible: checked()"></li>
but remember both of those tests are saying if checked equals true. If your value is a string you will need to test against a string.
Edit
if you have some id or value to test against you can do it like this -
<li data-bind="visible: section.checked() === true"></li>
or some other logical test
You need to set style of tr to be none if its checked (set to non 'none' value if want to show only checked one), thats all
Eg:
var viewModel= function(){
var self=this;
self.tableData=ko.observableArray(),
init=function(){
for(var i=0;i<5;i++){
//Put all your data here
var data={
selected:ko.observable(false),
id:ko.observable('loc'+i),
desc:'desc'+i
};
self.tableData.push(data);
}
};
init();
}
ko.applyBindings(new viewModel());
then HTML is like
<div data-bind="foreach:tableData">
<input type="checkbox" data-bind="checked:selected"/><span data-bind="text:id"></span>
</div>
<table>
<thead>
<th>Location</th>
<th>Desc</th>
</thead>
<tbody data-bind="foreach:tableData">
<tr data-bind="{style:{display:selected()?'none':''}}">
<td data-bind="text:id"></td>
<td data-bind="text:desc"></td>
</tr>
</tbody>
</table>
here is the jsfiddle :
http://jsfiddle.net/Dhana/keT7B/
By "row" I assume you mean li. The each() method that is part of the jQuery library is used for iterating through an array, in this case an array of matched elements returned by the jQuery selector $('input[type="checkbox"]').
$('input[type="checkbox"]').each(function(){
if(this.checked)
$(this).closest('li').hide()
})
http://jsfiddle.net/sailorob/4bC2P/
http://api.jquery.com/jQuery.each/
If you:
give an id to ul containing the checkboxes
give an id to the table
use some convention for a tr css class that includes the id
then you can - jsfiddle:
$('#sectionslist input').change(function() {
$('#offersTable .offerId' + $(this).val()).toggle();
});
I'm guessing you just want to do some simple client-side filtering? Have a look at this fiddle: http://jsfiddle.net/dzul1983/e6ADe/2/
Since there will be many sections, it's not ideal to hardcode them in JS. Instead, you could use .NET to output them in the TR element.
e.g. (if you use my fiddle)
<tr data-bind="visible: isChecked('#Html.Encode(item.Section.Value)') ">