I have a table, which shows invoices, then a nested table that shows the individual checks made for those invoices. I'm using knockout and typescript to render these tables. I am able to get the invoices to show, however the checks table doesn't show the data. Here's the code so far:
<tbody class="nohighlight" data-bind="foreach: parent.bankDrafts">
<tr>
<td><span data-bind="text: CheckID"></span></td>
<td><span data-bind="text: CheckRunID"></span></td>
<td><span data-bind="text: VendorName"></span></td>
<td><span data-bind="text: CheckDate"></span></td>
<td><span data-bind="text: FormatCurrency(CheckAmount)"></span></td>
<td><span data-bind="text: Globalize.formatCheckRunApproveStatus(ApprovalStatusID)"></span></td>
</tr>
</tbody>
Here's the typescript:
namespace CheckRunApproval {
declare let searchParameter: string;
class SearchCheckRunModel {
public searchParameter = ko.observable<string>(searchParameter || null);
public checkRuns = ko.observableArray<CheckRunModel>(null);
public bankDrafts = ko.observableArray<BankDraftInfoModel>();
}
var model = new SearchCheckRunModel();
export function GetBankDrafts(data: CheckRunModel): void {
CheckRunServiceMethods.GetBankDrafts(data.CheckRunID())
.done(bankDrafts => ko.mapping.fromJS(bankDrafts, null, model.bankDrafts));
}
}
And here's the service call:
public static GetBankDrafts(checkrunID: number): JQueryPromise<BankDraftInfo[]> {
return CommonMethods.doAjax<BankDraftInfo[]>(
"/Corp/Checks/CheckRunApprovalWS.asmx/getBankDrafts",
JSON.stringify({ checkrunID }),
"GetBankDrafts"
);
}
Now the server call does reach the server side code, passing in the correct parameters and returning the list of checks I'm trying to show as part of the invoice. However, the table itself does not have any data.
My thinking is that it has something to do with the way I'm mapping the model to the view model. It could also be the way I've setup the table itself, with the correct knockout attributes, etc. Any help would be greatly appreciated.
Edit: changing parent.bankDrafts to $parent.bankDrafts() it fixed the issue.
You have a typo in your code. Use $parent.bankDrafts instead of parent.bankDrafts in a foreach binding.
Related
I want to render a table containing a list of objects my server is sending me. I'm currently doing this:
<table>
<thead>
<tr>
<th>id</th>
<th>Name</th>
<th>Status</th>
</tr>
</thead>
<tbody data-bind="foreach: services">
<tr>
<td data-bind="text: id"></td>
<td data-bind="text: name"></td>
<td data-bind="text: status"></td>
</tr>
</tbody>
</table>
And the Knockout.js binding part:
var mappedData = komapping.fromJSON('{{{ services }}}');
ko.applyBindings({services: mappedData});
services is a variable containing JSON data and the whole page is rendered with handlebars. So far so good. I'm able to render the data received in the table.
Now the problem: I'd like to receive a notification which tells me that the status of a service has changed, and update the corresponding object within mappedData. The problem is that mappedData seems pretty opaque and I'm unable to retrieve an object and update it given its id.
Help appreciated!
Your mappedData variable at this point will be a knockout array with a bunch of objects that contain knockout observables.
So all you have to do is change the status observable in the correct object from the array.
function updateServiceStatus(id, status) {
var service = mappedData().filter(function(e) { return e.id() == id; });
if (service.length) {
service[0].status(status);
}
}
To get the object, you can write a helper function that will retrieve for you a service object. You could do something like this (assuming mappedData is an observableArray and id observable) :
function get_service_by_id(service_id){
for(var i=0;i<mappedData().length;i++){
if (mappedData()[i].id() === service_id){
return mappedData()[i];
}
}
return false;
}
So i ran into a little problem and i am having a hard time understanding what I should do to get the result I want/need.
So my application is supposed to show the route a certain object made in the last 2 hours. When the user loads the app they see several points scattered through out the map and when they click on one of those objects the route it made in the last 2 hours is shown, and a table I have is supposed to be updated with those coordinates. Now I make the call to fill the partial view when I get all the locations the object went to in the controller method.
this is how I start all of this (when the user clicks a point the following is executed)
(I am using openlayers 3 but it is irrelevant to this question)
$.ajax({
type: "GET",
url: '/Controller/GetRoutes',
dataType: "json",
success: function (result) {
alert('Added');
var layerLines = new ol.layer.Vector({
source: new ol.source.Vector({
features: [
new ol.Feature({
geometry: new ol.geom.LineString(routes, 'XY'),
name: 'Line'
})
]
})
});
map.addLayer(layerLines);
},
error: function (xhr, ajaxOptions, thrownError) {
//some errror, some show err msg to user and log the error
alert(xhr.responseText);
}
});
So as you can see from this code the method GetRoutes() is going to be responsible for getting the information on where the object has been to.
This is the controller (I omitted most of the code thats responsible for drawing the actual routes since its quite a bit chunky)
[HttpGet]
public JsonResult GetRoutes()
{
var lastpoints = get an arraylist with all the points I want
var name = get the name of the object
RouteInformation(lastPoints,name);
return Json(lastPoints, JsonRequestBehavior.AllowGet);
}
I know I should probably change something here but i do not know what.
The method that gives me the last points is not mine, but I am required to use it so I have no other choice but to accept the arrayList it returns to me.
this is the RouteInformation method
public ActionResult RouteInformation(ArrayList routeList, string name )
{
List<ObjPoint> routes = routeList.OfType<ObjPoint>().ToList();
List<ObjRoute> objRoutes = new List<ObjRoute>();
objRoutes.Add(new ObjRoute()
{
name = name,
ObjPoints = routes
});
return PartialView("RouteDetailsView", objRoutes);
}
My issue is updating/refreshing that table, I have it in a partial view but I have no idea on what I have to do in order update that table with the information I want to display (i can get that information I just canĀ“t seem to show it).
ObjPoint is composed of latitude,longitude, date, time.
This is the ObjRoute model
public class ObjRoute
{
public string name { get; set; }
public List<ObjPoint> ObjPoints { get; set; }
}
And now the Views ...
this is how the "main view" calls the partial view
<div>
#Html.Partial("routeDetailsView")
</div>
And this is my partial view
#model webVesselTracker.Models.ObjRoute
#{
ViewBag.Title = "RouteDetailsView";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<div>
<table id="routeTable" class="table">
<tr>
<th>
Name
</th>
<th>
Latitude
</th>
<th>
Longitude
</th>
<th></th>
</tr>
#if (Model != null) {
foreach (var item in Model.ObjPoints)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Latitude)
</td>
<td>
#Html.DisplayFor(modelItem => item.Longitude)
</td>
</tr>
}
}
else
{
<tr>
<td>
No object has been selected, please select one
</td>
</tr>
}
</table>
</div>
Now I know I could probably do this by adding some sort of json request in the js file and from there on build the table, but I would like to know how to do this with Razor and where I have gone wrong in the code/logic.
Am I supposed to add some ajax elsewhere?
So to summarize this:
-User sees points.
-User clicks point to see the route it made.
-App draws the route and then sends the route information to a method table so it can be added to the table
the user can see that information
Thank you for your time and if I missed something please point it out so I can fix or explain it better.
I finally gave up and looked into knockout.js, it managed to solve all the issues I was having
knockout.js
I'm in the process of replacing one hell of a lot of javascript/jquery code with knockoutjs and I'm trying to figure out the best way forward. I have no time to replace everything at the same time so I will have to integrate the knockout logic with the existing javascript...
Is there a way to populate a knockout view model from javascript which is not called from a data-bind attribute? Any help would be nice since I've not been able to find this anywhere else (at least not anything that worked).
I know what I'm mentioning here isn't the "correct" way of doing things, but I'm trying to migrate parts of the javascript code... Doing it all in one go isn't an option at the moment.
(using knockout 3.2)
Edit:
Typically the existing javascript does something like:
$('#productlist').append(productItemHtmlCode);
And I would rather have it do something like:
ViewModel.productList.push(productItemObject);
If I understand correctly, currently you have something like this:
<div id='myDiv'>
current status is: <span id='statusSpan'>Active</span>
</div>
with some corresponding javascript that might be something like:
function toggleStatus() {
var s= document.getElementById('statusSpan');
s.innerHTML = s.innerHTML == 'Active' ? 'Inactive' : 'Active';
}
And you want to change it so that the javascript is updating the viewmodel rather than manipulating the DOM?
var app = (function() {
var vm = {
statusText: ko.observable('Active'),
toggleStatus: toggleStatus
}
return vm
function toggleStatus() {
vm.statusText = vm.statusText == 'Active' ? 'Inactive' : 'Active';
}
}) ();
ko.applyBindings(app,document.getElementById('myDiv'));
And then the html would be
<div id='myDiv'>
current status is: <span id='statusSpan' data-bind="text: statusText"></span>
</div>
If that's what you're talking about, that's what Knockout is designed for. The javascript updates the viewmodel, knockout manipulates the DOM.
The example you give is easy to represent in Knockout.
the HTML:
<div>
<table data-bind="foreach: products">
<tr>
<td data-bind="text: id"></td>
<td data-bind="text: name"></td>
<td data-bind="text: category"></td>
</tr>
</table>
</div>
and in the viewmodel:
vm = {
products: ko.observableArray(), // empty array to start
addProduct: addProduct
}
return vm;
function addProduct(id, name, category) {
products.push({id: id, name: name, category:category});
}
etc.
I'm new to the world of Knockout and JavaScript so this question might be a bit on the loose side..
I'm having trouble sending my binded data from my view to the controlling JavaScript function.
The HTML:
<tbody data-bind="foreach: deals">
<tr>
<td><span data-bind="text: Name"></span></td>
<td><span data-bind="text: TotalDeals"></span></td>
<td><span data-bind="text: TotalOpenDeals"></span></td>
<td><span data-bind="text: TotalYellowDeals"></span></td>
<td><span data-bind="text: TotalRedDeals"></span></td>
<td><span data-bind="text: TotalClosedTermDeals"></span></td>
<td> </td>
<td><span data-bind="text: BusinessToInstLegal"></span></td>
<td><span data-bind="text: LawyerTAT"></span></td>
<td> <a href="#" data-bind='click: $parent.CountryRep'>View Country Report</a></td>
</tr>
</tbody>
Once the data is binded, I want the user to be able to click the "View Country Rep" and the data to be sent to this method in the corresponding js file:
self.CountryRep = function (obj) {
// Fetching deals data
//var locate = "/data/getCountryDeals"
//datacontext.GetJson(locate, obj.Name,
data[0].Name = obj.Name;
data[0].TotalDeals = obj.TotalDeals;
data[0].TotalOpenDeals = obj.TotalOpenDeals;
data[0].TotalClosedTermDeals = obj.TotalClosedTermDeals;
data[0].TotalYellowDeals = obj.TotalYellowDeals;
data[0].TotalRedDeals = obj.TotalRedDeals;
data[0].BusinessToInstLegal = obj.BusinessToInstLegal;
data[0].LawyerTAT = obj.LawyerTAT;
PlotCountry(data);
}
However I am receiving this runtime error:
JavaScript runtime error: Object doesn't support property or method 'CountryRep'
I've tried a few different things, like declaring the function without the 'var' and as a normal function, using a different call in the html (View Country Report ) to no avail.
Any suggestions?
Update: So I've changed the everything as Raheen instructed below and now it does not give the error, however it doesnt seem like its going into countryRep as I've put a breakpoint there and when I click it just loads the home page. Any thoughts on why this is happening? The html is in a file called report.regional.html in the views folder and countryRep is in a js file called report.regional.js in the viewmodels folder.
You should change your function like this. It is already enough and no need to get separate data
var CountryRep = function (obj) {
self.PlotCountry(obj);
}
Also where is CountryRep function. I assume it should be $parent.CountryRep as it is in foreach
Call it like this
data-bind='click: $parent.CountryRep'
Edit
Your view model should be something like this
function test(){
var self = this
self.deals = ko.observableArray([])
self.CountryRep = function (obj) {
self.PlotCountry(obj);
}
}
EDIT
It is going on home page because you dont have any href attribute set
<a href="countryrep.html" data-bind='click: $parent.CountryRep'>View Country Report</a>
And
self.CountryRep = function (obj) {
self.PlotCountry(obj);
return true
}
I am working on a table for a web application using MVC and knockout.js. I have experience doing web development but this is my first time using knockout. I currently have a table with 3 columns. The 2nd and 3rd are populated using a knockout function that displays the data. I tried to set up the first column the same way except using an image instead of text. I keep getting a broken image icon and an error in the brower's console.
The error the browser is giving me:
GET http://hostinfo/Sponsor/~PracticeAppImagesGWC.png 404 (Not Found)
This is my table:
<table id="sponsorTable">
<thead><tr>
<th></th><th id="sponsor">Sponsor</th><th id="description">Description</th>
</tr></thead>
<!-- Todo: Generate table body -->
<tbody data-bind="foreach: Sponsors">
<tr>
<td><img data-bind="attr: {src: Image}" /></td>
<td class="sTableInfo" data-bind="text: Name"></td>
<td class="sTableInfo" data-bind="text: Description"></td>
</tr>
</tbody>
</table>
This is my function for filling the table. (columns 2 and 3 fill properly)
function pageModel() {
var self = this;
self.Sponsors = ko.observableArray([]);
}
function Sponsor( _image, _name, _descrip)
{
var self = this;
self.Image = ko.observable(_image);
self.Name = ko.observable(_name);
self.Description = ko.observable(_descrip);
}
var viewModel = new pageModel();
viewModel.Sponsors().push(new Sponsor("~/Images/GWC.png", "name1", "info1"));
viewModel.Sponsors().push(new Sponsor("second image would go here", "name2", "info2"));
$(function () {
ko.applyBindings(viewModel);
})
I think something is escaping the slashes in the img source link, but I'm not sure and I can't figure out why.
UPDATE: my compiler is telling me this for the image tag: "Validation (HTML5): Element 'img' is missing required attribute 'src'."
The pushmust be on the observableArray itself, you need to remove the parenthesis:
var viewModel = new pageModel();
viewModel.Sponsors.push(new Sponsor("~/Images/GWC.png", "name1", "info1"));
viewModel.Sponsors.push(new Sponsor("second image would go here", "name2", "info2"));
Pass just image name from viewmodel, and static folder path can be append like this.
// in viewmodel
viewModel.Sponsors().push(new Sponsor("GWC.png", "name1", "info1"));
// inside table
<td><img data-bind="attr: {src:'/Image/'+ Image}" /></td>
Hope this helps...