Knockout JS Binding and Filter Data - javascript

I have the following Problem
I have this Code to load Json Data from a external Web api
and Show it in my site this works..
but my Problem is
I must FILTER the Data with a Dropdown List
When i select the Value "Show all Data" all my Data must be Show
and when i select the Value "KV" in the Dropdown only the Data
with the Text "KV" in the Object Arbeitsort must Show..
How can i integrate a Filter in my Code to Filter my Data over a Dropdown ?
and the next is how can i when i insert on each Item where in HTML Rendered a Button
to Show Details of this Item SHOWS his Detail Data ?
when i click Details in a Item i must open a Box and in this Box i must Show all Detail Data
of this specific Item ?
$(document).ready(function () {
function StellenangeboteViewModel() {
var self = this;
self.stellenangebote = ko.observableArray([]);
self.Kat = ko.observable('KV');
$.getJSON('http://api.domain.comn/api/Stellenangebot/', function (data) {
ko.mapping.fromJS(data, {}, self.stellenangebote);
});
}
ko.applyBindings(new StellenangeboteViewModel());
});

I'll give this a go, but there's quite a few unknowns here. My suggestions are as follows:
First, create a computed for your results and bind to that instead of self.stellenangebote
self.stellenangeboteFiltered = ko.computed(function () {
// Check the filter value - if no filter return all data
if (self.Kat() == 'show all data') {
return self.stellenangebote();
}
// otherwise we're filtering
return ko.utils.arrayFilter(self.stellenangebote(), function (item) {
// filter the data for values that contain the filter term
return item.Arbeitsort() == self.Kat();
});
});
With regards the detail link, I'm assuming you are doing a foreach over your data in self.stellenangeboteFiltered(), so add a column to hold a link to show more details:
<table style="width:300px">
<thead>
<tr>
<th>Id</th>
<th>Arbeitsort</th>
<th>Details</th>
</tr>
</thead>
<tbody data-bind="foreach: stellenangeboteFiltered">
<tr>
<td><span data-bind="text: Id"> </span></td>
<td><span data-bind="text: Arbeitsort"> </span></td>
<td>Detail</td>
</tr>
</tbody>
</table>
Add a control to show details:
<div data-bind="visible: detailVisible, with: selectedItem">
<span data-bind="text: Position"> </span>
<span data-bind="text: Arbeitsort"> </span>
</div>
In your JS add a function:
// add some observables to track visibility of detail control and selected item
self.detailVisible = ko.observable(false);
self.selectedItem = ko.observable();
// function takes current row
self.showDetail= function(item){
self.detailVisible(true);
self.selectedItem(item);
};
UPDATE
Here's an updated fiddle: JSFiddle Demo

Related

KnockoutJS Grid to add items

I currently have a form that will let the users to add item to the submission, since I am very new to KnockoutJS I just made this form to accept the one Product for the submission
<script type="text/html" id="page4-template">
<h4>Strain Information : </h4>
<table>
<tr>
<td class="firstCol">Stock number : </td>
<td><span id="SummaryP1_StockNum" data-bind="text: stockNumber"></span></td>
</tr>
<tr>
<td class="firstCol">Product Needed : </td>
<td>
<span id="SummaryP1_pdtNeeded" data-bind="text: pdtNeeded"></span>
<span data-bind="visible: pdtNeeded() == 'Other'">
<span id="SummaryP1_pdtNeededPleaseExplain" data-bind="text: pdtNeededPleaseExplain"></span>
</span>
</td>
</tr>
<tr>
<td class="firstCol">Requested Ship Date : </td>
<td><span id="SummaryP1_RequestedShipDate" data-bind="text: requestedShipDate"></span></td>
</tr>
<tr>
<td class="firstCol">Aditional Information : </td>
<td><span id="SummaryP1_AdditionalInformation" data-bind="text: additionalInformation"></span></td>
</tr>
</table>
<hr>
</script>
If I need to make this form to allow users to add more item to the submission dynamically, what should I be using here, I am little confused as thee are dynamic bootstrapping, Overservable Array and all. Can anyone please suggest what could I do to simple to allow users to dynamically add item.
I would suggest three steps:
The first step would be collect into one object all those observable properties which you bind to the table's elements:
createRowItem = function(data) {
return {
additionalInformation = ko.observable(data.additionalInformation),
pdtNeeded = ko.observable(data.pdtNeeded),
pdtNeededPleaseExplain = ko.obsevable(data.pdtNeededPleaseExplain),
requestedShipDate = ko.observable(data.requestedShipDate),
stockNumber = ko.observable(data.stockNumber),
}
};
You would obtain an instance of a new rowItem...
var newRowItem = createRowItem(data);
The second step is to create an observableArray (documentation) in your existing view-model:
self.rowItems = ko.observableArray([]);
To populate that array with your collection of rowItem instances you could call self.rowItems.push(newRowItem) (documentation) but it's more efficient to obtain a reference to the inner array (i.e., the primitive array which the observableArray is watching), add the new instance to that, then tell the observableArray that its data has been updated. [The reason for this efficiency has to do with the way Knockout works internally, and tracks mutations.]
My suggestion would be to do this inside a public function on your view-model:
self.addRowItem = function(newRowItem) {
var arr = ko.unwrap(self.rowItems); // obtain the underlying array
arr.push(newRowItem); // add the new object to the underlying array
self.rowItems.valueHasMutated(); // tell Knockout that the underlying array has been modified
};
The final step is to wrap your <tr> elements in a foreach binding (documentation):
<script type="text/html" id="page4-template">
<h4>Strain Information : </h4>
<table data-bind="foreach: rowItems">
<tr>
<td class="firstCol">Stock number : </td>
<td><span id="SummaryP1_StockNum" data-bind="text: stockNumber"></span></td>
</tr>
...
</table>
You will indeed want to use an observableArray to store multiple items. Then you loop through this array with the foreach binding and you add a method on your viewmodel to push new items to this array.
Something like this:
vm.row = ko.observableArray();
vm.addRow = function () {
vm.row.push({
stockNumber: ko.observable(1),
pdtNeeded: ko.observable('Other'),
pdtNeededPleaseExplain: ko.observable('Hello'),
requestedShipDate: ko.observable(),
additionalInformation: ko.observable()
})
}
Fiddle: https://jsfiddle.net/thebluenile/2q8tbp5n/
For good measure, I also added an example of how you could remove the rows.

How do i refresh or redraw table rows

Below is the classical issue which I am facing during my app development.
I have an array of JSONObjects in my spring controller that I have to iterate in the jsp;
Also another status attribute called JSONArrayStatus is set that suggests if JSON array is empty or not.
Using jquery if JSONArray is empty I will show noDataImageDiv otherwise will show tableDIV (Binding the data from JSONArray using JSTL)
The problem I am facing is as below.
1. Edit a row in the table and click on Update. At this time I make an Ajax Call say, "UpdatedUser", which will return all the records along with the updated records. I could use refresh however thats not a recommended user experience and hence a no no.
To reflect the updated users in the table, I use jquery as below
clearing table rows table.clear().draw()
Loop the result set as follows.
redraw code
function reDrawExternalContactUsers(externalUsers) {
table.clear().draw();
var row = "";
$.each(externalUsers, function (i, field) {
row = '<tr><td></td><td></td><td class="edit">edit</td></tr>';
$("#tableDIV").append(row);
});
}
afetr this redraw or refresh process
This function is NOT working
$(".edit").click(function(){
});
This function is working
$("#tableDIV .edit").click(function(){
});
Suggest a better way of refreshing table rows, if any.
<div id="tableDIV">
<table id="tableID">
<thead>
<tr>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
if data exist
loop{
<tr>
<td></td>
<td></td>
<td class="edit">edit</td>
</tr>
} // loops ends
if close
</tbody>
</table>
</div>
<div id="noDataImageDiv"> No data image</div>
html code :
<div id="tableDIV">
<table id="tableID">
<thead>
<tr>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
if data exist
loop{
<tr>
<td class="user-name"></td>
<td></td>
<td class="edit" data-user-id="">edit</td> //set user_id in attr data-user-id
</tr>
} // loops ends
if close
</tbody>
</table>
</div>
<div id="noDataImageDiv"> No data image</div>
jquery code :
you should use click event on document
$(document).on('click', '.edit', function () {
var btn = $(this);
var user_id = btn.attr("data-user-id"); //user_id of user will update
// extra user data
$.ajax({
method: "POST",
url: url,
data: {
'id': id,
// extra data to send
},
success: function (data) {
if (data.status) // successfully user updated
{
var user = data.user;
/* you can set user data like this */
btn.closest('tr').find('.user-name').html(user.name);
}
}
});
});

checkbox filters not working as desired in AngularJS

I have a requirement to filter some properties using check boxes. Here what I wrote:
js code:
app.controller("filterCtrl", function ($scope, $http) {
$http({
method: 'GET',
url: contextPath + '/properties'
})
.then(function (response) {
var properties = response.data.properties;
var propertyFilters = response.data.filters;
$scope.properties = properties;
$scope.propertyFilters = propertyFilters;
$scope.usePropertyGroups = {};
$scope.usePropertyTypes = {};
$scope.usePropertyStates = {};
$scope.$watch(function () {
return {
properties: $scope.properties,
usePropertyGroups: $scope.usePropertyGroups,
usePropertyTypes: $scope.usePropertyTypes,
usePropertyStates: $scope.usePropertyStates
}
}, function (value) {
var filterType = [
{selected : $scope.usePropertyGroups, filterProp : 'propertyGroups'},
{selected : $scope.usePropertyTypes, filterProp : 'propertyTypes'},
{selected : $scope.usePropertyStates, filterProp : 'states'}
];
var filteredProps = $scope.propertyVOs;
for(var i in filterType){
filteredProps = filterData(filteredProps, filterType[i].selected, filterType[i].filterProp);
}
$scope.filteredProps = filteredVOs;
}, true);
});
})
var filterData = function(allData,selectedProps,equalData){
var afterFilter = [];
var selected = false;
for (var j in allData) {
var p = allData[j];
for (var i in selectedProps) {
if (selectedProps[i]) {
selected = true;
if (i == p[equalData]) {
afterFilter.push(p);
break;
}
}
}
}
if (!selected) {
afterFilter = allData;
}
return afterFilter;
};
html:
<div data-ng-controller="filterCtrl">
<div>
<table>
<thead>
<tr>
<th>property ID</th>
<th>property name</th>
<th>property description</th>
</tr>
</thead>
<tbody>
<tr data-ng-repeat="vo in filteredProps">
<td>{{vo.id}}</td>
<td>{{vo.name}}</td>
<td>{{vo.description}}</td>
</tr>
</tbody>
</table>
</div>
<div>
<div class="filter-list-container">
<ul data-ng-repeat="(key,value) in propertyFilters.filterOfGroup">
<li><input type="checkbox" data-ng-model="usePropertyGroups[key]"/>{{key}}<span> ({{value}})</span></li>
</ul>
</div>
<div class="filter-list-container">
<ul data-ng-repeat="(key,value) in propertyFilters.filterOfType">
<li><input type="checkbox" data-ng-model="usePropertyTypes[key]"/>{{key}}<span> ({{value}})</span></li>
</ul>
</div>
<div class="filter-list-container">
<ul data-ng-repeat="(key,value) in propertyFilters.filterOfStates">
<li><input type="checkbox" data-ng-model="usePropertyStates[key]"/>{{key}}<span> ({{value}})</span></li>
</ul>
</div>
</div>
I defined three filters (property group, property type, and property state). so whenever user click the corresponding check box, table will show related properties. Everything looks good, the only issue is when I select the first check box (for example property group) table shows lets say 50 property of 100 total. If I click the next one it is filtering the 50 property which I already filtered instead of filtering the whole array (which is 100 properties). I mean I want to filter the whole properties whenever the user checks multiple check boxes. I have worked a lot on filterType loops in the controller to get it done but I couldn't. I really appreciate any help on this.
I noticed that this example is very similar to my case. if I check one filter from "Pant Size" and one filter from "Shirt Size" it would show just the matched items instead of all items.
i think its filter 1 time 50/100 item 2 time 20/50 remaining 50 witch already filter so you need on every check box click first bind grid then filter it.

AngularJS: on-click hide active table and show new one?

I have an html table, and when I click on any row new table shows with more specific information about some row data. I am using ng-click, ng-repeat and ng-show. Here is what I am trying to achieve: I want to do so, when I click on some row, the table shows and when I click on the same row again the table hides and also when some row is active, if you click on another row the first table hides and the new shows. Here is my html:
<tbody>
<tr ng-repeat-start="car in carList | filter:tableFilter" ng-click="modelRow.activeRow = car.name; car.showDetails = !car.showDetails">
....
</tr>
<tr ng-repeat-end ng-show="modelRow.activeRow==car.name && car.allReviews.length!=0 && car.showDetails" class="hidden-table">
<td colspan="6">
<table class="table table-striped table-bordered table-condensed table-hover">
<tbody ng-repeat="rev in car.allReviews">
....
</tbody>
</table>
</td>
</tr>
</tbody>
Here is my controller:
carApp.controller("TableBodyCtrl", function($scope){
$scope.modelRow = { activeRow: '' };
$scope.carList = [{"name":"Ford Focus hatchback",...,"showDetails":false}...];
And initially my "showDetails" in every object in my $scope.carList array is set to false.
Then as you can see in my html I do ng-click="modelRow.activeRow = car.name; car.showDetails = !car.showDetails".
It works fine, but when I click, for example, on "Volkswagen Golf" row then on "Ford Focus hatchback" and then again on "Volkswagen Golf" row, the table would not show up.
It is happening because when I click a bit on any rows "showDetails" values in $scope.carList array are set to true not false value.
How can I fix this issue or what the alternative way to achieve my goal?
Note: also I need a solution that will not slow my website, (my $scope.carList array have hundreds of cars)
make 'showDetails' a global variable not related to car.
make function for ng-click="func(car)".
$scope.func = function(car) {
if (!$scope.showDetails) {// open info
$scope.showDetails = true;
} else if ($scope.modelRow.activeRow = car.name && $scope.showDetails) {
// info was opened for, closing
$scope.showDetails = false;
} else { //info was opened, we open new one
$scope.showDetails = true;
}
$scope.modelRow.activeRow = car.name;
}

AngularJs. Problems with ng-repeat

I'm having two tables witch renders data trough angularJs, coming from 2 c#-methods.
The tables are structured almost exactly the same. The first one below is used as I searchfield and the other one is used basiclly to render names.
My problem is that the first one works perfect, but the other one does not. And I don't see the problem. Any help would be appreciated. // Thanks!
Here are my two tables. (the first one is working)
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.18/angular.min.js"></script>
<div ng-app="searchApp">
<div ng-controller="searchController">
#*first table works*#
<span style="color: white">Search:</span> <input data-ng-click="myFunction()" ng-model="searchText">
<table style="color: white" id="searchTextResults">
<tr><th>Name</th></tr>
<tr ng-show="!!searchText.length != 0" ng-repeat="friend in friends | filter:searchText">
<td data-id="{{friend.id}}" data-ng-click="SendFriendRequest(friend.id)">{{friend.id.replace("RavenUsers/","")}}</td>
</tr>
</table>
#*Does not work*#
<input type="button" value="Get friends requests" data-ng-click="GetFriendRequests()">
<table style="color: white">
<tr><th>Friend requests</th></tr>
<tr ng-repeat="friendRequest in friendRequests">
<td data-id="{{friendRequest.UserWhoWantsToAddYou}}" data-ng-click="acceptUserRequest(friendRequest.UserWhoWantsToAddYou)">{{friendRequest.UserWhoWantsToAddYou}}</td>
</tr>
</table>
</div>
</div>
HERE IS MY SCRIPT
<script>
var App = angular.module('searchApp', []);
App.controller('searchController', function ($scope, $http) {
//Get all users to the seachFunction
$scope.myFunction = function () {
var result = $http.get("/Home/GetAllUsersExeptCurrentUser");
result.success(function (data) {
$scope.friends = data;
});
};
//Get friendRequests from other users
$scope.GetFriendRequests = function () {
var result = $http.get("/Home/GetFriendRequests");
result.success(function (data) {
$scope.friendRequests = data;
});
};
});
</script>
The first script-function called myFunction works perfect and the data coming from my c#-method looks like this:
[{"id":"RavenUsers/One"},{"id":"RavenUsers/Two"},{"id":"RavenUsers/Three"}]
The second script-function called GetFriendRequests does not work, and as far as I can see there is no difference between this data passed into here than the data passed into myFunction:
[{"userWhoWantsToAddYou":"RavenUsers/Ten"},{"userWhoWantsToAddYou":"RavenUsers/Eleven"}]
I'd suggest you use then instead of success because $http returns a promise.
If your table doesn't "render" then put a breakpoint inside success function, console.log() the data or check friendRequests inside your HTML template, e.g. using <div>{{ friendRequests | json }}</div>, to ensure you actually got data from response.
Now you do not handle exceptions at all.
Example:
result.then(function(data) {
console.log('got data')
},function(error) {
console.log('oh noes :( !');
});
Related plunker here http://plnkr.co/edit/KzY8A3
It would be helpful if you either (a) provided a plunker to your code or (b) provided the error message.
ng-repeat requires a uniquificator on each item in the repeat, which defaults to item.id. If you don't have an id field on the item, you'll need to tell angular what field to use.
https://docs.angularjs.org/api/ng/directive/ngRepeat
So I'd suggest changing
<tr ng-repeat="friendRequest in friendRequests">
to
<tr ng-repeat="friendRequest in friendRequests track by userWhoWantsToAddYou">
and see if that works.

Categories