Configure Edit, New, Delete buttons on Editor datatable with API calls in angular 6 project.

Configure Edit, New, Delete buttons on Editor datatable with API calls in angular 6 project.

Ewoud SmetsEwoud Smets Posts: 5Questions: 1Answers: 1

I'm trying to implement the Edit, New and Delete buttons to an Editor datatable i've created in an angular. Basicly i populate the editor through an API call on my service and now i want to manipulate this data and pass it back. But i lack experience with angular to really find the solution to my problem. I am at this state:
Component.ts file:

 ngOnInit() {    
    //this.dataTable = $(this.table.nativeElement);
    // this.dataTable.dataTable();

    this.survService.getClientData(this.jwt).subscribe(
    data => {    
    this.items = data;
    this.loadOrganisations(),
    console.error();
    console.log(this.items);  
  });
  
    
  }

  public loadOrganisations(): void{
    if (this.tableWidget) {
      this.tableWidget.destroy(); // essentially refreshes the table
      // you can also remove all rows and add new
       this.tableWidget.clear().rows.add(this.items).draw();
  }

  console.log(this.items);

  let editor = new ($.fn.DataTable as any).Editor({
     ajax:{
       edit:{
         contentType:'application/json',         
         data:function (d) {
           var newdata;
           $.each(d.data, function (key, value) {
               newdata = this.survService.client
               console.log("ajax data new data...:" + newdata);
           });
           console.log("ajax data new data:" + newdata);
  //         // newdata = newdata.replace(/\\n/g, "\\n")
  //         //                         .replace(/\\'/g, "\\'")
  //         //                         .replace(/\\"/g, '\\"')
  //         //                         .replace(/\\&/g, "\\&")
  //         //                         .replace(/\\r/g, "\\r")
  //         //                         .replace(/\\t/g, "\\t")
  //         //                         .replace(/\\b/g, "\\b")
  //         //                         .replace(/\\f/g, "\\f");
  //         // newdata = newdata.replace(/[\u0000-\u0019]+/g,"");

           return newdata;
       },
       success: function (data) { $('#dataTables').dataTable().api().ajax.reload(); },
     },
   },
    table: '#dataTables',
    fields:[{
      label:"GUID",
      name:"guid",
    },{
      label:"Client Id",
      name:"clientId",
    },{
      label:"Organization Name",
      name:"clientName",
    },{
      label:"Address",
      name:"address",
    },{
      label:"Email",
      name:"email",
    },{
      label:"Responsible Person",
      name:"responsiblePersonName",
    },{
      label:"Work Flow Code",
      name:"workFlowCode",
    }]

  });




  let tableOptions: any = {
      table: '#dataTables',
      data: this.items,
      dom: 'Bfrtip',
      select: true,
      columns: [
          //{ title: 'GUID', data: 'guid' },
          { title: 'Client Id', data: 'clientId' },
          { title: 'Organization Name', data: 'clientName' },
          { title: 'Address', data: 'address' },
          { title: 'Email', data: 'email' },         
          { title: 'Responsible Person', data:'responsiblePersonName' },
          { title: 'Work Flow Code', data: 'workFlowCode' }
      ],
      buttons: [
                { extend: "create", editor: editor },

                { extend: "edit", editor: editor },

                { extend: "remove", editor: editor }
            ],
  }

  this.backofficeTable = $(this.el.nativeElement.querySelector('table'));
  this.tableWidget = this.backofficeTable.DataTable(tableOptions);
   this.tableWidget.on('select', (e, dt, type, indexes) => {
  //      // I DIDN'T TRY THIS IN HERE...Just debug it and check the best way to emit an actual object
        this.organisationSelected.emit(this.items[indexes[0]]);
    });
}

Service.ts file:

getClientData(jwt): Observable<any>{
    this.headers = this.headers.set('Authorization', jwt);
    return this.httpClient.get<Orgnanisations[]>(
      environment.surveyApiUrl + 'api/client/',
      { headers:this.headers,
        responseType: 'json' }
    );
  }

i get this error when i press one of the buttons:
'core.js:1673 ERROR TypeError: Cannot read property 'oFeatures' of undefined'
Any help is more than welcomed.

This question has an accepted answers - jump to answer

Answers

  • allanallan Posts: 63,695Questions: 1Answers: 10,500 Site admin

    Could you show me the full back trace of the error please? I don't immediately see anything wrong with the above code, but I'm not at all well versed in Angular myself I'm afraid.

    Allan

  • Ewoud SmetsEwoud Smets Posts: 5Questions: 1Answers: 1

    Hello, sorry for the late response, this is my error appearing on the console:
    core.js:1673 ERROR TypeError: Cannot read property 'oFeatures' of undefined
    at f._tidy (dataTables.editor.min.js:102)
    at f.edit (dataTables.editor.min.js:53)
    at r.action (dataTables.editor.min.js:122)
    at l (dataTables.buttons.min.js:13)
    at HTMLButtonElement.<anonymous> (dataTables.buttons.min.js:14)
    at HTMLButtonElement.dispatch (jquery.js:5237)
    at HTMLButtonElement.elemData.handle (jquery.js:5044)
    at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:423)
    at Object.onInvokeTask (core.js:3811)
    at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:422)
    defaultErrorLogger @ core.js:1673
    push../node_modules/@angular/core/fesm5/core.js.ErrorHandler.handleError @ core.js:1719
    push../src/app/monitoring/error.handler.ts.MonitoringErrorHandler.handleError @ error.handler.ts:15
    next @ core.js:4311
    schedulerFn @ core.js:3551
    push../node_modules/rxjs/_esm5/internal/Subscriber.js.SafeSubscriber.__tryOrUnsub @ Subscriber.js:192
    push../node_modules/rxjs/_esm5/internal/Subscriber.js.SafeSubscriber.next @ Subscriber.js:130
    push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber._next @ Subscriber.js:76
    push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.next @ Subscriber.js:53
    push../node_modules/rxjs/_esm5/internal/Subject.js.Subject.next @ Subject.js:47
    push../node_modules/@angular/core/fesm5/core.js.EventEmitter.emit @ core.js:3535
    (anonymous) @ core.js:3842
    push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke @ zone.js:391
    push../node_modules/zone.js/dist/zone.js.Zone.run @ zone.js:150
    push../node_modules/@angular/core/fesm5/core.js.NgZone.runOutsideAngular @ core.js:3779
    onHandleError @ core.js:3842
    push../node_modules/zone.js/dist/zone.js.ZoneDelegate.handleError @ zone.js:395
    push../node_modules/zone.js/dist/zone.js.Zone.runTask @ zone.js:198
    push../node_modules/zone.js/dist/zone.js.ZoneTask.invokeTask @ zone.js:498
    invokeTask @ zone.js:1744
    globalZoneAwareCallback

  • allanallan Posts: 63,695Questions: 1Answers: 10,500 Site admin

    That error suggests that the DataTable as been replaced. Is it possibly you have another element of the same id on the page, or possibly Angular has replaced the element (I don't know enough about Angular to say if that might be the case I'm afraid).

    If you can give me a link to the page I can take a look and see if that might be what is happening.

    Thanks,
    Allan

  • Ewoud SmetsEwoud Smets Posts: 5Questions: 1Answers: 1

    Unfortunatly i can't share it, since its not a personal project. I believe this error has to do with the fact that after i populate the dataTable on ngOnInit() method with the subscribe data, that data is no longer accessible by the datatable to pass that row data in the editor modal. Do you have any way to store the data and keep it accessible for the Editor?

  • newcliquenewclique Posts: 8Questions: 1Answers: 0
    edited April 2020

    Are you using the Angular-Datatables NPM package? Because Datatables.net does not really support any sort of direct manipulation via Angular 2+ without it. Therefore, you have use the Angular package to get the tables to render and then find ways to hook in for the rest of the functionality. It can be a challenge.

    First, how are you binding your data to the html element for the datatable? I can tell you that I struggled with the async nature of Angular and Ajax until I bound the dtOptions property of the <table> element to a property of my controller (i.e. code-behind .ts file).

    So, my html looks like:

    <table *ngIf="isDataReady" datatable [dtOptions]="myDatatableSettings" class="table hover...blah blah></table>
    

    The variable myDatatableSettings:Datatables.Settings contains the .columns array with the data bindings as well as the .data array of all the data. Additionally, you should be able to attach Angular events to myDatatableSettings.editor.events or some such.

    If you make it that far, you should be able to set some breakpoints and catch the api variable that's typically passed into the event handlers and the use that to reference the data. For example, if the index of the edited row is passed into the event, api.rows(index).data() should reveal the data.

    This is just the general theory of how the pieces should fit together. Personally, I took a look at the Editor extension several days ago and thought it was a bit of a mess for doing what should be pretty simple stuff, esp in Angular. So, I just made some nice modal forms and manually wired up the CRUD methods to do it all myself. Was much simpler. Good luck. Let me know if this was helpful or totally messed you up :smile:

  • newcliquenewclique Posts: 8Questions: 1Answers: 0

    Oh good grief the comment stripped out my html. I'll use braces instead of angles. Here's my table element:
    {table *ngIf="isDataReady" datatable [dtOptions]="myDatatableSettings" class="table hover blah blah"}{/table}

  • allanallan Posts: 63,695Questions: 1Answers: 10,500 Site admin

    You need to use backticks to show code - otherwise it is stripped on display (not on store - I've added the Markdown formatting needed to your post).

    Thanks for your thoughts - we haven't integrated DataTables / Editor with Angular at all (yet? Not yet sure if we will!) so I'm not surprised that it is proving to be a bit difficult. You've got two different libraries trying to control the DOM and there will need to be a bridge between them to make sure the expected interactions apply.

    Hopefully without Angular it isn't a "mess" though :).

    Allan

  • GirlsAreSoftGirlsAreSoft Posts: 10Questions: 2Answers: 0

    Sorry, Allan. I wasn't trying to be "that guy" on the Internet. The extension looks very feature rich. I was just a bit overwhelmed by seeing how much re-definition of the fields had to take place apart from the dt columns already defined, I know it makes sense for customizing the modal but was hoping that there was a default mode (did I miss it?) where the existing columns could be flagged with 'editor:true' or something and the plugin would interpolate the data type of the data and present accordingly.

    At any rate, I will try it again when it's available for Angular. I would have to do too much plumbing I'm afraid to tie the Ajax calls to my service layer. Thanks again. For a free library it is very, very impressive.

  • allanallan Posts: 63,695Questions: 1Answers: 10,500 Site admin

    No worries - there is a reason why WTFs/minute is funny :).

    much re-definition of the fields had to take place apart from the dt columns already defined

    You didn't miss it I'm afraid. Back when I first started writing Editor I did consider that, but I quickly hit the fact that columns and fields don't always map 1:1. Even something as simple as using a renderer to combine two fields into a single one column caused issues (since you need a way to then define both fields).

    For that reason the fields and columns need to be configured in their own objects.

    Allan

  • Ewoud SmetsEwoud Smets Posts: 5Questions: 1Answers: 1

    I finaly found a solution to this. Basicly with custom modals for the button functionality, but at the very least they work.
    Now i have a problem in production. We use Azure tech to deploy our projects in production. Localy everything works fine. But on live server i get this error:
    ERROR TypeError: $.fn.DataTable.Editor is not a constructor

    asociated with this warning:
    If you are seeing this message, it is because Editor has been
    scripts.f10d58363cdee5a6e2ff.js:1 installed using npm install datatables.net-editor, but the
    scripts.f10d58363cdee5a6e2ff.js:1 licensed or trial files have not been installed in place of
    scripts.f10d58363cdee5a6e2ff.js:1 the holding files.
    scripts.f10d58363cdee5a6e2ff.js:1
    scripts.f10d58363cdee5a6e2ff.js:1 To install the files, please download Editor from
    scripts.f10d58363cdee5a6e2ff.js:1 https://editor.datatables.net and replace the Javascript and
    scripts.f10d58363cdee5a6e2ff.js:1 CSS files in node_modules/datatables.net-editor with those
    scripts.f10d58363cdee5a6e2ff.js:1 from the downloaded package. install.sh can be used to
    scripts.f10d58363cdee5a6e2ff.js:1 install the files from the downloaded zip.
    main.db272d5f762fbcc2d56a.js:1 Angular is running in the development mode. Call enableProdMode() to enable the production mode.

    I have a developer license for editor datatables.
    Any idea how to solve this issue?

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

    Have you followed the steps explained in the error message?

  • Ewoud SmetsEwoud Smets Posts: 5Questions: 1Answers: 1
    Answer ✓

    I managed to fix it with a little tweek on server side.
    Now for anyone who is struggling with the same original error i had, this is the solution i followed:

    this.tableWidget.on('select', (e, dt, type, indexes) => {
            //this.selected will contain the data of the current selected value from the 
            //datatable
            this.selected = dt.row( {selected: true}).data();
           
        });
    }
    
    

    then declared the buttons:

     buttons: [
               { text: 'New',
                 action: () => {this.openDialog()},
                },{
                text: 'Edit',
                   //this method opens the dialog and pass this.selected data to its fields
                  action: () => {this.openEditDialog(this.selected)}
              },{
                  text: 'Delete',
                  action: () => {this.openDeleteDialog(this.selected)},
                }           
        ]
    
    

    Hope this can help anyone with the same problem. :smile:

This discussion has been closed.