I have a situation where i am using angularJs smart table for filtering.
html:
<section class="main" ng-init="listAllWorkOrderData()">
<table st-table="listWorkOrderResponse">
<thead>
<tr>
<th st-sort="id">ID <i></i></th>
<th st-sort="project">Project <i></i></th>
</tr>
</thead>
<tbody ng-repeat="workOrder in listWorkOrderResponse">
<tr>
<td>{{workOrder.id}}</td>
<td>{{workOrder.project}}</td>
</tr>
<tr>
<td></td>
</tr>
</tbody>
</table>
</section>
I am testing for 2 different cases.
In my controller first i call the same function but send dummy array and in the second case i send the array received from the api call.
1. Dummy data
$scope.listAllWorkOrderData = function () {
var listWorkOrderResponse = [{"id":"1","project":"project1"},{"id":2,"project":"project2"},{"id":"3","project":"project3"}];
}
2. I am using a service and fetching data through api.
$scope.listAllWorkOrderData = function () {
TestService.listAllWorkOrderData().then(function (response, status, headers, config) {
if (response != undefined && response != null) {
if (!$scope.listWorkOrderResponse) {
$scope.listWorkOrderResponse = [];
}
$scope.listWorkOrderResponse = response;
}, function (response, status, headers, config) {
console.log(response);
});
When i am using case1 the sorting works fine.
But when i use case2 the sorting does not work. Onclick of it the data just disappears.
I tried debugging to see whether the listAllWorkOrderData function is being called again when we click on the filter.But it is just called once when the page is loaded to populate the table.So that means the data is present in the listWorkOrderResponse. Then why is it not sorting?
I checked the response for both the situation by printing them the only difference i found was that the listWorkOrderResponse which comes from the api call has a $$hashKey: "object:363" added to it.
Can anyone point me what mistake i am doing.
I was able to resolve this issue by using stSafeSrc attribute
In the controller we add
$scope.listAllWorkOrderData = function () {
TestService.listAllWorkOrderData().then(function (response, status, headers, config) {
if (response != undefined && response != null) {
if (!$scope.listWorkOrderResponse) {
$scope.listWorkOrderResponse = [];
}
$scope.listWorkOrderResponse = response;
// we add one more list.
$scope.displayedWOList = [].concat($scope.listWorkOrderResponse);
}, function (response, status, headers, config) {
console.log(response);
});
and then in the html table we add the stSafeSrc attribute.
stSafeSrc attribute from the Smart Table document
http://lorenzofox3.github.io/smart-table-website/
stSafeSrc attribute
If you are bringing in data asynchronously (from a
remote database, restful endpoint, ajax call, etc) you must use the
stSafeSrc attribute. You must use a seperate collection for both the
base and safe collections or you may end up with an infinite loop.
<section class="main" ng-init="listAllWorkOrderData()">
<table st-table="displayedWOList" st-safe-src="listWorkOrderResponse">
<thead>
<tr>
<th st-sort="id">ID <i></i></th>
<th st-sort="project">Project <i></i></th>
</tr>
</thead>
<tbody ng-repeat="workOrder in displayedWOList">
<tr>
<td>{{workOrder.id}}</td>
<td>{{workOrder.project}}</td>
</tr>
<tr>
<td></td>
</tr>
</tbody>
</table>
</section>
Why it is not working i don't know but yo can solve it by doing like below
repeat your response & create a new object & push it into an array..
var res = [];
for(var i=0; i<response.length; i++) {
var x = {"id":response[i].id, "project":response[i].project};
arr[i] = angular.copy(x);
}
Related
I have a HTML table of clients, and I want to be able to delete one of them thanks to a simple axios delete request. In order to do that, I have to get the ID of the client I want to delete.
So here is what I did :
async function deleteClient(e) {
e.preventDefault();
const id = document.getElementById("client").innerHTML;
console.log(id);
axios
.delete("http://localhost:8080/api/clients/" + id)
.then(function (response) {
console.log(JSON.stringify(response.data));
})
.catch(function (error) {
console.log(error);
});
}
The deleteClient function is called onClick in the table. The issue is, it only ever gets the ID of the first row client and never the one from rows below. What can I do to get the right ID and be able to delete other clients ?
For info, here is my HTML table, it is filled dynamically thanks to useEffect hook and a simple axios get request :
<table className="text-white border-2 border-bikeexplogray-light rounded-full">
<tr>
<th>Clients</th>
</tr>
<tr>
<th>ID</th>
<th>Store Name</th>
<th>Username</th>
<th>Email</th>
<th>Phone number</th>
<th>Address</th>
<th>Zipcode</th>
<th>City</th>
<th>Website</th>
<th>Delete</th>
</tr>{" "}
{filteredData.map((value, index) => {
return (
<tr key={value.id}>
<td id="client">{value.id}</td>
<td>{value.storeName}</td>
<td>{value.username}</td>
<td>{value.email}</td>
<td>{value.phone}</td>
<td>{value.adress}</td>
<td>{value.zipcode}</td>
<td>{value.city}</td>
<td>{value.website}</td>
<td>
<button onClick={deleteClient}>Delete</button>
</td>
</tr>
Thanks in advance for the help!
The id attribute on the td tag must be unique within the document.
You can use data-id instead, data-* is just a standard way to attach data to the DOM.
In your event handler deleteClient you can use e.currentTarget to access the context.
function deleteClient(e) {
// this will be your button element
const button = e.currentTarget
// search up the DOM tree (each parent) for this selector match
const td = button.closest("tr > td[data-id]")
// this should be the ID from the data-* attribute
console.log(td.dataset.id)
// ...
}
You can easily get the id of the customer you want to delete because you are in a map() function. Each one of your rows have a unical key that is equal to value.id, so you can modify the button as follows to do the trick :
<button onClick={(e) => deleteClient(e, value.id)}>Delete</button>
And don't forget to modify your deleteClient function :
async function deleteClient(e, id) {
e.preventDefault();
//const id = document.getElementById("client").innerHTML;
console.log(id);
axios
.delete("http://localhost:8080/api/clients/" + id)
.then(function (response) {
console.log(JSON.stringify(response.data));
})
.catch(function (error) {
console.log(error);
});
}
Controller:
$scope.init = function(team){
$http.get("/getLeaderByTeamID/"+team.id)
.success(function (data, status, headers, config) {
// this is the object that I need to list in the table
$scope.leader = data;
})
.error(function (data, status, header, config) {
$log.error("Error!");
});
}
The data-ng-repeat directives in the table:
<table>
<thead>
<tr>
<th>Team</th>
<th>Leader</th>
</tr>
</thead>
<tbody>
<tr data-ng-repeat="team in teams" data-ng-init="init(team)">
<td>{{team.name}}</td>
<td data-ng-repeat="l in leader">{{l.name}}</td>
</tr>
</tbody>
The logic is as follow:
When every team is listed the init() function will send the object to the controller.
In the other side the init() function will make a query with the ID team in order to get its respective leader.
The function will return one object and the second ng-repeat directive will list this into its respective team object.
But as I showed the list is wrong because one same object is listed in every team.
I was thinking create an array into the init() function with every object but I don't know how to concatenate every object in order to create an array.
Any suggestions?
I think you'r just updating $scope.leader value in every request ,so at the end $scope.leader will have the same value for all teams. try this.
$scope.init = function(teamId){
$http.get("/getLeaderByTeamID/"+teamId)
.success(function (data, status, headers, config) {
// this is the object that I need to list in the table
$scope.leader[teamId] = data;
})
.error(function (data, status, header, config) {
$log.error("Error!");
});
<table>
<thead>
<tr>
<th>Team</th>
<th>Leader</th>
</tr>
</thead>
<tbody>
<tr data-ng-repeat="team in teams" data-ng-init="init(team.id)">
<td>{{team.name}}</td>
<td data-ng-repeat="l in leader[team.id]">{{l.name}}</td>
</tr>
</tbody>
or you can use function return leaders array in the second ng-repeat like:
<td data-ng-repeat="l in getLeader(team.id)">{{l.name}}</td>
I am displaying some data in the view, but I need to formatted first, I was doing something like
val.toFixed(2) and that is OK, it works but the problem is that val sometimes comes with letters, and toFixed(2) is not taking that into account so is not displaying the letters.
So I need something that takes into account letters and numbers, the letters don't have to change, only the numbers which comes like 234235.345345435, and obviously I need it like this 234235.34.
Here is some of the code I am using
<table>
<tr>
<th ng-repeat='header in headers'>{{header.th}}</th>
</tr>
<tr>
<td ng-repeat='data in headers'>
<div ng-repeat='inner in data.td'>
<span ng-repeat='(prop, val) in inner'>{{val.toFixed(2)}}</span>
</div>
</td>
</tr>
</table>
and in the controller
$scope.LoadMyJson = function() {
for (var s in myJson){
$scope.data.push(s);
if ($scope.headers.length < 1)
for (var prop in myJson[s]){
prop.data = [];
$scope.headers.push({th:prop, td: []});
}
}
for (var s in $scope.data){
for (var prop in $scope.headers){
var header = $scope.headers[prop].th;
var data = myJson[$scope.data[s]][header];
$scope.headers[prop].td.push(data);
console.log($scope.headers[prop].td);
}
}
};
and I prepared this Fiddle
the way it is right now, is displaying the table properly, but as you see, the table is missing the name, it is because of the toFixed method.
So, what can I do ?
Create a custom filter to use on your template.
<table>
<tr>
<th ng-repeat='header in headers'>{{header.th}}</th>
</tr>
<tr>
<td ng-repeat='data in headers'>
<div ng-repeat='inner in data.td'>
<span ng-repeat='(prop, val) in inner'>{{val|formatValue}}</span>
</div>
</td>
</tr>
</table>
angular.module('whatever').filter('formatValue', function () {
return function (value) {
if (isNaN(parseFloat(value))) {
return value;
}
return parseFloat(value).toFixed(2);
}
});
You can try this :
That is a clean way to render formated data in view using angularjs as MVC
frontend framework :
Create a filter in your angular application.
Include your filter in your index.html.
use your filter like this : {{somedata | filterName}}
That is a simple angular filter to solve your problem, hope it will help you :
angular.module('app')
.filter('formatHeader', function() {
return function(data) {
if(angular.isNumber(data)) {
return data.toFixed(2);
}
return data;
}
});
And us it like this :
<table>
<tr>
<th ng-repeat='header in headers'>{{header.th}}</th>
</tr>
<tr>
<td ng-repeat='data in headers'>
<div ng-repeat='inner in data.td'>
<span ng-repeat='(prop, val) in inner'>{{val | formatHeader}}</span>
</div>
</td>
</tr>
You can take a look about these references :
angular functions
filter doc.
angular tutorials
I have followed the approach "DataTables: Custom Response Handling" by Fabrício Matté. However, my requirement is to avoid rendering of table's rows and columns via callback. Instead, would like to traverse the current ajax request returned json data and render explicit html (tr/td) to have more control. Due to this, currently i see data shown twice on my table. At the same time, i understand that callback is rendering the page related buttons: prev, 1,2 next etc and click events which i would like to reuse and wan't to avoid custom implementation.
JS:
function notificationsCtrl($scope,$http,$resource, DTOptionsBuilder, DTColumnBuilder) {
var vm = this;
vm.notifications = [];
$scope.dtOptions = DTOptionsBuilder.newOptions()
.withOption('serverSide', true)
.withOption('ajax', function(data, callback, settings) {
// make an ajax request using data.start and data.length
$http.get('notifications/list?page=' + (((data.start)/10)+1)).success(function(res) {
// map your server's response to the DataTables format and pass it to
// DataTables' callback
callback({
recordsTotal: 120,
recordsFiltered: 120,
data: res
});
vm.notifications = res;
});
})
.withDataProp('data') // IMPORTANT¹
.withOption('processing', true)
.withPaginationType('full_numbers');
$scope.dtColumns = [
DTColumnBuilder.newColumn('notificationId').withTitle('notificationId'),
DTColumnBuilder.newColumn('createUserId').withTitle('createUserId'),
DTColumnBuilder.newColumn('Language').withTitle('language')
];
}
HTML: sample but actual will require extra processing for some of the td tags
<table datatable="" dt-options="dtOptions" dt-columns="dtColumns" class="table table-striped table-bordered table-hover">
<thead>
<tr>
<th>Id</th>
<th>Title</th>
<th>Language</th>
<th>Last Updated</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="notification in wynkCMSToolApp.notifications">
<td>{{ notification.notificationId }}</td>
<td>{{ notification.title }}</td>
<td>{{ notification.Language }}</td>
</tr>
</tbody>
</table>
If you want to render directly in the HTML, consider using the Angular renderer. However, such renderer does not support the server side processing.
So I recommend you use the server side processing along with the columns.render function.
Here an example of using the render function.
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;
}