Boolean value in initComplete property changes but html div doesn't

Boolean value in initComplete property changes but html div doesn't

Maido747Maido747 Posts: 14Questions: 2Answers: 0

I have a Datatable which should only display if my loadingSpinnervalue is set to false. The loadingSpinneron 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 initCompleteproperty 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 loadingSpinnervalue on the html divelement 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 initCompletevia 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

  • rf1234rf1234 Posts: 2,990Questions: 87Answers: 421

    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

    var table = this.api();
    

    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.

  • Maido747Maido747 Posts: 14Questions: 2Answers: 0

    Ok I understand that in initCompletethe "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:

    $('#dt')
        .on( 'init.dt', function () {
            console.log( 'Table initialisation complete: '+new Date().getTime() );
        } )
        .dataTable();
    

    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?

  • rf1234rf1234 Posts: 2,990Questions: 87Answers: 421

    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

  • kthorngrenkthorngren Posts: 21,327Questions: 26Answers: 4,949
    edited February 2022

    I agree with rf1234. The this object in initComplete is local to the initComplete function and is the Datatable API. The this.loadingSpinner = false; statement in initComplete is not setting the SearchComponent 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:

      createDataTable() {
        that = this;  // Get a copy of the object
    
        this.dtOptions = {
          pagingType: 'full_numbers',
    ......
          },
          initComplete: function(){
            that.loadingSpinner = false;  // `that` use outer scope copy of object
            console.log('test');
            $('#dt').show();
            $('#dt').DataTable().columns.adjust().draw();
            $("div.toolbar").html('<h4>Export:</h4>');
          }
        };
    

    The that is a copy of the object that can be access in initComplete. You can that to whatever name you feel is appropriate.

    Kevin

  • Maido747Maido747 Posts: 14Questions: 2Answers: 0

    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 the loadingSpinnerto false anywhere but NOT in initComplete 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 think

  • rf1234rf1234 Posts: 2,990Questions: 87Answers: 421

    Hmmm, 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.

  • kthorngrenkthorngren Posts: 21,327Questions: 26Answers: 4,949

    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?

    I can now change the global variable loadingSpinner but it is still loading forever in html template

    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

  • Maido747Maido747 Posts: 14Questions: 2Answers: 0

    Ok so the current situation is that the loadingSpinnershows 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:

    setTimeout(() => {
          this.loadingSpinner = false;
        }, 1000);
    

    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 the initComplete() doesn't fire.

    I hope this helps to further understand because I can't provide a link because of privacy reasons of data :(

  • tangerinetangerine Posts: 3,365Questions: 39Answers: 395

    I can't provide a link because of privacy reasons of data

    You can use dummy data.

  • rf1234rf1234 Posts: 2,990Questions: 87Answers: 421

    I want that the loadingSpinner displays all the way when/after the initialization of datatable is finished and not before the datatable finished.

    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 :smile:

    When the page is opened: I make sure no "half baked" content is being displayed using CSS, the spinner is being setup and started:

    $('#content').css("display", "none");
    $.busyLoadSetup({ fontawesome: "fa fa-spinner fa-spin fa-3x fa-fw" });
    $.busyLoadFull("show");
    

    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:

    //must be at the beginning!! only after all ajax calls are finished may the
    //page be displayed: There is at least one ajax call: checkValidLogin!
    $(document).ajaxStop(function () {
        $("h1:has(> img), h2:has(> img), h3:has(> img)").addClass("noBullet");  
        var timeout;
        if ( ( ! isEditor ) && ( ! isCtrEditor ) ) {
            timeout = 350;
        } else {
            timeout = 10;
        }
        setTimeout(function(){  
            $.busyLoadFull("hide");
            $('#content').fadeIn('fast').promise().done(function() {
                //"api: true" - means it is a data table
                $.fn.dataTable
                    .tables( { visible: true, api: true } )
                    .columns.adjust()
                    .responsive.recalc();
                if ( tooltipsUI ) {
                    tooltipsHeadings();
                    tooltipsSelectors();        
                    tooltipsTable();
                }        
            });
            $(".blinkSomeTime").addClass("blink3");
        }, timeout);        
    });   
    
  • Maido747Maido747 Posts: 14Questions: 2Answers: 0

    Thanks I will still try to solve my scoping issue.
    I console.log(this) in the initComplete and I got his:

    S.fn.init [table#myDataTable.table.table-bordered.table-striped.dataTable.no-footer, $: ƒ, _: ƒ, api: ƒ, fnAddData: ƒ, fnAdjustColumnSizing: ƒ, …]
    0: table#myDataTable.table.table-bordered.table-striped.dataTable.no-footer
    $: ƒ (f,g)
    api: ƒ (f)
    fnAddData: ƒ (f,g)
    fnAdjustColumnSizing: ƒ (f)
    fnClearTable: ƒ (f)
    fnClose: ƒ (f)
    fnDeleteRow: ƒ (f,g,k)
    fnDestroy: ƒ (f)
    fnDraw: ƒ (f)
    fnFilter: ƒ (f,g,k,m,n,p)
    fnGetData: ƒ (f,g)
    fnGetNodes: ƒ (f)
    fnGetPosition: ƒ (f)
    fnIsOpen: ƒ (f)
    fnOpen: ƒ (f,g,k)
    fnPageChange: ƒ (f,g)
    fnSetColumnVis: ƒ (f,g,k)
    fnSettings: ƒ ()
    fnSort: ƒ (f)
    fnSortListener: ƒ (f,g,k)
    fnUpdate: ƒ (f,g,k,m,n)
    fnVersionCheck: ƒ (a)
    internal: {_fnExternApiFunc: ƒ, _fnBuildAjax: ƒ, _fnAjaxUpdate: ƒ, _fnAjaxParameters: ƒ, _fnAjaxUpdateDraw: ƒ, …}
    length: 1
    loadingSpinner: false
    oApi: {_fnExternApiFunc: ƒ, _fnBuildAjax: ƒ, _fnAjaxUpdate: ƒ, _fnAjaxParameters: ƒ, _fnAjaxUpdateDraw: ƒ, …}
    _: ƒ (f,g)
    _fnAddColumn: ƒ ()
    _fnAddData: ƒ ()
    _fnAddOptionsHtml: ƒ ()
    _fnAddTr: ƒ ()
    _fnAdjustColumnSizing: ƒ ()
    _fnAjaxDataSrc: ƒ ()
    _fnAjaxParameters: ƒ ()
    _fnAjaxUpdate: ƒ ()
    _fnAjaxUpdateDraw: ƒ ()
    _fnApplyColumnDefs: ƒ ()
    _fnApplyToChildren: ƒ ()
    _fnBindAction: ƒ ()
    _fnBrowserDetect: ƒ ()
    _fnBuildAjax: ƒ ()
    _fnBuildHead: ƒ ()
    _fnCalculateColumnWidths: ƒ ()
    _fnCalculateEnd: ƒ ()
    _fnCallbackFire: ƒ ()
    _fnCallbackReg: ƒ ()
    _fnCamelToHungarian: ƒ ()
    _fnClearTable: ƒ ()
    _fnColumnIndexToVisible: ƒ ()
    _fnColumnOptions: ƒ ()
    _fnColumnTypes: ƒ ()
    _fnConvertToWidth: ƒ ()
    _fnCreateTr: ƒ ()
    _fnDataSource: ƒ ()
    _fnDeleteIndex: ƒ ()
    _fnDetectHeader: ƒ ()
    _fnDraw: ƒ ()
    _fnDrawHead: ƒ ()
    _fnEscapeRegex: ƒ ()
    _fnExtend: ƒ ()
    _fnExternApiFunc: ƒ ()
    _fnFeatureHtmlFilter: ƒ ()
    _fnFeatureHtmlInfo: ƒ ()
    _fnFeatureHtmlLength: ƒ ()
    _fnFeatureHtmlPaginate: ƒ ()
    _fnFeatureHtmlProcessing: ƒ ()
    _fnFeatureHtmlTable: ƒ ()
    _fnFilter: ƒ ()
    _fnFilterColumn: ƒ ()
    _fnFilterComplete: ƒ ()
    _fnFilterCreateSearch: ƒ ()
    _fnFilterCustom: ƒ ()
    _fnFilterData: ƒ ()
    _fnGetCellData: ƒ ()
    _fnGetColumns: ƒ ()
    _fnGetDataMaster: ƒ ()
    _fnGetMaxLenString: ƒ ()
    _fnGetObjectDataFn: ƒ ()
    _fnGetRowElements: ƒ ()
    _fnGetUniqueThs: ƒ ()
    _fnGetWidestNode: ƒ ()
    _fnHungarianMap: ƒ ()
    _fnImplementState: ƒ ()
    _fnInfoMacros: ƒ ()
    _fnInitComplete: ƒ ()
    _fnInitialise: ƒ ()
    _fnInvalidate: ƒ ()
    _fnLanguageCompat: ƒ ()
    _fnLengthChange: ƒ ()
    _fnLengthOverflow: ƒ ()
    _fnLoadState: ƒ ()
    _fnLog: ƒ ()
    _fnMap: ƒ ()
    _fnNodeToColumnIndex: ƒ ()
    _fnNodeToDataIndex: ƒ ()
    _fnPageChange: ƒ ()
    _fnProcessingDisplay: ƒ ()
    _fnReDraw: ƒ ()
    _fnRenderer: ƒ ()
    _fnRowAttributes: ƒ ()
    _fnSaveState: ƒ ()
    _fnScrollDraw: ƒ ()
    _fnSetCellData: ƒ ()
    _fnSetObjectDataFn: ƒ ()
    _fnSettingsFromNode: ƒ ()
    _fnSort: ƒ ()
    _fnSortAria: ƒ ()
    _fnSortAttachListener: ƒ ()
    _fnSortData: ƒ ()
    _fnSortFlatten: ƒ ()
    _fnSortListener: ƒ ()
    _fnSortingClasses: ƒ ()
    _fnSplitObjNotation: ƒ ()
    _fnStringToCss: ƒ ()
    _fnThrottle: ƒ ()
    _fnUpdateInfo: ƒ ()
    _fnVisbleColumns: ƒ ()
    arguments: null
    caller: null
    length: 0
    name: ""
    prototype: {constructor: ƒ}
    [[FunctionLocation]]: node_modules\datatab…ataTables.min.js:93
    [[Prototype]]: ƒ ()
    [[Scopes]]: Scopes[3]
    _fnVisibleToColumnIndex: ƒ ()
    [[Prototype]]: Object(0)
    

    Looks like I only get the datatable with this

  • Maido747Maido747 Posts: 14Questions: 2Answers: 0

    So now I use

    $('#loadingSpinner').show();
    

    and

    $('#myDataTable').hide();
    

    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:

    onSubmit() {
        $('#loadingSpinner').show();
        $('#myDataTable').hide();
        $("#myDataTable").DataTable().columns.adjust().draw();
        $('#DivDT').hide();
    
    fetchTableData() {
        
        this.contactSearchService.search(this.searchString).then(
          //success
          () => {
            this.subscription = this.contactSearchService.currentSearchedContacts.subscribe(Response => {
              console.log(Response);
              this.searchedContacts = Response;
              $('#loadingSpinner').hide();
              $('#DivDT').show();
              $("#myDataTable").DataTable().columns.adjust().draw();
              $('#myDataTable').show();
            });
            this.subscription.unsubscribe();
    
          },
          //error
          () => {
            this.isError = true;
            this.loadingSpinner = false;
            this.notifyModalBody = 'Suche für \'' + this.searchString + '\' ergab keine Treffer! :(';
            this.openNotificationModal();
          }
        );
      }
    

    @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"?

  • Maido747Maido747 Posts: 14Questions: 2Answers: 0

    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.

  • colincolin Posts: 15,240Questions: 1Answers: 2,599

    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

  • Maido747Maido747 Posts: 14Questions: 2Answers: 0

    UPDATE #2:
    By rearranging of my code the dtOption load and the behaviour is almost what I want but now the header is gone :D
    Code:

    fetchTableData() {
        var datatable = $('#myDataTable').DataTable();
        this.contactSearchService.search(this.searchString).then(
          //success
          () => {
            this.subscription = this.contactSearchService.currentSearchedContacts.subscribe(Response => {
              console.log(Response);
              this.searchedContacts = Response;
              datatable.clear().destroy();
              setTimeout(()=>{
                $('#myDataTable').DataTable(this.dtOptions);
                $('#DivDT').show();
                $("#myDataTable").DataTable().columns.adjust().draw();
                $('#myDataTable').show();
                $('#loadingSpinner').hide();
                
                this.createDataTable();
              },1000)
            });
            this.subscription.unsubscribe();
    
          },
          //error
          () => {
            this.isError = true;
            this.loadingSpinner = false;
            this.notifyModalBody = 'Suche für \'' + this.searchString + '\' ergab keine Treffer! :(';
            this.openNotificationModal();
          }
        );
      }
    
  • Maido747Maido747 Posts: 14Questions: 2Answers: 0

    UPDATE #3: Final Comment
    My Issue was that I set the datatable to display: none in the beginning
    After removing that from css my app does what I want.
    Thanks everyone for the comments and ideas.
    Maybe my comments will help someone

  • colincolin Posts: 15,240Questions: 1Answers: 2,599
    Answer ✓

    Thanks for posting back, that is helpful,

    Cheers,

    Colin

Sign In or Register to comment.