Ajax error using an external data source

Ajax error using an external data source

bigdogdmanbigdogdman Posts: 14Questions: 2Answers: 0

Link to test case: Sorry! I don't have one :,(
Debugger code (debug.datatables.net): ovimob
Error messages shown: DataTables warning: table id=users-table - Ajax error
Description of problem:
Okay. First off, I'd like to apologize for not having a test case link & explain why I don't have one. I am integrating DataTables into my Laravel project on my site, and...well, let me just dive right in.

So I've got a Laravel 11.5 project running on the latest version of Ubuntu, and my data "source" is an API endpoint(s). I am running this site on a closed WLAN that has multiple endpoints that my work is trying to coalesce together under the same "Laravel hood". Sounds simple enough. So I get the endpoints working, and I even get the AuthServiceProvider override working. I now have (practically) custom auth and a custom "API database". This is being coded from scratch, but is part of a larger project to refactor a CI 3 site into Laravel. In this CI 3 app, they're using DataTables, so I thought I'd do the same, for simplicity's sake. However, upon trying to integrate DataTables into this project, I actually got it working. Here's the catch: I have no idea how I got it working. I started off following the default tutorial steps until I got to the part about hooking up the data source. Obviously it's not going to work the same, but I followed as many steps as I could anyway and lo and behold, it actually worked!! Here's a screenshot of it "working" (sorry I had to censor it, but it was live user data, and I haven't been able to get anything else working to show you):

Here's my problem: I need different data. I don't want to see the users' data. Now, seeing as how I'm brand-new to DT's, I'm just trying to get anything working, just so I can understand how everything works. Currently I do not. You see, if I use my database, as opposed to the one that is built into Laravel (MyCustomModel $model instead of Users $model in the query method of UsersDataTable), I get the Ajax error warning message. I have debugged the data coming to the DT, and the content looks like this:

{"draw":0,"recordsTotal":1,"recordsFiltered":1,"data":[{"id":95,"display":"Idaho Falls","name":"Idaho Falls","slug":"idaho-falls","parent":null,"description":"","tags":[],"custom_fields":[],"created":"2023-12-08T04:49:35.371160Z","last_updated":"2023-12-08T04:49:35.371181Z","site_count":0,"_depth":0}],"input":[]}

I've run this through jsonlint.com, so I know it's valid. However, here's my [first] problem: This data is part of a larger JsonResponse object and, while I've tried passing just the data portion of the object to the DT directly, no matter how I try to pass it, I get the same error. I've checked the error logs on my server (and in Laravel), and there's no mention of my DT at all. So, here's my confusion. How do I debug this further? I'm hoping the attached debugger code points to a glaring problem with my config, and I can simply fix that. So far, I haven't been able to find anything useful on the forums (for this error).
I did use the
return (new CollectionDataTable($collection))->toJson();
line in my UsersController->index(), and that did seem to solve a number of other errors I was getting, but that's when I started receiving the Ajax error warning instead.

I'm trying to be as helpful as I can, but I honestly don't know what you need to see without having access to a working test case link and, honestly, I've made so many changes to my codebase trying to fix this, it's highly doubtful I'll ever get it working again, at least to where it was pulling the users data (from a nonexistent Users model and an API endpoint that doesn't allow me to see the users' data... :s :s :s ), but here is my UsersDataTable class: (the commented-out columns are from the data object I'm trying to get it to show)

<?php

namespace App\DataTables;

use App\Models\User;
use Illuminate\Database\Eloquent\Builder as QueryBuilder;
use Yajra\DataTables\EloquentDataTable;
use Yajra\DataTables\Html\Builder as HtmlBuilder;
use Yajra\DataTables\Html\Button;
use Yajra\DataTables\Html\Column;
use Yajra\DataTables\Html\Editor\Editor;
use Yajra\DataTables\Html\Editor\Fields;
use Yajra\DataTables\Services\DataTable;
use App\Models\MyCustomModel;

class UsersDataTable extends DataTable
{
    public function dataTable(QueryBuilder $query): EloquentDataTable
    {
        return (new EloquentDataTable($query))->setRowId('id');
    }

    public function query(MyCustomModel $model): QueryBuilder
    {
        return collect($model->getData())->keyBy('id');   // I don't think I'm using this anymore...
    }

    /**
     * Optional method if you want to use the html builder.
     */
    public function html(): HtmlBuilder
    {
        return $this->builder()
                    ->setTableId('users-table')
                    ->columns($this->getColumns())
                    ->minifiedAjax()
                    //->dom('Bfrtip')
                    ->orderBy(1)
                    ->selectStyleSingle()
                    ->buttons([
                        Button::make('excel'),
                        Button::make('csv'),
                        Button::make('pdf'),
                        Button::make('print'),
                        Button::make('reset'),
                        Button::make('reload')
                    ]);
    }

    public function getColumns(): array
    {
        $cssClasses = ['class' => 'text-center text-white'];
        return [
            Column::make('network')->data('id')->width(230)->attributes($cssClasses),
            Column::make('state')->data('name')->width(138)->attributes($cssClasses),
            Column::make('county')->data('email')->width(320)->attributes($cssClasses),
            Column::make('city')->data('name')->width(276)->attributes($cssClasses),
            Column::make('footprint')->data('email')->width(177)->attributes($cssClasses),
            Column::make('site name')->data('id')->width(187)->attributes($cssClasses),
            Column::make('address')->data('email')->width(819)->attributes($cssClasses),
            Column::make('latitude')->data('name')->width(163)->attributes($cssClasses),
            Column::make('longitude')->data('email')->width(187)->attributes($cssClasses),
            Column::computed('action')
                  ->exportable(false)
                  ->printable(false)
                  ->width(60)
                  ->attributes($cssClasses),
            
            //Column::make('id'),
            //Column::make('name'),
            //Column::make('display'),
            //Column::make('url'), 
            //Column::make('description'),
            //Column::make('created'),
            //Column::make('last_updated'),
            //Column::make('site_count'),
            //Column::make('slug'),
            //Column::make('parent'),
            //Column::make('tags'),
            //Column::make('custom_fields'),
        ];
    }
}

Here is my route:

Route::controller(MyCustomController::class)->group(function () {
        Route::get('/users', [UsersController::class, 'index'])->name('users.index');
    });
});

And here is my UsersController:

<?php
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\DataTables\UsersDataTable;
use Yajra\DataTables\CollectionDataTable;
use App\Http\Controllers\MyCustomController;
 
class UsersController extends Controller
{
        public function index(UsersDataTable $dataTable)
    {
        $data = self::all();
        return $dataTable->render('users.index', compact('data'));
    }
    
    public static function all() {
        $data = MyCustomController::getRegions();    // equivalent to MyCustomModel->getData()
        $collection = collect($data->keyBy('id'));
        return (new CollectionDataTable($collection))->toJson();
    }
}

I'm trying not to inundate you with code that you don't need to see, but...I don't know what you need to see. Please, pardon my ignorance. :/

This question has an accepted answers - jump to answer

Answers

  • kthorngrenkthorngren Posts: 21,343Questions: 26Answers: 4,954

    The Ajax error has a link for troubleshooting steps:
    https://datatables.net/manual/tech-notes/7

    When you get the error what error response code do you see using the steps in the link?

    The JSON you posted is a valid JSON string. Is this in the response when you get the Ajax error?

    There is one issue with the response. It has draw":0. Datatables will never expect the draw parameter to be 0. I don't believe this will cause the Ajax error but Datatables won't use the data. The draw parameter is a sequence number. When Datatables initializes the the draw value will start at 1 and increment from there. See the Server side processing protocol docs for details. However I don't see that you have serverSide enabled Datatables won't use this parameter and will only refer to the data property for the table data. Possibly Yarja Datatables forces Server Side processing to be enabled?

    I'm not sure what minifiedAjax() in line 36 is. It looks to be a Yarja Datatables option. For Yarja Datatalbes questions you will need to contact the developers of the package. Yarja Datatables leverages the Datatables found at this site.

    We can help with the jQuery Datatables questions but this forum doesn't have anyone with Laravel or Yarja Datatables experience.

    This data is part of a larger JsonResponse object and, while I've tried passing just the data portion of the object to the DT directly, no matter how I try to pass it,

    You can use the ajax.dataSrc option to point to the data object if its not in the default location where Datatables looks, See the Ajax docs for details. However if you are using serverSide then Datatables expects the other parameters to be in the root of the JSON string.

    I've checked the error logs on my server (and in Laravel), and there's no mention of my DT at all. So, here's my confusion. How do I debug this further?

    You will need to contact the Yarja Datatables developers to find out how to debug this environment.

    Kevin

  • bigdogdmanbigdogdman Posts: 14Questions: 2Answers: 0

    Oh, my apologies, I meant to include the response code I'm getting from the error was 500.
    And thank you for the quick response. I did look at that link the error provides, which is why I looked at the server logs; there was practically nothing else it suggested doing, other than, well, this. But I will look into the serverSide setting; might you happen to know if that's something I can turn on from the server side? Here is my datatable draw(and yes, I suppose I should've said I was following Yajra's Getting Started tut when implementing DT's for the first time):

    <x-app-layout>
        <x-slot name="slot">
            <div class="container text-white">
                <div class="card">
                    <div class="card-header">REGIONS (***TESTING***)</div>
                    <div class="card-body">
                        {{ $dataTable->table(['data' => $data]) }}
                    </div>
                </div>
            </div>
            @push('scripts')
                {{ $dataTable->scripts(attributes: ['type' => 'module']) }}
            @endpush
        </x-slot>
    </x-app-layout>
    

    So I have absolutely no direct control over the jQuery code that the $dataTable->scripts() generates, beyond passing it (I'm assuming) config settings. Here is where I'd wager I'd have to pass it serverSide?

    Regardless, I understand you may not have any Laravel experts, and I'm not really asking Laravel-related questions, only relaying that info to you so that you understand that I do not have direct manipulation capabilities of the raw HTML/JS that's creating the DT. This is the other reason I don't have a test case link.

    As far as the draw:0 is concerned, that's being added on by the new CollectionDataTable($collection) line, which is coming straight out of the Yajra documentation, so I just assumed it was correct. And, at one point I do recall seeing the draw count increment to 1, but I don't recall exactly where; perhaps the network response details in the devtools window, back when it was working?

    ajax.dataSrc I don't think I need, because looking at the Server-side processing page, that looks exactly like what I'm passing to it (barring the draw:0). However I still may need to turn on serverSide. I'm pretty sure I already have an endpoint set up for the DT to hit, if I can just pass it to it somehow....

  • bigdogdmanbigdogdman Posts: 14Questions: 2Answers: 0

    Oh! I found it! draw:1 was on the 500 response! ...Is that normal?

  • kthorngrenkthorngren Posts: 21,343Questions: 26Answers: 4,954

    I found it! draw:1 was on the 500 response! ...Is that normal?

    That is the sent parameters from the client. Datatables looks for the corresponding draw value of 1 in the response. If its different it won't use the response. But being different doesn't cause the Ajax error. The next request, like from a search or page, will increment the draw value. Since you see the server side processing parameters being sent then that suggests Yarja forces server side processing to be enabled. You will need to verify with their docs or ask them.

    The 500 Internal Server Error is an error coming from the server. The client side Datatable is not generating the error. Possibly the response tab might have some indication. You will need to troubleshoot this from the server by looking at the logs, etc. See this technote for general troubleshooting tips. You might need to turn up the logging level of your server to track down the error. Refer to the docs for your environment to learn how.

    As far as the draw:0 is concerned, that's being added on by the new CollectionDataTable($collection) line,

    Something is not correct in the server side processing code or configuration. The Yarja Datatable developers will be the place to ask. Possibly you can find some help on Stack Overflow.

    Kevin

  • bigdogdmanbigdogdman Posts: 14Questions: 2Answers: 0

    Possibly the response tab might have some indication.

    Thank you for the follow-up on this! :) You pointed me in the right direction, and I found the problem. Unfortunately, now I'm back to dealing with a Laravel problem: the CollectionDataTable($collection) line is returning the wrong datatype. Weirdly, it's not giving the error until after it's hit the server (after having drawn the DT--minus the data), and it's hitting the line that I said I didn't think I was using anymore in my original question. So why would it give that error the second time it's given the data, and not the first, since they're the same datatype? Perhaps that's a Yajra question...

    In any event, I'm not expecting you to know the answer to that, but I thought it wouldn't hurt to ask. ;)

    Thanks again for your time! :)

  • bigdogdmanbigdogdman Posts: 14Questions: 2Answers: 0
    edited May 23

    Okay! Finally getting a different error:

    DataTables warning: table id=users-table - Exception Message:
    
    SQLSTATE[HY000]: General error: 1 no such table: netboxes
    (Connection: sqlite, SQL: select count(*) as aggregate from "netboxes")
    

    I'm reaching out one last time to see if anyone knows anything about the ajax config for a Yajra table (I have already reached out to that community as well), but seeing as how I got such fast reply here, I thought I might as well try. Thank you for all the help you've given me thus far @kthorngren! I think you got me past my roadblock at least, and so now all I think I need to do is config this DT correctly to use my endpoint instead of the default DB conn. defined in the .env. Here is my updated index.blade.php file:

    <x-app-layout>
        <x-slot name="slot">
            <div class="container text-white">
                <div class="card">
                    <div class="card-header">REGIONS (***TESTING***)</div>
                    <div class="card-body">
                        {{ $dataTable->table(['data' => $data]) }}
                    </div>
                </div>
            </div>
            @push('scripts')
                {{ $dataTable->scripts(attributes: ['type' => 'module', 'serverSide' => true, 'ajax' => route('user-data')]) }}
            @endpush
        </x-slot>
    </x-app-layout>
    
    

    I'm clearly doing something wrong, as it's not hitting my user-data endpoint. I've also tried setting 'ajax' directly to 'user-data' and setting it to ['url' => route('user-data')], and several other things as well, but still got the same error. Thanks in advance! :)

  • allanallan Posts: 63,530Questions: 1Answers: 10,473 Site admin

    If you "View source", what does the created Javascript look like?

    That line of code is specific to Yajra, but seeing the resulting code might give a close at least as to why it isn't working.

    Allan

  • bigdogdmanbigdogdman Posts: 14Questions: 2Answers: 0

    Is this what you're looking for @allan?

    $(function(){
        window.LaravelDataTables=window.LaravelDataTables||{};
        window.LaravelDataTables["users-table"]=$("#users-table").DataTable({
            "serverSide":true,
            "processing":true,
            "ajax":{
                "url":"https:\/\/myapiendpoint.test\/users",  // <--- /users is Not the endpoint I want it to use
                "type":"GET",
                "data":function(data) {
                    for (var i = 0, len = data.columns.length; i < len; i++) {
                        if (!data.columns[i].search.value) delete data.columns[i].search;
                        if (data.columns[i].searchable === true) delete data.columns[i].searchable;
                        if (data.columns[i].orderable === true) delete data.columns[i].orderable;
                        if (data.columns[i].data === data.columns[i].name) delete data.columns[i].name;
                    }
                    delete data.search.regex;
                }
            },
            "columns":[
                {"data":"id","name":"network","title":"Network","orderable":true,"searchable":true,"width":230},
                {"data":"name","name":"state","title":"State","orderable":true,"searchable":true,"width":138},
                {"data":"email","name":"county","title":"County","orderable":true,"searchable":true,"width":320},
                {"data":"name","name":"city","title":"City","orderable":true,"searchable":true,"width":276},
                {"data":"email","name":"footprint","title":"Footprint","orderable":true,"searchable":true,"width":177},
                {"data":"id","name":"site name","title":"Site Name","orderable":true,"searchable":true,"width":187},
                {"data":"email","name":"address","title":"Address","orderable":true,"searchable":true,"width":819},
                {"data":"name","name":"latitude","title":"Latitude","orderable":true,"searchable":true,"width":163},
                {"data":"email","name":"longitude","title":"Longitude","orderable":true,"searchable":true,"width":187},
                {"data":"action","name":"action","title":"Action","orderable":false,"searchable":false,"width":60}
            ],
            "order":[[1,"desc"]],
            "select":{"style":"single"},
            "buttons":[
                {"extend":"excel"},
                {"extend":"csv"},
                {"extend":"pdf"},
                {"extend":"print"},
                {"extend":"reset"},
                {"extend":"reload"}
            ]
        });
    });
    

    Whatever clue you're looking for, I'm not smart enough to see it. All I see is it's not using the uri I'm trying to pass to it. The error doesn't appear to be coming from Yajra, however. It seems to be coming from the response from the server, which is confusing me even more. I mean that error makes sense that it would come from the server, but not from that endpoint:

    I haven't found anything yet to point me in the right direction, but the search continues...

  • bigdogdmanbigdogdman Posts: 14Questions: 2Answers: 0

    Am I supposed to configure the table or the scripts with the ajax url? When I attached it to the datatable config {{ $dataTable->table(['data' => $data, 'ajax' => ['url' => route('user-data')]]) }}, I saw the request query data come through (which I wasn't seeing before) but it was still hitting the wrong endpoint.

  • bigdogdmanbigdogdman Posts: 14Questions: 2Answers: 0

    The error's coming from the pagination. When I comment out the DB_CONNECTION line in my .env file, I don't get the error, and I don't get the pagination footer.

  • kthorngrenkthorngren Posts: 21,343Questions: 26Answers: 4,954
    edited May 24 Answer ✓

    I hate to keep saying this but if this url is wrong:

    url":"https:\/\/myapiendpoint.test\/users", // <--- /users is Not the endpoint I want it to use

    Then the problem is with how the Yarja Datatables environment is configured. I don't believe there is anyone on this forum that regularly answers questions that knows the Yarja environment. You will need to refer to the Yarja docs and developers and support forums to learn how to configure the url. I found this doc but don't know enough to know if what you are doing above is correct or the same as the doc. Have you tried searching Stack Overflow?

    Kevin

  • bigdogdmanbigdogdman Posts: 14Questions: 2Answers: 0

    That was it! Now it's showing the correct url in the ajax setting! :smiley: ...except now...it's doing nothing. It's still not loading the pagination html, or the data...oh man, this is so confusing to me... I can't even get it to throw any kind of error now. I somehow seem to have completely broken the pagination, and I certainly wasn't messing around with any of that stuff...

  • allanallan Posts: 63,530Questions: 1Answers: 10,473 Site admin

    With it being able to see it, it is extremely hard to debug, also it does sound like an issue with yajra rather than DataTables.

  • bigdogdmanbigdogdman Posts: 14Questions: 2Answers: 0

    It's working!! it's working!!! Well, sorta... now it's only hitting the callback when I switch entries per page (as opposed to an initial load of data) and the number of entries it shows is only the default return amount (50) from the API, rather than all records (which is what I'm passing to the DT). Is there a reason it doesn't display the data I'm passing to it on create? I guess that would be a Yajra question...

  • kthorngrenkthorngren Posts: 21,343Questions: 26Answers: 4,954

    now it's only hitting the callback when I switch entries per page (as opposed to an initial load of data)

    I'm not sure what callback you are referring to.

    the number of entries it shows is only the default return amount (50) from the API, rather than all records (which is what I'm passing to the DT).

    Sounds like you are trying to configure the lengthMenu option. Yes you will need to refer to the Yarja documentation to see how to configure this with Yarja. The Yarja Datatables library will convert its Laravel like configuration to the Datatables Javascript configuration you see when using View Source. We understand the Javascript config not the Yarja config :smile:

    Kevin

Sign In or Register to comment.