Detecting Selected Row in html table Knockout/ASP.NET MVC - asp.net-mvc-4

I've loaded an ASP.NET MVC viewModel into KnockoutJS using ko.mapping.fromJS(Model).
My viewModel looks something like this:
public IEnumerable<FunkyThing>funkyThings;
public FunkyThing selectedFunkyThing;
I've then got in my HTML View a table which looks something like this
<tbody data-bind="foreach: funkyThings">
<tr>
<td data-bind="text:name"></td>
<td data-bind="text:funkiness"></td>
<td>
</td>
</tr>
</tbody>
and all is good with this table. Clicking the select funky thing link happily calls the selectFunkyThing function:
model.selectFunkyThing= function (funkyThing) {
window.location = '#Url.Action(MVC.FunkyThingController.Index())' + "?funkyThingID=" + funkyThing.funkyThingID();
};
which in turn refreshes the page. The MVC viewmodels is reloaded and selectedFunkyThing is populated with the selected FunkyThing and the knockout view models are then re-read from the MVC viewmodel. So far so good.
I then wanted to update the table to highlight the selected entry.
So I updated the tr line as follows:
<tr data-bind="css:{'selected': $root.isSelected()}">
and created the new knockout function:
model.isSelected = function (funkyThing) {
return funkyThing.funkyThingID== model.selectedFunkyThing.funkyThingID;
};
but... it's not working.
Chrome throws a javascript exception stating that the FunkyThing parameter is undefined.
Technically I figure I could solve it by changing the MVC viewModel to actually set a isSelected on each FunkyThing within the array. However I figure there's got to be away of doing this from Knockout?

You were close! I added the ko.utils.unwrapObservable call because I bet the funkyThingID is an observable and not just a straight property - you did this yourself in your selectFunkyThing function. You could just execute them as well. I like the verbosity of unwrap though.
model.isSelected = function (funkyThing) {
var thisId = ko.utils.unwrapObservable(model.selectedFunkyThing.funkyThingID);
return ko.utils.unwrapObservable(funkyThing.funkyThingID) == thisId;
};
and then in your document you actually have to execute this function when ko parses the binding
<tr data-bind="css:{'selected': $root.isSelected($data)}">

Are those properties not both observables, so you need to reference them as functions? You also need to make your function a computed observable, I think:
model.isSelected = ko.computed(function (funkyThing) {
return funkyThing().funkyThingID() == model.selectedFunkyThing().funkyThingID();
});

Related

Send Dynamic Table as Model back to the controller

I would like to send a dynamic table back to the controller by using its model.
Following situation. On the view I am creating a table with javascript which allows the users to insert different values. These values are picked from pre-defined comboboxes:
$(document).ready(function () {
$('#btn_insert').on('click', function ()
var table=document.getElementById("table_computerimport");
var table_len=(table.rows.length)-1;
var row = table.insertRow(table_len).outerHTML="<tr id='row"+table_len+"'><td id='computername_row"+table_len+"'>"+computername+"</td><td id='uuid_row"+table_len+"'>"+"HERE UUID"+"</td><td id='collection_row"+table_len+"'>"+" HERE Collection Name"+"</td><td><input type='button' value='Delete' class='delete' onclick='delete_row("+table_len+")'></td></tr>";
});
This is working fine, so that at least the table is created with n entries.
<div>
<button type="button" id="btn_insert" class="btn btn-primary">Insert</button>
<div >
<table class="table" id="table_computerimport">
<thead>
<tr>
<th>Hostname</th>
<th>MacAddress</th>
<th>GUID</th>
</tr>
</thead>
<tr>
</tr>
</table>
</div>
</div>
These entries I would like to pass back to the controller by using its model:
public IEnumerable<ComputerImportModel> TableComputerImport {get; set;}
#model ViewModels.ComputerImportViewModel
So I don’t want to loop though the list of objects and publish them in the view, I would like to create n records in one view at once and pass these back to the controller by using the model TableComputerImport.
I guess in Javascript somehow I have to say something like this: input asp-for="ComputerImport.hostname"
Update,
ok, I think I "solved" it. What I did is that I created an array with javascript out of the table and passed this by ajax to the controller. It was important that the array has the name attributes like the class does so that I can use this object in the constructor as a well defined object.
As I mentioed, I had to use an array which I send by AJAX to the controller. Because of the identical names in the table and in the model, everything goes its correct way:
var singlecomputerimport = [];
var headers = [];
$('#table_computerimport th').each(function(index, item) {
headers[index] = $(item).html();
});
$('#table_computerimport tr').has('td').each(function() {
var arrayItem = {};
$('td', $(this)).each(function(index, item) {
if(headers[index] !== undefined){
//console.log($(item));
//arrayItem[headers[index].toLowerCase()] = $(item).html();
arrayItem[headers[index].toLowerCase()] = $(item).attr('value');
}
});
singlecomputerimport.push(arrayItem);
});

Angular2 table is not refreshing data

I have an Angular2 application which is getting Json data and displaying it as a table. When I try to refresh / Update it with new data, table is showing old data only.
getting data through service like below:
#Input()
participants:Participant[];
testClasses: TestClass[] = [];
ngOnChanges() {
let codes="";
for (let item of this.participants)
{
codes = codes.concat(item.Id).concat(",");
}
this.buildTable(codes);
}
buildTable(codes){
let url = this.serverUrl + "api/test?codes=" + codes;
// passing input participants as parameter in url
this.testService.getData(url)
.subscribe(data => this.onDataUpdate(data));
}
onDataUpdate(data: any) {
this.testClasses = data;
// here I am getting new/refreshed data.
}
displying in table like below:
<table id="testGrid">
<tr *ngFor="let testClass of testClasses">
<td>{{testClass.name}}</td>
<td *ngFor="let t of (testClass.Score)">
{{ t }}
</td>
</tr>
</table>
When page loads data first time, table is showing data, but when I try to refresh data, I can see new refreshed data in onDataUpdate funcion, but html table is not refreshing view with new data.
Any idea or help would be much appreciated.
I tried below link:
https://github.com/swimlane/ngx-datatable/issues/255
Angular 2 - View not updating after model changes
I made my own example and I was able to reproduce the behaviour.
So what going on here it's that you use an array as input. (participants:Participant).
And after what you said about your checkboxes its seems that you mutate your array. It has for effect that the reference of the array is still the same therefor angular don't detect a change. (by the way I don't know if it's an expected behaviour).
A solution to your problem could be to create a new array of participants at each change on checkboxes.
Something like that:
this.participants = this.participants.slice(0)
You could also look at this answer: Angular2 change detection: ngOnChanges not firing for nested object

Aurelia ui-virtualization infinite-scroll-next function not being hit after data refresh

I'm using aurelia's ui-virtualization library to create a table using virtual-repeat and infinite-scroll-next. Html looks something like this:
<table>
<thead>
...
</thead>
<tbody>
<tr virtual-repeat.for="item of items" infinite-scroll-next="getMore">
...
</tr>
</tbody>
</table>
This works great, except I have certain ui components and interactions that update what is in my list array. Once that has be updated, the infinite-scroll-next function (getMore()) is never called. I update something like this:
update() {
let vm = this;
vm.apiService.getData(vm.filterOption)
.then(response => {
vm.items = response.content.items;
});
}
Where filterOptions are changed through the ui and apiService.getData() returns a promise from an http call. The data in the table updates correctly, but the infinite scroll is then broken.
Am I doing something wrong, or is this a bug in ui-virtualization?
Edit:
It appears there are some properties added to the array __array_observer__ and __observers__. Thinking overwriting the whole array and thus removing these properties might be causing the problem, I tried an approach where I just add or remove elements appropriately. This does not work either.
Edit:
It seems to fail if I leave fewer than 7 of the original elements in the array.

Using jQuery DataTables with Aurelia

I need some advice regarding the usage of jQuery DataTables with Aurelia. Basically I'm running into two problems.
I can't determine the best way to initialize it AFTER the repeat.for binding loop has completed. Apparently that loop is still working even after the attached() lifecycle is fired.
If I use $(myRef).DataTables(data: myArray) method to populate the table, and insert links (<a href-route=... click.delegate=...>) into that table, Aurelia doesn't seem to recognize the links or activate the router.
Problem 1: Here's my attempt to populate the table using Aurelia's binding. Aurelia correctly makes the table, and I can just wait 2-3 seconds and then load DataTables, but that's not the right way. I don't have a definitive event to trigger the loading of the DataTables class because I don't know when repeat.for is completed.
<div class="table-responsive">
<table ref="tblUserList" class="table table-condensed table-hover table-striped" width="100%">
<thead>
<tr>
<th><span t="Username"></span></th>
<th><span t="First_name"></span></th>
<th><span t="Last_name"></span></th>
</tr>
</thead>
<tbody>
<tr repeat.for="record of records">
<td><a route-href="route: user; params.bind: {id: record.user_id}" click.delegate="$parent.select(record)">${record.user_username}</a></td>
<td>${record.p_fname}</td>
<td>${record.p_lname}</td>
</tr>
</tbody>
</table>
</div>
Problem 2: Here's my attempt to populate the table using the jQuery method. DataTables successfully loads the table, but Aurelia doesn't recognize the links or trigger action.
$(this.tblUserList).dataTable({
"paginate": true,
"pageLength": 10,
data: this.records,
columns: [
{ data: 'user_username',
fnCreatedCell: function (nTd, sData, oData, iRow, iCol) {
$(nTd).html("<a route-href='route: user; params.bind: {id:" + oData.user_id + "}' click.delegate='$parent.select(" + oData.user_id + ")'>" + oData.user_username + "</a>");
}
},
{ data: 'p_fname' },
{ data: 'p_lname' }
]
});
Can anyone help me solve any one of the above problems? Or... am I approaching this whole issue the wrong way? Is it better to use the jQuery method to populate, or the Aurelia repeat.for binding loop?
I know this is old, but just in case, if it can help.
When you add DOM elements after binding, they are not aureliazed. You should use the enhance method of TemplatingEngine:
import the TemplatingEngine and inject it:
import {inject, TemplatingEngine} from 'aurelia-framework';
#inject(TemplatingEngine)
In the constructor, initialize the template engine:
constructor(templatingEngine) {
this.templatingEngine = templatingEngine;
}
in Aurelia's attached() method, do your datatable init stuff, and add a class to be able to retrieve your newly created DOM elements:
$(nTd).html("<a class='myclass' ...
Enhance your elements:
$('.myclass').each(function (index, value) {
let el = $(this);
if (!el.hasClass('au-target')) { //can't enhance already aureliazed elm
this.templatingEngine.enhance({element: el[0], bindingContext: this});
//el[0] is important to have DOM and not jQuery object
}
});
Then your binding should work.
Using the first approach (aurelia binding), remove data from the config object and load your data in the activate lifecycle hook:
import 'datatables';
export class UserList {
activate() {
this.records = [...];
}
attached() {
$(this.tblUserList).dataTable();
}
}

Assigning a Razor var using a Javascript function

On this page, a user fills out a web form, and it gets added to the list when created.
I want to filter the list so a logged in user will only see forms they made themselves.
I have some Razor code that runs a foreach loop through all available items, and I have Javascript that rips the currently logged in user's info.
is it possible to assign this data to a var in the razor code?
ex.
#{var user = getUser(); //want something like this }
#foreach (var item in Model) {
//add check if item.name == user.name here
<tr>
<td>
#Html.DisplayFor(modelItem => item.name)
</td>
etc etc
<script>
function getUser() {$.getJSON('GetLoggedUserInfo', function(data) {
return data;
});
</script>
I do not think it is a good idea to have this logic in a view. View should be as 'dumb' as possible.
You could make item filtering at data storage level or at least in controller:
return View(items.Where(x => x.name == user.name))
There is an easier way. You can get current user properties using:
HttpContext.Current.User