Button Actions Quit Working When Using Web Service

Button Actions Quit Working When Using Web Service

bobc02bobc02 Posts: 19Questions: 6Answers: 0

I am using SmartAdmin Angular 5 framework 1.9.1, which provides DataTables capabilities. I have installed DataTables 1.10.18, and Select 1.2.6.

My app can use either test data, or data from a web service. When I use test data, all works fine with the Delete and Archive buttons - meaning, the button action events are fired. But, when using web service data, the Delete and Archive buttons do not fire the button actions.

You can try my app at:
tanglemydata.com

To see problem, do this:

  1. Use latest Chrome or Firefox (no mobile), make browser window wide enough to see the buttons (as shown below).
  2. Login - I've hard-coded in the login credentials.
  3. Click Notifications in left pane.
  4. Expand General Messages, wait for table to display. Data comes from web service.
  5. Select one or multiple rows.
  6. Click Delete or Archive buttons - you should see a YES/NO dialog, it does not work.
  7. Expand Colleagues, then expand Colleague Messages, wait for table to display. Test data is hard-coded.
  8. Select one or multiple rows.
  9. Click Delete or Archive buttons - you should see a YES/NO dialog, it does work.

Looking at the .ts code line 145, you see that I go through a table.clear().rows.add(data_from_web_service).draw() operation. I tried it w/wo clear(), but same problem.

I have the following component.ts code:

import { NgModule, Component, OnInit, Injectable, Inject, ViewChild, DoCheck } from '@angular/core';
import { Router } from '@angular/router';
import { FormGroup, FormControl } from '@angular/forms';

import { FadeInTop } from "../shared/animations/fade-in-top.decorator";
import { NotificationsService } from './notifications.service';
import { NotificationService } from '../shared/utils/notification.service';
import { DatatableComponent } from '../shared/ui/datatable/datatable.component';

declare let $:any;

@FadeInTop()
@Component({
  selector: 'sa-datatables-case',
  templateUrl: './notifgenmsg.component.html',
  styleUrls: ['../../assets/css/tmd_basic.css']
})
  
@Injectable()  
export class NotifGenMsgComponent implements OnInit, DoCheck {
  @ViewChild(DatatableComponent) ngxDatatableComponent: DatatableComponent;
  
  public genMsgs: any;
    
  constructor(
    private router: Router,  
    private notificationsService: NotificationsService, //TMD version
    private notificationService: NotificationService   //SA version used for dialogs
  ) {}
  
  ngOnInit() {  
    //###console.log("NotifGenMsgComponent ngOnInit - ENTRY");
  
    //this allows the router.navigate to re-call this component, providing fresh data
    this.router.routeReuseStrategy.shouldReuseRoute = function(){
       return false;
    };
    
    //this kicks-off the web service call for data, when data is returned the (val) block is reached then refreshDataTable() is called 
    this.getNotifGenMsgData();
        
    //###console.log("NotifGenMsgComponent ngOnInit - EXIT");
  }
  
  ngDoCheck() {
    //this is called after ngOnInit - see https://codecraft.tv/courses/angular/components/lifecycle-hooks/
  }
   
  handleButtons() {
    //###console.log('handleButtons');
    if($(this.ngxDatatableComponent)) {   
      if($(this.ngxDatatableComponent).DataTable) {
         console.log('processing button clicks');
        
         const ngxDataTable = $($(this.ngxDatatableComponent).DataTable.tables()).DataTable();
        
         const deleteBtnNum = 4;
         const archiveBtnNum = 5;
        
         ngxDataTable.button(deleteBtnNum).action(() => { //lambda expression provides access to outer this, but inner this is not accessible
            //###console.log( this.text() +' button was clicked' ); //will not work when using lambda expression
            console.log('Delete button clicked');
                       
            const selection = ngxDataTable.rows({ selected: true } ).data().toArray();
            this.doSelection(selection, 'Delete');
         });
                  
         ngxDataTable.button(archiveBtnNum).action(() => { //lambda expression provides access to outer this, but inner this is not accessible
            //###console.log( this.text() +' button was clicked' ); //will not work when using lambda expression
            console.log('Archive button clicked');
                       
            const selection = ngxDataTable.rows({ selected: true } ).data().toArray();
            this.doSelection(selection, 'Archive');
         });
        }    
      }
   }
           
   doSelection(selection: any, buttonName: String) {
     
     //remove after testing
     let rowStr = "";     
     for (const row of selection) {
       rowStr += row.rowid +",";
     }
     rowStr = rowStr.substring(0, rowStr.lastIndexOf(","));
     
     const content = "Do you want to " +buttonName +" the " +selection.length +" rows selected?";
     this.notificationService.smartMessageBox({
       title : "<i class='fa fa-question-circle txt-color-yellow'></i> " +buttonName +" <span class='txt-color-white'><strong>" + $('#show-shortcut').text() + "</strong></span>",
       content : content,
       buttons : '[No][Yes]'

     }, (ButtonPressed) => {
       if (ButtonPressed === "Yes") {
         //###console.log(buttonName +' selected rows');
         //call appropriate web service
        
         for(let i=0; i<selection.length; i++) { 
           //###console.log("row having database ID " +selection[i].rowid +" selected for " +buttonName); 
         }
       }
     });
    }
  
   getNotifGenMsgData() {  
      //###console.log("NotifGenMsgComponent getNotifGenMsgData - ENTRY");
          
      const TESTING = false;        
      if(TESTING) {
        //###console.log("NotifGenMsgComponent getNotifGenMsgData - ############# USING TEST DATA");
        const resStr = '['
                     + '{"checked":null,"rowid":"14","personID":"ALL","senderID":"ADMIN","message":"TEST DATA10 - TangleMyData has an outage planned for November 15, 2018 from 1:00 UTC until 2:00 UTC.","messageStatus":"NEW","messagePrimType":"SYSTEM","messageSubType":"GENERAL","dateExpires":"2018-11-15 09:02:37","dateCreated":"2018-11-05 09:02:37","dateLastUpdate":"2018-11-08 01:19:10"},'
                     + '{"checked":null,"rowid":"15","personID":"ALL","senderID":"ADMIN","message":"TEST DATA11 - TangleMyData has an outage planned for November 15, 2018 from 1:00 UTC until 2:00 UTC.","messageStatus":"NEW","messagePrimType":"SYSTEM","messageSubType":"GENERAL","dateExpires":"2018-11-15 09:02:37","dateCreated":"2018-11-05 09:02:37","dateLastUpdate":"2018-11-08 01:19:10"},'
                     + '{"checked":null,"rowid":"16","personID":"ALL","senderID":"ADMIN","message":"TEST DATA12 - TangleMyData has an outage planned for November 15, 2018 from 1:00 UTC until 2:00 UTC.","messageStatus":"NEW","messagePrimType":"SYSTEM","messageSubType":"GENERAL","dateExpires":"2018-11-15 09:02:37","dateCreated":"2018-11-05 09:02:37","dateLastUpdate":"2018-11-08 01:19:10"}'
                     + ']';
        this.genMsgs = JSON.parse(resStr);
      } else {
          this.notificationsService.getNotifGenMsgData().then(
            (val) => {
               this.genMsgs = this.notificationsService.genMsgs;
               if (this.genMsgs) {
                  console.log("NotifGenMsgComponent getNotifGenMsgData - genMsgs is not null");
                  this.refreshDataTable();
               } else {
                  //###console.log("NotifGenMsgComponent getNotifGenMsgData - genMsgs is null");
               }  
               this.router.navigate(['/notifgenmsg']);  
            },
            (err) => {
               //###console.log("NotifGenMsgComponent getNotifGenMsgData - error: " +err);
            }
          );        
      }
          
      //###console.log("NotifGenMsgComponent getNotifGenMsgData - EXIT");
   }
  
   refreshDataTable() {
     if($(this.ngxDatatableComponent)) {   
       if($(this.ngxDatatableComponent).DataTable) {
         console.log('refreshing ngxDataTable #####################');
        
         const ngxDataTable = $($(this.ngxDatatableComponent).DataTable.tables()).DataTable();   
         ngxDataTable.clear().rows.add(this.genMsgs).draw();
       }
     }
   }
        
}

Here's a screen shot of my General Messages table:

Thanks,
Bob

This question has an accepted answers - jump to answer

Answers

  • colincolin Posts: 15,237Questions: 1Answers: 2,598
    Answer ✓

    Hi @bobc02 ,

    I'm not familiar with the framework, and really don't understand how those buttons are being initialised. For me, I've only seen custom buttons defined like this (at initialisation), or added later like this.

    That said, if it's working with local data, it must be something to do with the async nature of the data loading - when the webservice loads it, there's going to be a delay, so it would be worth investigating that.

    Sorry can't be any more use,

    Cheers,

    Colin

  • bobc02bobc02 Posts: 19Questions: 6Answers: 0

    Hi Colin,

    The standard way of defining the buttons, would probably work, but it would likely interfere with the plugin's datatable. The plugin provides me with some features that I'd prefer not to code myself. But, I appreciate the suggestion.

    Yes, it is the async nature of the web service that is causing the problem. But, I was hoping the problem could be solved by some different DT calls, than the table.clear().rows.add(data_from_web_service).draw() operation I was using. But, clearly not.

    I solved it by saving the first instance of the DT being created, as a property of the service.ts. I moved the button action listeners to the service.ts, as well. So, now when the data from the web service is returned, I do the table.clear().rows.add(data_from_web_service).draw() operation in the component.ts, as before, but the action listeners in the service.ts catches the button clicks.

    Here's my modified component.ts:

    import { NgModule, Component, OnInit, Injectable, Inject, ViewChild, DoCheck } from '@angular/core';
    import { FormGroup, FormControl } from '@angular/forms';
     
    import { FadeInTop } from "../shared/animations/fade-in-top.decorator";
    import { NotificationsService } from './notifications.service';
    import { NotificationService } from '../shared/utils/notification.service';
    import { DatatableComponent } from '../shared/ui/datatable/datatable.component';
     
    declare let $:any;
     
    @FadeInTop()
    @Component({
      selector: 'sa-datatables-case',
      templateUrl: './notifgenmsg.component.html',
      styleUrls: ['../../assets/css/tmd_basic.css']
    })
       
    @Injectable() 
    export class NotifGenMsgComponent implements OnInit, DoCheck {
      @ViewChild(DatatableComponent) ngxDatatableComponent: DatatableComponent;
       
      public genMsgs: any;
         
      constructor(
        private notificationsService: NotificationsService, //TMD version
        private notificationService: NotificationService   //SA version used for dialogs
      ) {}
       
       ngOnInit() { 
         //###console.log("NotifGenMsgComponent ngOnInit - ENTRY");
         
         //get either test data, or kick-off the web service call for data, when data is returned the (val) block is reached, then refreshDataTable() is called
         this.getNotifGenMsgData();
             
         //###console.log("NotifGenMsgComponent ngOnInit - EXIT");
       }
       
       ngDoCheck() {
         //###console.log("NotifGenMsgComponent ngDoCheck - ENTRY");
         
         //this is called after ngOnInit - see https://codecraft.tv/courses/angular/components/lifecycle-hooks/
         if(!this.notificationsService.notifGenMsgDataTable) {
           if($(this.ngxDatatableComponent)) {   
             if($(this.ngxDatatableComponent).DataTable) {
               console.log('setting notifGenMsgDataTable *******');
               this.notificationsService.setNotifGenMsgDataTable($($(this.ngxDatatableComponent).DataTable.tables()).DataTable(), this);
             }
           }
         }
         //###console.log("NotifGenMsgComponent ngDoCheck - EXIT");
       }
        
       handleButtons() {
        //###console.log('handleButtons');
        //nothing to do - all done in notificationsService
       }
                
       doSelection(selection: any, buttonName: String) {
          
         //remove after testing
         let rowStr = "";    
         for (const row of selection) {
           rowStr += row.rowid +",";
         }
         rowStr = rowStr.substring(0, rowStr.lastIndexOf(","));
          
         const content = "Do you want to " +buttonName +" the " +selection.length +" rows selected?";
         this.notificationService.smartMessageBox({
           title : "<i class='fa fa-question-circle txt-color-yellow'></i> " +buttonName +" <span class='txt-color-white'><strong>" + $('#show-shortcut').text() + "</strong></span>",
           content : content,
           buttons : '[No][Yes]'
     
         }, (ButtonPressed) => {
           if (ButtonPressed === "Yes") {
             //###console.log(buttonName +' selected rows');
             //call appropriate web service
             
             for(let i=0; i<selection.length; i++) {
               //###console.log("row having database ID " +selection[i].rowid +" selected for " +buttonName);
             }
           }
         });
       }
       
       getNotifGenMsgData() { 
          //###console.log("NotifGenMsgComponent getNotifGenMsgData - ENTRY");
               
          const TESTING = false;       
          if(TESTING) {
            //###console.log("NotifGenMsgComponent getNotifGenMsgData - ############# USING TEST DATA");
            const resStr = '['
                         + '{"checked":null,"rowid":"14","personID":"ALL","senderID":"ADMIN","message":"TEST DATA10 - TangleMyData has an outage planned for November 15, 2018 from 1:00 UTC until 2:00 UTC.","messageStatus":"NEW","messagePrimType":"SYSTEM","messageSubType":"GENERAL","dateExpires":"2018-11-15 09:02:37","dateCreated":"2018-11-05 09:02:37","dateLastUpdate":"2018-11-08 01:19:10"},'
                         + '{"checked":null,"rowid":"15","personID":"ALL","senderID":"ADMIN","message":"TEST DATA11 - TangleMyData has an outage planned for November 15, 2018 from 1:00 UTC until 2:00 UTC.","messageStatus":"NEW","messagePrimType":"SYSTEM","messageSubType":"GENERAL","dateExpires":"2018-11-15 09:02:37","dateCreated":"2018-11-05 09:02:37","dateLastUpdate":"2018-11-08 01:19:10"},'
                         + '{"checked":null,"rowid":"16","personID":"ALL","senderID":"ADMIN","message":"TEST DATA12 - TangleMyData has an outage planned for November 15, 2018 from 1:00 UTC until 2:00 UTC.","messageStatus":"NEW","messagePrimType":"SYSTEM","messageSubType":"GENERAL","dateExpires":"2018-11-15 09:02:37","dateCreated":"2018-11-05 09:02:37","dateLastUpdate":"2018-11-08 01:19:10"}'
                         + ']';
            this.genMsgs = JSON.parse(resStr);
          } else {
              this.notificationsService.getNotifGenMsgData().then(
                (val) => {
                   this.genMsgs = this.notificationsService.genMsgs;
                   if (this.genMsgs) {
                      console.log("NotifGenMsgComponent getNotifGenMsgData - genMsgs is not null");
                      this.refreshDataTable();
                   } else {
                      //###console.log("NotifGenMsgComponent getNotifGenMsgData - genMsgs is null");
                   } 
                },
                (err) => {
                   //###console.log("NotifGenMsgComponent getNotifGenMsgData - error: " +err);
                }
              );       
          }
               
          //###console.log("NotifGenMsgComponent getNotifGenMsgData - EXIT");
       }
       
       refreshDataTable() {
         if($(this.ngxDatatableComponent)) {  
           if($(this.ngxDatatableComponent).DataTable) {
             console.log('refreshing ngxDataTable #####################');
             
             const ngxDataTable = $($(this.ngxDatatableComponent).DataTable.tables()).DataTable();  
             ngxDataTable.clear().rows.add(this.genMsgs).draw();
           }
         }
       }
             
    }
    

    Here's the important stuff in my service.ts:

      public genMsgs: any = null;
      public notifGenMsgDataTable: any;
     ...
      setNotifGenMsgDataTable(dataTable: any, notifGenMsgComponent: any) {   
        //###console.log('setNotifGenMsgDataTable ENTRY');
        
        this.notifGenMsgDataTable = dataTable;  
          
        this.getBtnNums(dataTable);
        
        this.notifGenMsgDataTable.button(this.deleteBtnNum).action(() => { //lambda expression provides access to outer this, but inner this is not accessible
          //###console.log( this.text() +' button was clicked' ); //will not work when using lambda expression
          console.log('Delete button clicked');
                     
          const selection = dataTable.rows({ selected: true } ).data().toArray();
          notifGenMsgComponent.doSelection(selection, 'Delete');
        });
        
        this.notifGenMsgDataTable.button(this.archiveBtnNum).action(() => { //lambda expression provides access to outer this, but inner this is not accessible
          //###console.log( this.text() +' button was clicked' ); //will not work when using lambda expression
          console.log('Archive button clicked');
                     
          const selection = dataTable.rows({ selected: true } ).data().toArray();
          notifGenMsgComponent.doSelection(selection, 'Archive');
        });
        
      }
    

    Thanks,
    Bob

  • allanallan Posts: 63,175Questions: 1Answers: 10,409 Site admin

    Hi,

    I was just going to have a look, but I'm getting:

    Cannot login because your password reset is not yet confirmed. Check your email.

    That said, I see you say you've worked around it for now, so I guess the page might not show the error any more.

    There are two things that spring to mind:

    1. Make sure you are using the latest versions of DataTables and its extensions (the debugger can help with that - https://debug.datatables.net ).
    2. It might be that the buttons are being removed from the DOM and then reinserted - if you use jQuery().remove() that will remove the event listeners, meaning they will no longer work. But if you are using Angular you are unlikely to be doing that and I don't see it above. If the DataTable works (e.g. sorting and filtering) then the buttons should really continue to work as well!

    Allan

  • bobc02bobc02 Posts: 19Questions: 6Answers: 0
    edited December 2018

    Hi Allan,

    Ooops - I accidentally turned WAN IP verification on during testing this morning. Please try again.

    I have actually solved the problem. I indicated that in my last comment. But, I do appreciate you having a look.

    Thanks,
    Bob

  • allanallan Posts: 63,175Questions: 1Answers: 10,409 Site admin

    That's a good workaround. I'm not seeing anything obvious for why it wouldn't be working without the workaround I'm afraid. As you say its due to the async nature of it, but other than your workaround, I'm not certain what the best fix would be.

    Allan

This discussion has been closed.