I have a table on my page and custom filters for this table, so by default I'm using API call that to load data.
$('#table').bootstrapTable('refreshOptions', {
url: 'http://address:8080/events-api1/rest/Events/'
});
On the left side I have filters. This filters I'm getting thru http get method
$http({
method: "GET",
url: "http://address:8080/events-api1/rest/EventTypeCategories"
}).then(function success(response) {
$scope.categories = response.data;
}, function error(response) {
$scope.categories = response.statusText;
});
and rendering filters thru scope function
$scope.selectCat = function () {
angular.forEach($scope.categories, function (category) {
if (category.selected) {
$scope.selectedAllCat = false;
if (category.name == "Study") {
$scope.checked = true;
$('#table').bootstrapTable('refreshOptions', {
url: 'http://address:8080/events-api1/rest/Events?category=1'
});
}
else if (category.name == "Corporate") {
$scope.checked = true;
$('#table').bootstrapTable('refreshOptions', {
url: 'http://address:8080/events-api1/rest/Events?category=2'
});
}
else if (category.name == "Safety") {
$scope.checked = true;
$('#table').bootstrapTable('refreshOptions', {
url: 'http://address:8080/events-api1/rest/Events?category=3'
});
}
else if (category.name == "Partners") {
$scope.checked = true;
$('#table').bootstrapTable('refreshOptions', {
url: 'http://address:8080/events-api1/rest/Events?category=4'
});
}
else if (category.name == "Standards") {
$scope.checked = true;
$('#table').bootstrapTable('refreshOptions', {
url: 'http://address:8080/events-api1/rest/Events?category=5'
});
}
else if (category.name == "Technology") {
$scope.checked = true;
$('#table').bootstrapTable('refreshOptions', {
url: 'http://address:8080/events-api1/rest/Events?category=6'
});
}
}
});
};
html
<div class="panel-body">
<table class="table">
<tr>
<td class="col-md-1"> <input type="checkbox" ng-model="selectedAllCat" ng-click="selectAllCat()"> </td>
<td class="col-md-9">All</td>
<td class="col-md-2">
{{ categories.length }}
</td>
</tr>
<tr ng-repeat="category in categories | orderBy : 'id' ">
<td class="col-md-1"> <input type="checkbox" ng-model="category.selected" ng-click="selectCat()"> </td>
<td class="col-md-9">{{ category.name }}</td>
<td class="col-md-2">
{{ category.selected }}
</td>
</tr>
</table>
</div>
<table id="table"
data-flat="true"
data-toggle="table"
data-toolbar="#toolbar"
data-search="true"
data-show-toggle="false"
data-show-columns="true"
data-show-export="true"
data-filter-control="true"
data-events="operateEvents"
data-formatter="operateFormatter"
data-response-handler="responseHandler"
class="table-striped">
</table>
My question is what do I have to do for multiple category selections?
EDIT: With more explanation.
Your goal here would be to compose the query string to be able to filter upon multiple categories.
When you want category 1, you do a request like:
http://address:8080/events-api/rest/Events?category=1
Now, you want to filter upon category 1 and 6 with a single request, so your request should be like this:
http://address:8080/events-api/rest/Events?category=1&category=6
So, in your code you should:
1 Change the markup from:
<td class="col-md-1"> <input type="checkbox" ng-model="category.selected" ng-click="selectCat()"> </td>
to:
<td class="col-md-1"> <input type="checkbox" ng-click="updateFilter(category.id)"> </td>
2 The following function will help us to keep track of currently marked filters:
$scope.selectedFilters = [];
$scope.updateFilter = function(categoryId) {
if ($scope.selectedFilters.indexOf(categoryId) > -1) {
$scope.selectedFilters.push(categoryId);
} else {
$scope.selectedFilters.splice($scope.selectedFilters.indexOf(categoryId), 1);
}
//Optional, to reload on change.
$scope.requestEvents();
}
3: The following function to request data, based on selected filters, will work also without filters at all.
$scope.requestEvents() {
var url = 'http://address:8080/events-api/rest/Events';
if ($scope.selectedFilters.length > -1) {
var queryString = '?category=';
queryString += $scope.selectedFilters.join('&category=');
url += queryString;
}
$('#table').bootstrapTable('refreshOptions', {
'url': url
});
}
-- Old version --
Not sure if this will be clear from the first time, but it's due to lack of details from your side. So, keeping the code style as you already have it, you can write something like this.
$scope.filterByCategory = function(categories) {
//Assuming you have in categories, an array of numbers, (or
//strings, anyway they should match categories from your backend)
categories = categories.join('&category=');
$('#table').bootstrapTable('refreshOptions', {
url: 'http://address:8080/events-api/rest/Events?category=6' + categories
}
Note, depending on your backend, query string maybe should be joined by ('&category[]='), instead of ('&category=')
Related
I am rendering data using ng-repeat through GET request, which retrieves an array.
HTML
<div ng-controller="candidateCtrl" >
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>NO</th>
<th>NAMA</th>
<th>NIP</th>
<th>INSTANSI</th>
<th><span ng-show="animateCandidateAdmin" class="ion-load-a"></span></th>
</tr>
</thead>
<tbody ng-repeat="candidate in candidatesAdmin">
<tr class="well whel">
<td>{{$index + 1}}</td>
<td>{{candidate.candidate_name}}</td>
<td>{{candidate.candidate_nip}}</td>
<td>{{candidate.candidate_institusi}}</td>
<td>
<button class="btn btn-xs btn-success" ng-show="candidate.m_assesment_assesment_id == NULL" ng-click="addCandidate3(candidate.candidate_id)">
</td>
</tr>
</tbody>
</table>
</div><!-- OFF-MAINBAR -->
<div ng-repeat="item in percentage_penilaian" >
<div id="candidate_{{item.m_assesment_assesment_id}}" >
<div class="panel-body">
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>NO</th>
<th>NAMA</th>
<th>NIP</th>
<th>INSTANSI</th>
<th>BOBOT</th>
<th>SKOR</th>
<th>NILAI</th>
<th><span ng-show="animateCandidateManagerial" class="ion-load-a"></span>
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="candidate in candidates[item.m_assesment_assesment_id]" class="well whel">
<td>{{$index + 1}}</td>
<td>{{candidate.candidate_name}}</td>
<td>{{candidate.candidate_nip}}</td>
<td>{{candidate.candidate_institusi}}</td>
<td>{{candidate.percentage}}%</td>
<td ng-show="candidate.skor != NULL">
<button ng-click="$eval(arrAddCandidate[percentage_penilaian[$parent.$index+1].m_assesment_assesment_id])(candidate.candidate_id)"><i class="ion-arrow-right-a"></i> Mengikuti {{percentage_penilaian[$parent.$index+1].assesment_name}}</button>
</td>
</tr>
</tbody>
</table>
</div><!-- OFF-MAINBAR -->
</div>
</div>
</div>
</div>
JS
<script>
var SITEURL = "<?php echo site_url() ?>";
var selectionApp = angular.module("selectionApp", ["isteven-multi-select"]);
selectionApp.controller('candidateCtrl', function ($scope, $http) {
$scope.candidates = [];
$scope.arrAddCandidate = [];
$scope.getPercentagePenilaian = function () {
var url = SITEURL + 'xxx/xxx/' + 14;
$http.get(url).then(function (response) {
$scope.percentage_penilaian = response.data;
for(var i in response.data){
$scope.arrAddCandidate[response.data[i].m_assesment_assesment_id] = "addCandidate"+response.data[i].m_assesment_assesment_id;
}
})
};
$scope.getCandidateAdmin = function () {
var url = SITEURL + 'api/get_candidate_admin/' + 14;
$http.get(url).then(function (response) {
$scope.candidatesAdmin = response.data;
})
};
$scope.get_3 = function () {
var url = SITEURL + 'xxx/xxx/3__14';
$http.get(url).then(function (response) {
$scope.$apply(function () {
$scope.candidates[3] = response.data;
// $scope.candidates.push(response.data);
});
})
};
$scope.addCandidate3 = function (id) {
$scope.animateCandidateAdmin = true;
var postData = $.param({
candidate_id: id,
assesment_id: 3 });
$.ajax({
method: "POST",
url: SITEURL + "xx/xxx/xxxxx",
data: postData,
success: function (response) {
if(response=='sukses'){
$scope.animateCandidateAdmin = false;
$scope.getCandidateAdmin();
$scope.get_3();
}
}
});
};
$scope.get_5 = function () {
var url = SITEURL + 'xx/xxx/5__14';
$http.get(url).then(function (response) {
$scope.$applyAsync(function () {
$scope.candidates[5] = response.data;
// $scope.candidates.push(response.data);
});
})
};
$scope.addCandidate5 = function (id) {
$scope.animateCandidateAdmin = true;
var postData = $.param({
candidate_id: id,
assesment_id: 5 });
$.ajax({
method: "POST",
url: SITEURL + "xx/xxx/xxxxx",
data: postData,
success: function (response) {
if(response=='sukses'){
$scope.animateCandidateAdmin = false;
$scope.getCandidateAdmin();
$scope.get_5();
}
}
});
};
angular.element(document).ready(function () {
$scope.getPercentagePenilaian();
$scope.get_3;
$scope.get_5;
});
});
</script>
Response from $scope.getCandidateAdmin
[{"candidate_id":"24","candidate_name":"contoh","candidate_nip":"12345","candidate_institusi":"Institusi A","selection_selection_id":"14"}]
Response $scope.getPercentagePenilaian
[{"id":"14","m_assesment_assesment_id":"3","percentage":"50"},
{"id":"15","m_assesment_assesment_id":"5","percentage":"10"}]
Response from $scope.get_3
[{"id":"43","selection_selection_id":"14","m_assesment_assesment_id":"3"
,"candidate_id":"24","m_candidate_id" :"1","candidate_name":"contoh","candidate_nip":"12345","candidate_institusi":"Institusi A","competency_skor":null}]
After I adding candidate, I believe that the $scope.candidates array is updated correctly, however the table in my view does not change. I don't know what i'm doing wrong.
Potential Issue
I think 'm_assesment_assesment_id' is stored as a String and not a Integer, which is the root of your problem.
[
{ "id":"14",
"m_assesment_assesment_id":"3", // Note: the numbers are wrapped in quotes
"percentage":"50"
},
{ "id":"15",
"m_assesment_assesment_id":"5", // JSON parsers will interpret these as Strings, NOT numbers
"percentage":"10"}
]
Background to Understanding Problem
Since the ng-repeat is using the item.m_assesment_assesment_id property, Angular resolves these as Strings. When this String is passed into the Array, the Array is interpreted as a JavaScript Object and not an array. For example:
// Both the following evaluate to the same thing
// Using dot accessor
item.m_assesment_assesment_id
// Using square brackets to access data through Object Notation
item["m_assesment_assesment_id"]
This is why JavaScript interprets your Array as an Object, using item.m_assesment_assesment_id as a key and not and index.
<!-- item.m_assesment_assesment_id will evaluate as a String as the JSON data specifies the String format, triggering Object evaluation, not Array evaluation -->
<tr ng-repeat="candidate in candidates[item.m_assesment_assesment_id]" class="well whel">
Here is some good overview of Array Evaluation:
https://www.w3schools.com/js/js_arrays.asp
Associative Arrays Many programming languages support arrays with
named indexes.
Arrays with named indexes are called associative arrays (or hashes).
JavaScript does not support arrays with named indexes.
In JavaScript, arrays always use numbered indexes.
Solutions
There are a couple of solutions. If you have access to manipulating the JSON formatting, I would recommend correcting the problem there:
{
"id":"14",
"m_assesment_assesment_id": 3, // No quote == Integer
"percentage":"50"
}
But this would require you to have access to the server code constructing the response and there may be architectural reasons for using a string.
Alternatively, you could change your ng-repeat:
<tr ng-repeat="candidate in candidates[parseInt(item.m_assesment_assesment_id)]" class="well whel">
However there is a performance loss as this requires more processing during Angular's $digest() event. This will not be significant for small datasets, but it is worth noting.
If you don't have access to changing the JSON formatting on the server and you don't want to put extra load on the $digest event, your control could process the response data from the AJAX calls. This is often the approach recommended by Angular documentation.
angular.module("selectionApp", ["isteven-multi-select"])
.controller('candidateCtrl', function ($scope, $http) {
$scope.candidates = [];
$scope.arrAddCandidate = [];
$scope.getPercentagePenilaian = function () {
var url = SITEURL + 'xxx/xxx/' + 14;
$http.get(url).then(function (response) {
// USE OUR CONVENIENCE METHOD HERE TO CONVERT STRINGS TO INTEGERS
$scope.percentage_penilaian = processPercentagePenilaian(response.data);
for(var i in response.data){
$scope.arrAddCandidate[response.data[i].m_assesment_assesment_id] = "addCandidate"+response.data[i].m_assesment_assesment_id;
}
})
};
// *** MORE of your code exists between these functions
// ALSO YOU HAVE TO CALL THIS IN THE CALLBACK OF THE APPROPRIATE AJAX CALLBACK
var processPercentagePenilaian = function(items) {
// Loop through the Items
angular.forEach(items, function(item, key) {
// For Every Item, convert the m_assesment_assesment_id to an Integer
item.m_assesment_assesment_id = parseInt(item.m_assesment_assesment_id);
});
// End Controller
});
There are many examples of adding large datasets to a ko.observableArray using the underlying array such as this:
ko.observableArray.fn.pushAll = function(valuesToPush) {
var underlyingArray = this();
this.valueWillMutate();
ko.utils.arrayPushAll(underlyingArray, valuesToPush);
this.valueHasMutated();
return this; //optional
};
The problem with doing it this way is I lose my observables. When I use chrome and pause in debugger, I get the values in my array rather than the function c() which is the observable wrap. I have to also observe many of these variables.
I found what does work is the following:
var model = #Html.Raw(Json.Encode(Model));
vm.POs = ko.mapping.fromJS(model.POs);
The problem is that this is slow. How do I use the underlying array to add but then add the observable wrap to each variable without performance issues?
Here is some more code:
var vm = {
POs: ko.observableArray([]),
headersWithAccounting: ko.observableArray([
{header_name: "DATE CREATED", property: "DATE_CREATED", state: ko.observable('')},
{header_name: "DATE ISSUED", property: "DATE_ISSUED", state: ko.observable('')},
{header_name: "USER CREATED", property: "NAME_USER", state: ko.observable('')},
{header_name: "PO NUMBER", property: "NO_PO", state: ko.observable('')},
{header_name: "ORDER STATUS", property: "NAME_STATUS", state: ko.observable('')},
{header_name: "VENDOR", property: "NAME_VENDOR", state: ko.observable('')},
{header_name: "TOTAL COST", property: "COST_TOTAL", state: ko.observable('')},
{header_name: "CTU", property: "ID_CTU", state: ko.observable('')},
{header_name: "ACCOUNTING CODE", property: "ACCOUNTING_CODE_NAME", state: ko.observable('')},
{header_name: "CLOSE ORDER", property: "ACCOUNTING", state: ko.observable('')}
])
};
function PO() {
var self = this;
self.ID_ORDER = ko.observable();
self.DATE_CREATED = ko.observable();
self.DATE_ISSUED = ko.observable();
self.NAME_STATUS = ko.observable();
self.NAME_VENDOR = ko.observable();
self.NAME_USER = ko.observable();
self.COST_TOTAL = ko.observable();
self.ACCOUNTING_CODE_NAME = ko.observable();
self.ACCOUNTING_CODE_ID = ko.observable();
self.NO_PO = ko.observable();
self.SHOWDETAILS = ko.observable(0);
self.ID_TYPE = ko.observable(0);
self.DESCRIPTION = ko.observable('');
self.FILES = ko.observableArray();
self.POParts = ko.observableArray();
self.ACCOUNTING = ko.observable(0);
self.ID_CTU = ko.observable(0);
self.ACCOUNTING.subscribe(function(val) {
if (vm.avoidCloseOrder() == 0) {
$.ajax({
type: "POST",
url: '#Url.Action("AccountingCloseOrder", "Report")',
dataType: 'JSON',
data: {
orderId: self.ID_ORDER()
},
success: function(msg) {
if (msg != 'Good') {
window.location.href = msg;
}
},
error: function (err) {
alert("Error closing order, please try again");
}
});
}
});
self.ACCOUNTING_CODE_ID.subscribe(function(val) {
if (vm.avoidCloseOrder() == 0) {
$.ajax({
type: "POST",
url: '#Url.Action("AccountingCodeChange", "Report")',
dataType: 'JSON',
data: {
orderId: self.ID_ORDER(),
accountingCodeId: self.ACCOUNTING_CODE_ID()
},
success: function(msg) {
},
error: function (err) {
alert("Error closing order, please try again");
}
});
}
});
}
function POPart() {
var self = this;
self.CATEGORY = ko.observable();
self.SUBCATEGORY = ko.observable();
self.DESCRIPTION = ko.observable();
self.PARTNO = ko.observable();
self.QTY_ORDERED = ko.observable();
self.QTY_RECEIVED = ko.observable();
self.COST = ko.observable();
}
function FILE() {
var self = this;
self.LOCATION = ko.observable();
}
Now the issue is in the razor code with the knockout bindings:
<div class="row">
<div class="col-md-12">
<div data-bind="foreach:POs">
<table class="table-responsive">
<thead data-bind="if: $index() == 0 || ($index() > 0 && vm.POs()[$index()-1].SHOWDETAILS() == 1)">
<tr data-bind="foreach:vm.headersWithAccounting">
<th>
<span data-bind="click:$root.sortPOs.bind(property), text:header_name" style="cursor:pointer"></span><i data-bind="css: state"></i>
</th>
</tr>
</thead>
<tbody class="clickabletbody">
<tr>
<td data-bind="click:$parent.showDetailsFor">
<div data-bind="text:DATE_CREATED"></div>
</td>
<td data-bind="click:$parent.showDetailsFor">
<div data-bind="text:DATE_ISSUED"></div>
</td>
<td data-bind="click:$parent.showDetailsFor">
<div data-bind="text:NAME_USER"></div>
</td>
<td data-bind="click:$parent.showDetailsFor">
<div data-bind="text:NO_PO"></div>
</td>
<td data-bind="click:$parent.showDetailsFor">
<div data-bind="text:NAME_STATUS"></div>
</td>
<td data-bind="click:$parent.showDetailsFor">
<div data-bind="text:NAME_VENDOR"></div>
</td>
<td data-bind="click:$parent.showDetailsFor">
<div data-bind="text:COST_TOTAL"></div>
</td>
<td data-bind="click:$parent.showDetailsFor">
<div data-bind="text:ID_CTU"></div>
</td>
<td>
#Html.DropDownList("ddlVendor", new SelectList(Model.ACCOUNTING_CODE_SELECTLIST, "Value", "Text"), "--Select Accounting Code--", new { #class = "form-control", data_bind = "value:ACCOUNTING_CODE_ID" })
</td>
<td>
<input type="checkbox" style="height:30px; width: 30px;" data-bind="checked:ACCOUNTING, enable:(NAME_STATUS() == 'ACCOUNTING' || NAME_STATUS() == 'CLOSED')" /> //PROBLEM RIGHT HERE!!!!
</td>
</tr>
</tbody>
</table>
<table data-bind="if:SHOWDETAILS, fadeVisible:SHOWDETAILS" style="background-color:antiquewhite" class="table-responsive">
<!-- ko if:(ID_TYPE() == 2 || ID_TYPE() == 3) -->
<thead>
<tr>
<th>
DESCRIPTION
</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<div data-bind="text:DESCRIPTION"></div>
</td>
</tr>
</tbody>
<!-- /ko -->
<!-- ko if:(ID_TYPE() == 1) -->
<thead>
<tr>
<th>
CATEGORY
</th>
<th>
SUBCATEGORY
</th>
<th>
DESCRIPTION
</th>
<th>
PART NO
</th>
<th>
QTY ORDERED
</th>
<th>
QTY RECEIVED
</th>
<th>
COST
</th>
</tr>
</thead>
<tbody data-bind="foreach:POParts">
<tr>
<td>
<div data-bind="text:CATEGORY"></div>
</td>
<td>
<div data-bind="text:SUBCATEGORY"></div>
</td>
<td>
<div data-bind="text:DESCRIPTION"></div>
</td>
<td>
<div data-bind="text:PARTNO"></div>
</td>
<td>
<div data-bind="text:QTY_ORDERED"></div>
</td>
<td>
<div data-bind="text:QTY_RECEIVED"></div>
</td>
<td>
<div data-bind="text:COST"></div>
</td>
</tr>
</tbody>
<!-- /ko -->
</table>
<table data-bind="if:SHOWDETAILS, fadeVisible:SHOWDETAILS" style="background-color:antiquewhite" class="table-responsive">
<thead>
<tr>
<th>
Files
</th>
</tr>
</thead>
<tbody data-bind="foreach:FILES">
<tr>
<td>
<a data-bind="attr: {href: LOCATION, target: '_blank'}" class="btn btn-primary btn-md">Download File</a>
</td>
</tr>
</tbody>
</table>
<div data-bind="if:SHOWDETAILS"><hr /></div>
</div>
<!-- /ko -->
</div>
</div>
The problem is with the checkbox, chrome console says error:
knockout-3.4.0.js:72 Uncaught TypeError: Unable to process binding "enable: function (){return (NAME_STATUS() =='ACCOUNTING'|| NAME_STATUS() =='CLOSED') }"
Message: NAME_STATUS is not a function
This is because in the value it is no longer a function with knockout bindings, it is simply a value, therefore it is not a function and this error is correct. I am losing this because using the underlying array pushes only the javascript values and is not mapping the observable function.
It is taking roughly 10 seconds for 200 entries to map currently using observables, which is pretty ridiculous if you ask me. What will happen when I have 1000+. Even if I only load 50 of them starting out and use ajax to gather the rest behind the scenes, every time I get more data, it will lag the page for a few seconds until it loads them all. Not sure how to go about fixing this.
Edit:
I just had an AHA moment and fixed the losing binding problem. It is taking roughly 4 seconds now for 232 entries. Would still like to get it faster but heres what I changed.
function PO(data) {
var self = this;
self.ID_ORDER = ko.observable(data.ID_ORDER);
self.DATE_CREATED = ko.observable(data.DATE_CREATED);
self.DATE_ISSUED = ko.observable(data.DATE_ISSUED);
self.NAME_STATUS = ko.observable(data.NAME_STATUS);
self.NAME_VENDOR = ko.observable(data.NAME_VENDOR);
self.NAME_USER = ko.observable(data.NAME_USER);
self.COST_TOTAL = ko.observable(data.COST_TOTAL);
self.ACCOUNTING_CODE_NAME = ko.observable(data.ACCOUNTING_CODE_NAME);
self.ACCOUNTING_CODE_ID = ko.observable(data.ACCOUNTING_CODE_ID);
self.NO_PO = ko.observable(data.NO_PO);
self.SHOWDETAILS = ko.observable(0);
self.ID_TYPE = ko.observable(data.ID_TYPE);
self.DESCRIPTION = ko.observable(data.DESCRIPTION);
self.FILES = ko.observableArray();
if (data.FILES != null) {
for (var i = 0; i < data.FILES.length; i++) {
self.FILES.push(new FILE(data.FILES[i]));
}
}
self.POParts = ko.observableArray();
if (data.POParts != null) {
for (var i = 0; i < data.POParts.length; i++) {
self.POParts.push(new POPart(data.POParts[i]));
}
}
self.ACCOUNTING = ko.observable(data.ACCOUNTING);
self.ID_CTU = ko.observable(data.ID_CTU);
self.ACCOUNTING.subscribe(function(val) {
if (vm.avoidCloseOrder() == 0) {
$.ajax({
type: "POST",
url: '#Url.Action("AccountingCloseOrder", "Report")',
dataType: 'JSON',
data: {
orderId: self.ID_ORDER()
},
success: function(msg) {
if (msg != 'Good') {
window.location.href = msg;
}
},
error: function (err) {
alert("Error closing order, please try again");
}
});
}
});
self.ACCOUNTING_CODE_ID.subscribe(function(val) {
if (vm.avoidCloseOrder() == 0) {
$.ajax({
type: "POST",
url: '#Url.Action("AccountingCodeChange", "Report")',
dataType: 'JSON',
data: {
orderId: self.ID_ORDER(),
accountingCodeId: self.ACCOUNTING_CODE_ID()
},
success: function(msg) {
},
error: function (err) {
alert("Error closing order, please try again");
}
});
}
});
}
function POPart(data) {
var self = this;
self.CATEGORY = ko.observable(data.CATEGORY);
self.SUBCATEGORY = ko.observable(data.SUBCATEGORY);
self.DESCRIPTION = ko.observable(data.DESCRIPTION);
self.PARTNO = ko.observable(data.PARTNO);
self.QTY_ORDERED = ko.observable(data.QTY_ORDERED);
self.QTY_RECEIVED = ko.observable(data.QTY_RECEIVED);
self.COST = ko.observable(data.COST);
}
function FILE(data) {
var self = this;
self.LOCATION = ko.observable(data.LOCATION);
}
And the push function:
ko.observableArray.fn.pushAll = function(valuesToPush)
{
var underlyingArray = this();
this.valueWillMutate();
ko.utils.arrayForEach(valuesToPush, function(item) {
underlyingArray.push(new PO(item));
});
this.valueHasMutated();
return this;
}
Any ideas to make this faster than 4 seconds?
I didn't really get what you mean by "I loose my bindings". It's maybe a debugger artefact (which shows you the value of the observable).
I could also be a "this" issue.
I get this snippet which is working (and you can use the click binding while array is populated)
var elementVM = (function () {
function elementVM(message) {
this.myText = ko.observable(message);
}
elementVM.prototype.changeText = function () {
this.myText(this.myText() + " changed");
};
return elementVM;
}());
var myVM = (function() {
var getText = function(count) {
return "My Text " + (count);
};
var myObservableArray = ko.observableArray([new elementVM(getText(0))]);
return function() {
this.myArray = myObservableArray;
myVM.prototype.populate = function() {
myObservableArray.valueWillMutate();
for(var i = 1; i <= 1000; ++i)
{
myObservableArray().push(new elementVM("My Text " + i));
}
myObservableArray.valueHasMutated();
};
};
}());
var vm = new myVM();
ko.applyBindings(vm);
setTimeout(function() {
var start = new Date();
vm.populate();
var stop = new Date();
document.getElementById("pushAll").innerHTML = "pushallTiming: " + (stop - start);
}, 1000);
li {
list-style: none;
border: 1px solid black;
width: auto;
text-align: center;
}
#pushAll {
background-color: red;
width: auto;
text-align: center;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div id="pushAll"></div>
<ul data-bind="template: { name: 'my-template', foreach: myArray }"></ul>
<script type="text/html" id="my-template">
<li data-bind="text: myText, click: changeText"></li>
</script>
The actual copying to the array takes less than 1 sec. It is the valueHasMutated() function which is taking a few seconds ands thats just part of KO. I'm glad its not taking long to copy the data into the array. I will try to paginate only 50 entries which should help the DOM load faster. Thanks for everybody who responded.
I want to update a column of data in a table in MVC view.
This is the View that I want to update.
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.CurrentTemperature)
</th>
<th></th>
</tr>
#{int i = 1;}
#foreach (var item in Model)
{
<tr>
<td align="center" #*id="#("current"+i)" data-id="#item.ID"*#>
<div id="#("current"+i)" data-id="#item.ID">
#{Html.RenderAction("TemperatureUpdateDeviceList", new { idDevice = item.ID });}
</div>
</td>
</tr>
i++;
}
</table>
I wrote a simple script in order to update the divs. Just for trying I decided to update only the 4th div with id= current4.
$(function () {
setInterval(function () {
var id = $(this).attr('data-id');
$.ajax({
url: '#Url.Action("TemperatureUpdateDeviceList")',
type: 'GET',
data: { idDevice: id},
}).success(function (partialView) {
$('#current4').html(partialView);
});
},3000);
});
Using this method I can't perform a correct request because the generated URL is not correct. How to have a correct URL?
Notice that TemperatureUpdateDeviceList function is defined as:
public PartialViewResult TemperatureUpdateDeviceList(int idDevice)
{
return PartialView(temperatureModel);
}
You use of var id = $(this).attr('data-id'); will not pass the data-id value because $(this) is not your <div> element. If you want to update all your elements, then change the html to
<div class="container" data-id="#item.ID">
#{Html.RenderAction("TemperatureUpdateDeviceList", new { idDevice = item.ID });}
</div>
Note the id attribute is not necessary. Then in the script use
var url = '#Url.Action("TemperatureUpdateDeviceList")';
setInterval(function () {
$('.container').each(function() {
var id = $(this).data('id');
var div = $(this);
$.get(url, { idDevice: id}, function(partialView) {
div.html(partialView);
});
});
},3000);
I am currently using jQuery sortable to rearrange table rows in my application. I have this all working fine except I need to introduce functionality where dragging is only enabled when the user selects a team from a drop-down box on the page. The idea is that when the user first arrives onto the view, a list of team rows will be displayed. They user can select a unique team from the dropdown and filter all the rows associated with that particular team. This is when I want to be able to enable the sortable functionality. I am using the sortable library in an Angular directive, and I am filtering all table rows in the Angular controller. If someone could please point me in the right direction I would be extremely grateful. Here is my code:
myTeam.html
<div class="row">
<!-- Header Start -->
<banner-directive banner-title="Favorite Teams">
</banner-directive>
<!-- Header End -->
<div id="wrap">
<div class="container">
<div class="row">
<div class="team-order">
<div class="team-select-wrapper" style="display: inline-block">
<h2>Select team</h2>
<select class="form-control" name="teamList" ng-model="selectedteam" ng-options="team as team.name for team in teams" ng-change="onTeamSelected()">
<option value="" disabled selected>Select a Team</option>
</select>
</div>
<strong>{{teammatches.length}}</strong>Teams:
</div>
<team-order-matches matches="teammatches" teamorder="teamorder"></team-order-matches>
</div>
</div>
</div>
</div>
<div class="team-order-matches">
<div class="content">
<table class="table" id="draggable_rows">
<thead style="position: fixed; top:182px;">
<tr>
<th class="table-col-medium">
Name
</th>
<th class="table-col-medium">
Team
</th>
<th class="table-col-medium">
Amount
</th>
<th class="table-col-large">
Win
</th>
<th class="table-col-medium">
Loss
</th>
<th class="table-col-medium">
Draw
</th>
<th class="table-col-medium">
Manager
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat-start="team in teams" class="team-order-row" id="{{team._id}}">
<td class="table-col-medium" style="position: relative;">
Name
</td>
<td class="table-col-medium">
Example
</td>
<td class="table-col-medium">
Example
</td>
<td class="table-col-large">
Example
</td>
<td class="table-col-medium">
Example
</td>
<td class="table-col-medium">
Example
</td>
<td class="table-col-medium">
Example
</td>
</tr>
</tbody>
</table>
</div>
</div>
This is my controller
function TeamOrderMatchesController($scope,$location, $filter, $window, LocaleService) {
$scope.teammatches = [];
$scope.sortColumn = sortColumn;
$scope.onTeamSelected = onTeamSelected;
//filters the list to team that is selected
function onTeamSelected(){
TeamService.getMatchEvalOrder($scope.selectedteam._id).then(function(result){
$scope.runorder = result.result;
$scope.teammatches = orderByEvalOrder($scope.runorder, filterMatchesByTeam())
});
}
//Gets the unique team list for the dropdown menu
function getTeamList(){
var teams = _.map($scope.matches, function(match){
var liveVersion = _.find(match.versions, function(version){
return match.liveVersion === version.version;
});
return liveVersion.team;
});
var uniqueTeams = _.uniq(teams, function(team){
return team._id;
});
return uniqueTeams;
}
//Captures the evaluation order of the rows dragged and dropped
function orderByEvalOrder(orderedMatchIds, matches) {
var orderedMatches = [];
if(orderedMatchIds && orderedMatchIds.length > 0){
_.each(orderedMatchIds, function(orderedmatch){
var foundMatch = _.find(matches, function(m) {
return m._id == orderedmatch;
});
if(foundMatch) {
orderedMatches.push(foundMatch);
}
});
_.each(matches, function(match){
if (orderedMatchIds.indexOf(match._id) < 0) {
orderedMatches.push(match);
}
});
}
else{
orderedMatches = matches;
}
return orderedMatches;
}
//Filters the matches on the team selected
function filterMatchesByTeam() {
return _.filter($scope.matches, function(match){
var liveVersion = _.find(match.versions, function (version) {
return match.liveVersion === version.version;
});
return liveVersion.team._id === $scope.selectedteam._id;
});
}
function sortColumn(predicate, reverse) {
$scope.sortedColumn = predicate;
$scope.matches = $filter('orderBy')($scope.matches, matchPredicate, reverse);
function matchPredicate(match) {
var column;
if (shouldSortOnRoot()) {
column = match[predicate];
} else {
setVersionColumn();
}
return column;
function shouldSortOnRoot() {
return match.hasOwnProperty(predicate);
}
function setVersionColumn() {
var tempColumn = getLiveVersion(match)[predicate];
if (tempColumn.name) {
column = tempColumn.name;
} else {
column = tempColumn;
}
}
}
}
}
enter code here
exampleApp.directive('teamOrderMatches',[ '_',teamOrderMatches]);
function teamOrderMatches(_) {
return {
restrict: 'E',
scope: {
onsave: '&',
oncancel: '&',
matches: '=',
isDraggable: '&',
runorder: '='
},
link: function(scope, elem){
var table = elem.find("#draggable_rows tbody");
function updateScope() {
var ids = _.map(table.children(), function(child){
return child.id;
});
var idsWithoutEmpty = _.filter(ids, function(id) {
return id && id !== "";
});
scope.runorder = idsWithoutEmpty;
console.log(scope.teamorder);
scope.$apply();
}
table.sortable({
axis: 'y',
items: 'tr',
connectWith: 'table',
placeholder: "highlight",
tolerance: 'pointer',
scroll: true,
sort: function(event, ui) {
var currentScrollTop = $(window).scrollTop(),
topHelper = ui.position.top,
delta = topHelper - currentScrollTop;
setTimeout(function() {
$(window).scrollTop(currentScrollTop + delta);
}, 5);
},
//Helper function to keep table from collapsing when being sorted
helper: function(e, tr){
var $originals = tr.children();
var $helper = tr.clone().addClass('clone');
$helper.children().each(function(index)
{
$(this).width($originals.eq(index).width());
});
return $helper;
},
//The update function will update the table rows scope after they have been dragged to a new position
update: function(){
updateScope();
}
}).disableSelection();
},
controller: 'TeamOrderMatchesController'
};
}
I have a api call who give me the list of data, and I am iterating data via ng-repeat (its a list of more than 100 items)
For getting list of data I have call an Api in App Controller in angularjs like this:
var path = serverUrl + 'api/getAllMails';
$http.get(path).then(function (result) {
$scope.mails=result
})
For Iterating the mails in Html file i have use table like the below
<table>
<tr class="header">
<th class="center">Id</th>
<th class="center">Mode of Payment</th>
<th class="center">Payment Collected</th>
<th class="center">Status</th>
</tr>
<tr ng-repeat="mail in mails">
<td>{{mail.id}}</td>
<td>{{mail.paymentType}}</td>
<td>Rs. {{mail.cost}}
<input type="text" ng-model="mail.cost">
<button ng-click="updateCost=(mail.id, mail.cost)">Update Cost</button>
</td>
<td>{{mail.status}}
<input type="text" ng-model="mail.status">
<button ng-click="updateStatus(mail.id, mail.status)">Update Status</button>
</td>
</tr>
</table>
Suppose in the first iterations the cost will be "100" and the status will be "pending". And I have to update this row only, change cost to "1000" and status will be "Delivered".
In my App controller of Angularjs I have create methods. These two methods are calling apis and updating data in database and return the list of updated mails.
$scope.updateStatus = function(mailId, mailStatus) {
var path = serverUrl + 'api/updateStatus';
$http.get(path, {
params: {
mailId: mailId,
mailStatus: mailStatus
}
}).then(function(result) {
$scope.mails = result
})
}
$scope.updateCost = function(mailId, mailCost) {
var path = serverUrl + 'api/updateStatus';
$http.get(path, {
params: {
mailId: mailId,
mailCost: mailCost
}
}).then(function(result) {
$scope.mails = result
})
}
These code are working fine but while it took lot of time to load a page. So what can I do to reduce the loading time or is there any better way to do the same thing.
Any help will be appreciable. Thank you
You are replacing the entire dataset when there is no reason for that, you should only update the row you change. Ensure your updateStatus return the object you update and update that item in $scope.mails
In example
$scope.updateCost = function(mailId, mailCost) {
var path = serverUrl + 'api/updateStatus';
$http.get(path, {
params: {
mailId: mailId,
mailStatus: mailCost
}
}).then(function(result) {
// result is the item you changed
for (var i = $scope.mails.length - 1; i >= 0; i--) {
if($scope.mails[i].id === mailId) {
$scope.mails[i] = result;
return;
}
};
})
}