How to show Axios response data into DataTable using Jquery - axios

Hello guys i have this problem i have been trying to fix for days now.
Am making a post request to my PHP server to fetch results from database. Now am using JS map function to populate the results into table.
Am using DataTable which populates and show results but when i try to search for Student using DataTable search everything disappears and show "No Results Found"
Need help to make pagination and search, sorting work.
i have provided my script and html code below
student.ejs //HTML page containing students table
<thead>
<tr>
<th class="text-center">Name</th>
<th class="text-center">Index No</th>
</tr>
</thead>
<tbody id="tableData" class="text-center"></tbody>
</table>
<!-- fetch -->
<script>
axios.get('myFetchAPIHere')
.then(function(response){
let finalData = response.data;
let tableData = "";
finalData.map((values) => {
tableData += `<tr class='text-center'>`;
tableData += `<td class='fw-bolder text-dark'>${values.fullname}</td>`;
tableData += `<td class='text-primary fw-bolder'>${values.index_no}</td>`;
tableData += `</tr>`;
});
document.getElementById('tableData').innerHTML = tableData;
})
.catch(function(error){
console.log(error);
})
</script>
<script>
// Jquery Datatable
let jquery_datatable = $("#table1").DataTable({)
</script>

Order of Execution
You have two scripts in your page. The second script initializes your JavaScript datatable:
$("#table1").DataTable();
However, it is being executed before the first script finishes.
This means that DataTables finds an empty <html> table - and therefore the DataTable contains no data. When you perform any DataTables operation such as filtering or sorting, DataTables re-displays its data - which is no data at all.
Your first script starts before your second script starts... but the first thing it does is to issue an Ajax call using your axios library. This is an asynchronous event - so it waits for the response before it executes its .then function.
While it is waiting for that callback, the second script runs (as described above).
Now, after the Ajax call has returned its data, your first script builds the HTML table - and that is what you get to see in your web page.
It's not showing you a DataTable - it's just showing you a raw HTML table. And (as noted already) that is why all its data disappears as soon as you try to use the DataTable.
Quick Fix
The simplest fix is to delete that second script, and then place the following line...
let jquery_datatable = $("#table1").DataTable();
...inside the .then function, at the very end:
<script>
$(document).ready(function() {
axios.get('myFetchAPIHere')
.then(function(response) {
let finalData = response.data;
let tableData = "";
finalData.map((values) => {
tableData += `<tr class='text-center'>`;
tableData += `<td class='fw-bolder text-dark'>${values.fullname}</td>`;
tableData += `<td class='text-primary fw-bolder'>${values.index_no}</td>`;
tableData += `</tr>`;
});
document.getElementById('tableData').innerHTML = tableData;
// place the DataTables initialization command here:
let jquery_datatable = $("#table1").DataTable();
}); // note how the above command MUST BE INSIDE the "then" function
});
</script>
Note how I have also wrapped everything inside a $(document).ready(function() { ... } - so that no JavaScript starts running until the page is ready.
If you are interested, you can prove to yourself what is happening with your existing code, by adding console.log() statements to your code.
For example:
<script>
console.log( 'step 1: ' + Date.now() );
axios.get('myFetchAPIHere')
.then(function(response){
console.log( 'step 2: ' + Date.now() );
let finalData = response.data;
let tableData = "";
finalData.map((values) => {
tableData += `<tr class='text-center'>`;
tableData += `<td class='fw-bolder text-dark'>${values.fullname}</td>`;
tableData += `<td class='text-primary fw-bolder'>${values.index_no}</td>`;
tableData += `</tr>`;
});
document.getElementById('tableData').innerHTML = tableData;
console.log( 'step 3: ' + Date.now() );
})
.catch(function(error){
console.log(error);
})
</script>
<script>
// Jquery Datatable
console.log( 'step 4: ' + Date.now() );
let jquery_datatable = $("#table1").DataTable({)
console.log( 'step 5: ' + Date.now() );
</script>
Take a look at what order the steps are printed in the browser's console (F12 to open).

Quick fix I found:
I deleted the Axios call and used Ajax as suggested by andrewJames
<script>
// Jquery Datatable
let jquery_datatable = $("#table1").DataTable({
ajax:{
url:"Your API CALL",
type: "GET",
dataSrc:"",
},
// fetch all the columns in your json object array
"columns" :[
{"data" : "fullname"},
{"data" : "index_no"}
]
})
</script>

Related

SharePoint CAML + REST + Paging issue

I suppose I have found another SP bug... but maybe I do something wrong.
I have this POST request:
https://dmsdev/coll/f7c592adcb4c4e5996c2de00d444a94c/_api/Web/Lists/GetByTitle('1')/GetItems?$expand=ContentType&$select=Id,SonarDocId,ContentTypeId,EncodedAbsURL,Modified,ContentType/Name
With body:
{"query":{"ViewXml":"<View><Query><OrderBy><FieldRef Name='Modified'/></OrderBy></Query><RowLimit>50</RowLimit></View>","ListItemCollectionPosition":{"PagingInfo":"Paged=TRUE&p_Modified=2017-08-10T07:25:28"}}}
As you can see I do a CAML query with ORDER BY Modified column and I want to take items starting from the item after the item with some modified date but looks like this is not working... I mean similar request on other SP environment works, and on the other env it is not working... it takes all items starting from the first one after ordering by modified... I have no idea what is wrong :/
You could check my sample test script.
<script type="text/javascript">
function restCallwithCaml(listName, caml,PID) {
/// get the site url
var siteUrl = _spPageContextInfo.siteAbsoluteUrl;
/// set request data
var data = {
"query": {
"__metadata":
{ "type": "SP.CamlQuery" },
"ViewXml": caml,
"ListItemCollectionPosition": {
"PagingInfo": "Paged=TRUE&p_ID=" + PID
}
}
};
/// make an ajax call
return $.ajax({
url: siteUrl + "/_api/web/lists/GetByTitle('" + listName + "')/GetItems",
method: "POST",
data: JSON.stringify(data),
headers: {
"X-RequestDigest": $("#__REQUESTDIGEST").val(),
'content-type': 'application/json;odata=verbose',
'accept': 'application/json;odata=verbose'
}
});
}
function GetItemsPaging() {
var pageLimit = 2;
var pageNumber = 0;
var caml = "<View><Query><Where><Geq><FieldRef Name='ID'/><Value Type='Number'>1</Value></Geq></Where></Query><RowLimit>" + pageLimit + "</RowLimit></View>";
var listName = "ChildB";
restCallwithCaml(listName, caml, pageNumber).done(function (data) {
if (data.d.results.length == pageLimit) {
pageNumber++;
//add to array or display
var PID=data.d.results[data.d.results.length - 1].Id;
alert(PID);
restCallwithCaml(listName, caml, PID).done(function (data) {
//add to array or display
alert(data.d.results[data.d.results.length - 1].Id);
})
}
});
}
</script>
Original thread
The problem was with my understanding of how this whole thing works + time zones
I had to write a paging query eg:
Paged=TRUE&p_ID=10&p_Modified=2018-12-14T18:52:00
So I had to add p_Modified parameter from the last item from the previous page... Additionally this data has to be in UTC, so for example I can execute get query with the time returned by the CAML
https://server/site/_api/web/RegionalSettings/TimeZone/localTimeToUTC(#date)?#date='2018-12-14T11:52:00'
And date returned by this call should be passed in p_Modified.

angular 2 patch value reactive forms returns undefined

after this..
ngOnInit() {
this.sub = this.route.params.subscribe(params => {
let id = Number.parseInt(params['id']);
console.log('getting person with id: ', id);
this.peopleService
.get(id)
.subscribe(p => this.person = p);
});
}
why does
console.log(this.person);
return undefined?
code my code is based on this
It all works fine when I use it in the html, for example
{{person.id}}
and it works fine in my component.ts when I pass the data to a Material dialog using
data:this.activity
I've added
#input person:Person;
What do I need to do to be able to console.log(this.person) ?
The reason I'm asking is curiosity and also I need use patchValue on a reactive form and I get undefined if I add
this.personForm
// .patchValue({name:this.person.name});
I rewrote the subscribe line
.subscribe((response) =>{
this.person = response;
this.personForm
.patchValue({name:this.person.name});
});
and it works but probably not the nicest way to do it?

Jade + mongodb + express.js, delete form not working

My delete code is not working and I think not even firing as I don't see my console.log, I have an add button that works with a form and they look alike, this is why I don't get it.
app.js:
var db = monk('localhost:27017/mongodb');
Jade:
extends admin_menu
block content
h1.
Cocktail list
ul
each cocktail, i in cocktaillist
li
p= cocktail.name
form#form_delete_project(name="/admin/delete_cocktail", method="post", action="/admin/delete_cocktail")
input#input_name(type="hidden", placeholder="", name="_id", value="#{cocktail._id}")
button#submit_project(type="submit") delete
index.js:
router.post('/admin/delete_cocktail', function(req, res) {
console.log(id)
// Set our internal DB variable
var db = req.db;
// Get our form values. These rely on the "name" attributes
var id = req.body._id;
// Set our collection
var collection = db.get('cocktailcollection');
// Submit to the DB
collection.remove({
"_id":id
}, function (err, doc) {
if (err) {
// If it failed, return error
res.send("There was a problem removing the information to the database.");
}
else {
// And forward to success page
res.redirect("/admin/cocktail_list");
}
});
});
Jade is built on indentation. Since you are not indenting the items in your form it is not in you form. In html your code would look like this:
<form>
</form>
<input name="_id">
<button>
Since your input with _id is outside the form it is not being posted. That is why your console log is showing nothing. There is no req.body._id.And, of course, your submit-button is also outside the form. So it does nothing.
So, the first thing you should do is indent the code.

How to insert blob using knex?

Currently, I have an upload system using ng-file-upload to another server, which is working well thanks to CORS.
To manage my database I use knex (migrations and seed), and I have a specific table with a bytea column.
PostgreSQL database.
to make upload possible, I've added the busboy module to allow express to manage multipart requests, and the file is being saved to the disk with no problem.
but what I really want is to save it in the table, in the bytea column, and right now I'm with no luck on such quest.
Any guidance and better documentation are welcome.
After a long time i figured it out.
In the end it is dead simple to make upload works with angular+express+knex+postgres
first of all, there's no need to busboy, instead, you'll need the bodyParser's raw mode
second, adjust it to comport a reasonable upload size.
third, ng-file-upload will help with the upload part.
here's a few snippets if anyone is in need of it:
Upload button:
<div layout="row" layout-align="center center">
<md-button ngf-select ng-model="arquivo" class="md-raised md-primary">Selecionar arquivo</md-button>
<md-button ng-show="arquivo" ng-click="arquivo = null" class="md-raised md-warn">Cancelar</md-button>
<md-button ng-show="arquivo" ng-click="sendarquivo(arquivo)" class="md-raised md-primary" ng-disabled="arquivo.size > 4096 * 1024">Enviar arquivo</md-button>
</div>
controller.sendarquivo:
$scope.sendarquivo = function (arquivo) {
enqueteservice.uploadanexo(idenquete, arquivo).then(function () {
$scope.list();
$scope.arquivo = null;
});
};
enqueteservice.uploadanexo:
// serviço de enquete
angular.module("roundabout").factory("enqueteservice", function($http, Upload) {
return {
uploadanexo: function(idenquete, file) {
return Upload.http({
url: "/enquete/" + idenquete + "/uploadanexo/" + file.name,
method: 'POST',
headers: {
'Content-Type': 'application/octet-stream' // file.type //
},
data: file
});
}
}
});
on server side, express router:
router.post("/:idenquete/uploadanexo/:descricaoanexoenquete", function (req, res) {
knex("anexoenquete").insert({
idenquete: req.params.idenquete,
descricaoanexoenquete: req.params.descricaoanexoenquete,
dadoanexoenquete: req.body
}, "idanexoenquete").then(function (ret) {
res.send("idanexoenquete:" + ret[0]);
}).catch(function (err) {
res.status(500).send(err);
console.log(err);
});
});
for reference, the bodyParser setup at index.js
// ...
app.use(bodyParser.json({limit: 1024 * 1024}));// 1MB of json is a lot of json
// parse some custom thing into a Buffer
app.use(bodyParser.raw({limit: 10240 * 1024, type: 'application/octet-stream'})); // 10 MB of attachments
with this setup, ng-file-upload body will arrive at express router as a Buffer, wich you can pass directly to knex insert statement.
download binary content can also be easily solved as follows:
download attachment
router.get("/downloadanexo/:idanexoenquete", function (req, res) {
knex("anexoenquete").select().where({
idanexoenquete: req.params.idanexoenquete
}).then(function (ret) {
if (!ret.length)
res.status(404).send("NOT FOUND");
else {
var anexoenquete = ret[0];
res.setHeader("Content-disposition", "attachment;filename=" + anexoenquete.descricaoanexoenquete);
res.send(anexoenquete.dadoanexoenquete);
}
}).catch(function (err) {
res.status(500).send(err);
console.log(err);
});
});
hope this reference helps anyone else in the future, i'm able to shutdown an simple java app which was solving this issue for me.
The best approach is using Amazon s3 or other services to store blobs while storing metadata in sql.
If you want to store anyway, you can use sql driver and bluebird.

AngularJS ng-repeat with data from service

Originally in my app, I created controllers with very basic $http calls to get a resource by getting the ID of an object from the url ($routeParams). Ng-repeat display the results correctly.
However, I noticed refreshing in a later view (different controller) wiped out the data and broke the page. So, I created a function on the service to be used in multiple controllers, to check whether the data has is available and to react as follows:
1) If the resource is defined, return it (no API call)
2) If the resource is not defined, get the id from the url and get it from the API
3) If the resource is not defined & you can't get the ID, just return false.
However, this broke the code: the template rendered before the service returned the data, and ng-repeat did not update. The code looks like this:
angular.module('myApp', ['ngCookies'])
.config(...)
.service('myService', ['$cookies', '$http', function($cookies, $http) {
myData = {};
return {
getData:function(dataID) {
if(myData.name) {return myData);
else if (dataID && dataID !== '') {
$http.get('/api/data/' + dataID)
.success(function(data) {
myData = data.object;
$cookies.dataID = data.object.id;
return myData;
}
}
else { return false; }
}
}
}]);
function myCtrl($scope, $http, $routeParams, myService) {
$scope.data = myService.getData($routeParams.dataID);
...
}
And here's the template. It's in jade, which means rather than angle brackets, you just list the element with parameters in parenthesis right after, and content after the parenthesis.
h2 My heading
ul
li(ng-repeat='option in data')
a(href="#", ng-click='someFuncInCtrl(option.name)') {{ option.name }}
When the controller did the $http.get itself, the ng-repeat worked fine because the $scope was updated in the ".success" callback. Now that there's a service that returns the data after a slight delay, "$scope.data" is just undefined, the ng-repeat list is empty.
I used a console.log to check myData right before return "return myData", and the myData is working, it just isn't returned in time, and for whatever reason the list is not updating whenever $scope does get the data.
I looked a using $routeProvider's resolve... but that makes getting the ID from the url challenging, as the resolve object doesn't seem to have access to $routeParams. I know that $scope.$apply is supposed to help update the scope when it's altered by outside functions... but I have no clue where to put it. The most similar problem on SO didn't use a service.
I tried:
$scope.$apply($scope.data = myService.getData($routeParams.dataID));
And
$scope.$apply(function() {
$scope.data = myService($routeParams.dataID);
});
Both times I only got Error: $digest already in progress.
The problem is on the way you interact with the service. Since your getData function can return both synchronous and/or asynchronous information, you can't just use normal return(s).
$http.get('/api/data/' + dataID)
.success(function(data) {
myData = data.object;
$cookies.dataID = data.object.id;
return myData;
});
The return on the above snippet will not return anything from getData because it will be executed on the context of the $http.get success callback (and not on the getData call stack).
The best approach for handling sync and async service requests is to use promises.
Your getData function should look something like this:
getData:function(dataID) {
var deferred = $q.defer();
if(myData.name) {
deferred.resolve(myData);
} else if (dataID && dataID !== '') {
$http.get('/api/data/' + dataID)
.success(function(data) {
myData = data.object;
$cookies.dataID = data.object.id;
deferred.resolve(myData);
// update angular's scopes
$rootScope.$$phase || $rootScope.$apply();
});
} else {
deferred.reject();
}
return deferred.promise;
}
Note: You need to inject the $rootScope on your service.
And on your controller:
function myCtrl($scope, $http, $routeParams, myService) {
myService.getData($routeParams.dataID).then(function(data) {
// request was successful
$scope.data = data;
}, function() {
// request failed (same as your 'return false')
$scope.data = undefined;
});
}