Boolean value in initComplete property changes but html div doesn't
Boolean value in initComplete property changes but html div doesn't
I have a Datatable which should only display if my loadingSpinner
value is set to false
. The loadingSpinner
on the other hand should be active while the datatable and the appropriate bootstrap styling of datatable is finished. This situation I can do with the initComplete
property of datatable. This allows me to only show the datatable if also the styling to the table has been added, otherwise it would show the table twice, first a naked table and then table with styling.
But for some reason the loadingSpinner
value on the html div
element doesn't change. I can clearly change the value in my search.component.ts file but apparently that doesn't trigger the change for the div. Is it that I can't listen to initComplete
via the html template?
I can't create a testcase because it uses sensitive data from my company.
I can't share an error message because there is none!
Here is my code:
search.component.ts
export class SearchComponent implements OnInit {
//variables used for the searching
searchedContacts: Contact[] = [];
searchString = '';
subscription: Subscription;
// used for configuring the datatable
dtOptions: any;
displayTable: Boolean = false;
//variables used for the bootstrap5 modals
notifyModalBody: string;
notifyModal: Modal | undefined;
//variable used to check if error occured
isError = false;
loadingSpinner = true;
//variables used for the search html form
contactName = '';
itemIsComplete = 0;
searchForm = new FormGroup({
name: new FormControl(this.contactName, Validators.required),
isComplete: new FormControl(this.itemIsComplete, Validators.required)
});
//variables used for the edit contact html form inside edit modal
editContactForm: FormGroup;
constructor(private fb: FormBuilder, private contactSearchService: ContactSearchService, private contactService: ContactService) { }
ngOnInit(): void {
//configuring the settings for the datatable
this.createDataTable();
}
/*
* Method to submit the query to in search bar
* Before displaying the data a loading spinner is activated for 3 seconds
*/
onSubmit() {
//Setting loadingSpinner back to true for every query
this.loadingSpinner = true;
this.isError = false;
this.searchString = this.searchForm.value.name;
/*
* When clicking in the search bar, searchstring goes from empty string to null string
* changing back to empty string to prevent error
*/
if (this.searchString == null || this.searchString == undefined) {
this.searchString = '';
}
/*
//Convert completed value of option element for query-database purposes
if (this.searchCompleted == 0) {
this.isError = true;
this.loadingSpinner = false;
this.notifyModalBody = 'Bitte ein Dropdown-Feld auswählen';
this.openNotificationModal();
}
*/
//Start the search if there is no error
if (this.isError == false) {
this.fetchTableData();
}
}
//fetch TableData
fetchTableData() {
this.contactSearchService.search(this.searchString).then(
//success
() => {
this.subscription = this.contactSearchService.currentSearchedContacts.subscribe(Response => {
console.log(Response);
this.searchedContacts = Response;
});
this.subscription.unsubscribe();
},
//error
() => {
this.isError = true;
this.loadingSpinner = false;
this.notifyModalBody = 'Suche für \'' + this.searchString + '\' ergab keine Treffer! :(';
this.openNotificationModal();
}
);
}
//Method to open the notification modal
openNotificationModal() {
this.notifyModal = new bootstrap.Modal(document.getElementById('notificationModal'), {
keyboard: false
})
this.notifyModal?.show();
}
reloadDataTable() {
var datatable = $('#dt').DataTable();
//datatable reloading
datatable.destroy();
this.loadingSpinner = true;
setTimeout(() => {
this.loadingSpinner = false;
}, 1000);
this.createDataTable();
}
createDataTable() {
this.dtOptions = {
pagingType: 'full_numbers',
pageLength: 5,
lengthMenu: [5, 15, 30],
processing: true,
fixedHeader: true,
scrollX: true,
dom: '<"toolbar">Blfrtip',
buttons: {
buttons: [
{ extend: 'excel', className: 'btn btn-outline-success mb-3' },
{ extend: 'csv', className: 'btn btn-outline-success mb-3 me-3' }
],
dom: {
button: {
className: 'btn'
}
}
},
responsive: true,
language: {
emptyTable: "Keine Daten in der Tabelle vorhanden",
info: "_START_ bis _END_ von _TOTAL_ Einträgen",
infoEmpty: "Keine Daten vorhanden",
infoFiltered: "(gefiltert von _MAX_ Einträgen)",
infoThousands: ".",
loadingRecords: "Wird geladen ..",
processing: "Bitte warten ..",
paginate: {
first: "Erste",
previous: "Zurück",
next: "Nächste",
last: "Letzte"
},
aria: {
sortAscending: ": aktivieren, um Spalte aufsteigend zu sortieren",
sortDescending: ": aktivieren, um Spalte absteigend zu sortieren"
},
decimal: ",",
search: "Suche:",
thousands: ".",
zeroRecords: "Keine passenden Einträge gefunden",
lengthMenu: "Zeilen anzeigen: _MENU_",
datetime: {
previous: "Vorher",
next: "Nachher",
hours: "Stunden",
minutes: "Minuten",
seconds: "Sekunden",
unknown: "Unbekannt",
},
},
initComplete: function(){
this.loadingSpinner = false;
console.log('test');
$('#dt').show();
$('#dt').DataTable().columns.adjust().draw();
$("div.toolbar").html('<h4>Export:</h4>');
}
};
}
}
<div class="container">
<div class="row">
<div class="col-md-12">
<!-- template to show for very first query -->
<ng-template [ngIf]="searchedContacts.length == 0" [ngIfElse]="show">
<h3 class="text-center center mt-3">
Welche Kontakte suchen Sie?
</h3>
</ng-template>
<ng-template #show>
<div class="mt-4">
<div *ngIf="isError == false">
<p class="text-center center" *ngIf="searchString.length == 0">Es
werden <strong>alle</strong>
Kontakte angezeigt!</p>
</div>
<!-- html for the loading -->
<div *ngIf="this.loadingSpinner == true" id="loadingSpinner" class="text-center">
<h3>Einen Moment bitte</h3>
<div class="spinner-border" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</div>
<div *ngIf="this.loadingSpinner == false && isError == false">
<!-- datatable in bootstrap5 style starts here -->
<table class="table table-bordered table-striped" datatable
[dtOptions]="dtOptions" id="dt" style="width:100%">
<thead>
<tr>
bunch of columns...
</tr>
</thead>
<tbody>
<tr *ngFor="let contact of searchedContacts">
bunch of attributes...
</tr>
</tbody>
</table>
</div>
</div>
</ng-template>
</div>
</div>
The result is that the loadingSpinner is always loading
This question has an accepted answers - jump to answer
Answers
I guess it is a scope problem. "this" should refer to the data table and not a wider context. Just checked my code. To get the data table inside "initComplete" my code is
Using an event like "init" which can be used to do the same thing as "initComplete" the scope is different. There I don't need the code above to get the data table.
I use a spinner as well - and always turn it off on "init" or on "draw". Works perfectly fine.
Ok I understand that in
initComplete
the "this
" keyword refers to the data table and not a wider context. But that is not true! If that is so, the loading Spinner value would stay the same all the time, but the boolean changes in the typescript file, just not in the html template even if I'm reffering to "this" in the initComplete. And I just tried "init" something like that, this is the code from the usage of init:And this doesn't work, the function never gets executed...
So I'm still lost but thank you for the comment!
Do you have another idea?
Sorry, can't help you with that. But maybe @kthorngren has an idea?
If you like I can post my working spinner solution which is not embedded in any html template.
Here is actually a link that I posted earlier:
https://datatables.net/forums/discussion/comment/181146/#Comment_181146
and more that also cover the built-in data tables processing indicator which you can style to look like a spinner:
https://datatables.net/forums/discussion/comment/177491/#Comment_177491
https://datatables.net/forums/discussion/comment/163761/#Comment_163761
I agree with rf1234. The
this
object ininitComplete
is local to theinitComplete
function and is the Datatable API. Thethis.loadingSpinner = false;
statement ininitComplete
is not setting theSearchComponent
oadingSpinner
value. You can verify this using the browser's debugger.See this SO Thread for options of how to access the
loadingSpinner
in the outer scope.EDIT: Possibly do something like this:
The
that
is a copy of the object that can be access ininitComplete
. You canthat
to whatever name you feel is appropriate.Kevin
Thank you all guys for posting comments and ideas!
When I use the
that
keyword the scope is different, I can now change the global variable loadingSpinner but it is still loading forever in html template. BUT when I set theloadingSpinner
to false anywhere but NOT ininitComplete
the html display changes. I think it is that way because the initComplete runs at the end of everything, it is the last thing that happens in the application I thinkHmmm, I tend to disagree. I think you still have a scoping issue. What you could do is to assign the loading spinner to a global variable that you can access from anywhere without running into these issues. Might not be the most elegant solution but it works. I always do it that way working with data tables.
initComplete
executes after Datatables has completed initialization. rf1234 is correct that you have a scoping issue. Have you used the browser's debugger to take a look?If the loadingSpinner object is being set appropriately in
initComplete
but the spinner is not removing itself then there is something outside of Datatables happening. We will need to see the issue to further diagnose. Please post a link to your page or a running test case showing the issue.https://datatables.net/manual/tech-notes/10#How-to-provide-a-test-case
Kevin
Ok so the current situation is that the
loadingSpinner
shows up for one second (1000ms) and is then gone and just a moment later, like half a second later, the datatable initializes completely. I want to remove exactly this short time between the loadingSpinner going and the initialization of the datatable. I want that the loadingSpinner displays all the way when/after the initialization of datatable is finished and not before the datatable finished.I removed these lines of code:
These lines I had before, so that nevermind how much data the user searches it will first **display the loadingSpinner for one second **and then display the data. When the user searches and my API returns a lot of data, the loadingSpinner time **extends **the 1000ms and it will go on for a bit longer.
After removing these lines of code I only get the loadingSpinner showing on my screen and the datatable is not showing. So now the initComplete method doesn't trigger!. Before I removed the code, it did trigger the initComplete method. But now it doesn't.
So not only the loadingSpinner value doesn't change anything but also the
console.log()
I have in theinitComplete()
doesn't fire.I hope this helps to further understand because I can't provide a link because of privacy reasons of data
You can use dummy data.
I have a general solution that works for all of my pages: A loading spinner is displayed until the entire content of the page has been loaded - regardless whether it is Datatables and / or other stuff.
As I wrote above I use a special loading spinner called "busyLoad". Works fine. But you can use yours as well - if you get the scoping issue fixed
When the page is opened: I make sure no "half baked" content is being displayed using CSS, the spinner is being setup and started:
When all the ajax loading has been finished (including but not limited to Datatables): I get the content faded in and subsequently I do columns adjust and repsonsive recalc for all the potential data tables and some other stuff:
Thanks I will still try to solve my scoping issue.
I console.log(this) in the initComplete and I got his:
Looks like I only get the datatable with this
So now I use
and
And the now the timing is Right. The spinner doesn't end before the init but at the same time which is great.
but now I have the problem that my Datatable loads, shows the data but also shows "no data available". When sorting the columns the data dissapears and I end up with having one row saying "no data available".
This is new now in my code:
@rf1234 are you still there or @kthorngren?
Or should I post a new question for this one "getting the data but saying no data available"?
UPDATE:
I have it now that my data is displayed AND it is valid data, so my Datatable recognized the "fetching Data" process.
Now the only problem is that my dtOptions are not being set and I don't know why.
We're happy to take a look, but as per the forum rules, please link to a test case - a test case that replicates the issue will ensure you'll get a quick and accurate response. Information on how to create a test case (if you aren't able to link to the page you are working on) is available here.
Cheers,
Colin
UPDATE #2:
By rearranging of my code the dtOption load and the behaviour is almost what I want but now the header is gone
Code:
UPDATE #3: Final Comment
My Issue was that I set the datatable to
display: none
in the beginningAfter removing that from css my app does what I want.
Thanks everyone for the comments and ideas.
Maybe my comments will help someone
Thanks for posting back, that is helpful,
Cheers,
Colin