xbrowse (likely a custom table) whose contents change dynam

mod_harbour is an Apache module that allows to run PRGs directly on the web !!!

xbrowse (likely a custom table) whose contents change dynam

Postby Otto » Sat May 04, 2024 7:42 am

xbrowse (likely a custom table) whose contents change dynamically


Checkboxes that control the appearance of radio menus and changes to the xbrowse's size.

Here's a possible scenario based on your description:



Initial State:

The xbrowse table is visible.

Checkboxes are located beneath it.

First Checkbox Clicked:

The xbrowse's size changes.

A group of radio menus appear based on the selection.

Second Checkbox Clicked:

The xbrowse's size changes further.

Another group of radio menus appear.

Unchecking the Checkboxes:

If checkboxes are unchecked, the radio menus related to those checkboxes disappear.

The xbrowse is adjusted back to its initial state.

To achieve this, you likely need to:



viewtopic.php?f=45&t=44481



Listen to Checkbox Events: Attach JavaScript event listeners to the checkboxes.

Manipulate the DOM: Based on which checkboxes are checked, modify the xbrowse's size and visibility of radio menus.

Adjust xbrowse Layout: Modify the layout or size of the xbrowse according to which radio menus are visible.



Dynamic Filter Interaction and Data Submission
This feature enables users to interactively apply filters to the displayed data, adjusting the table's view based on selected criteria. Each filter change triggers a real-time update, sending the selected filter states and values to the server for precise data processing.

To achieve what you want, you need to call a JavaScript function each time the filter inputs are changed. This function will use the Fetch API to send the selected filter values to the server. Here’s how you can modify your existing JavaScript to achieve this:



Add a JavaScript function to handle the fetch request.

Modify the existing change event handlers to call the new function.



The JavaScript fetch function is used to make asynchronous HTTP requests to servers. It provides a modern, promise-based way to request data, send data, or interact with web APIs. With fetch, you can easily send HTTP requests like GET, POST, PUT, etc., and handle responses in a clean and readable manner.

The JavaScript function fetch then calls the mod harbour program data.prg.
A JSON string is passed to the server through the body property of the fetch request.

fetch sends:

filterData in the body to the mod harbour program:

filterData is a JavaScript object that contains the data you want to send to the server

JSON.stringify(filterData) converts this object into a JSON string, which is a text representation of the object.

Image

Image




mod harbour data.prg
This harbour code can be considered an API endpoint because it is designed to handle HTTP requests and return responses in a structured format (JSON).

Here are some reasons why:

HTTP Request Handling: It uses functions like AP_Body() to get the request data, which indicates that it's built to handle HTTP requests.
JSON Data Processing: The function hb_jsondecode decodes incoming JSON data, and the response is structured as a JSON object using hb_jsonEncode.
Response Formatting: It sets the content type to application/json and formats the response data as JSON, which is typical for API endpoints.
Logic Based on Input: It processes the input data (the keys and values in hPost) and generates a response based on this data, which is a common pattern in API endpoints.


The function processes a POST request containing JSON data, logs the data, and returns a JSON response with the processing time and a success message.

It decodes the JSON into a Harbour hash object (hPost), extracts the keys from the decoded hash, and logs their values. It then sends a JSON response back to the client.

We are only logging the request that we receive from the client in this code. It is only for demonstration purposes.

Here, one would then integrate the harbour query, i.e. plain harbour code.

The result would then be assigned to the HASH and sent back to the client.
hData['data'] := here is the result of the e.g. filter evaluation



Detailed Explanation
Variable Initialization:
hData: A hash to store the response data.
hPost: A hash to store the decoded JSON body from the request.
nIdx: An index variable used for iteration.
aParameters: An array to store the keys of the decoded JSON.
speedtest: Stores the current milliseconds count to measure execution time.
Logging:
logline("<H>", time()): Logs the current time to a log file.
JSON Decoding:
hb_jsondecode(AP_Body(), @hPost): Decodes the JSON body of the HTTP request (AP_Body()) into the hPost hash.
logline("-> hPost", ValToChar(hPost)): Logs the decoded hPost hash in a readable format.
Processing Parameters:
aParameters := hb_HKeys(hPost): Gets the keys from the hPost hash into the aParameters array.
logline("hb_HKeys", ValToChar(aParameters)): Logs these keys.
A FOR loop iterates through the keys in aParameters:
Logs each key and its corresponding value in hPost. If a key does not exist, hb_HGetDef returns "---".
Response Preparation:
hData['error']: Indicates whether an error occurred (in this case, 'false').
hData['posted']: A success message indicating the data was posted successfully.
hData['hspeed']: The time taken to execute the function in milliseconds.
hData['response'] and hData['data']: Set to 'false' and 'data', respectively.
Sending the Response:
AP_SetContentType("application/json"): Sets the response content type to JSON.
?? hb_jsonEncode(hData, .T.): Outputs the encoded hData hash in JSON format (.T. for pretty print).



Image


Image

filter.html

Code: Select all  Expand view

<!DOCTYPE html>
<html lang="de">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <style>
        #table-container {
            height: 300px;
            overflow: auto;
        }

        .filter-div {
            display: none;
            /* Beginnt versteckt */
            margin: 0;
            padding: 0;
        }

        #filter-container {
            margin: 0;
            padding: 0;
        }

        .filter-section {
            display: flex;
            align-items: center;
            margin: 0;
            padding: 0;
        }

        .filter-section label {
            margin: 0;
            padding: 0;
        }

        .grid-container {
            display: grid;
            grid-template-columns: min-content auto;
            column-gap: 10px;
            align-items: center;
        }

        .inputs-container {
            display: flex;
            gap: 10px;
        }
    </style>
</head>

<body>
    <div class="container mt-5">
        <div class="row">
            <div class="col-md-12">
                <h2 class="mb-4">Tabellenhöhe anpassen</h2>

                <button id="increase" class="btn btn-success mb-3">+</button>
                <button id="decrease" class="btn btn-danger mb-3">-</button>

                <div id="table-container" class="border mb-3">
                    <table class="table table-striped">
                        <thead>
                            <tr>
                                <th>#</th>
                                <th>Vorname</th>
                                <th>Nachname</th>
                                <th>Email</th>
                            </tr>
                        </thead>
                        <tbody>
                            <tr>
                                <td>1</td>
                                <td>John</td>
                                <td>Doe</td>
                                <td>john.doe@example.com</td>
                            </tr>
                            <!-- Weitere Zeilen hinzufügen, falls nötig -->
                        </tbody>
                    </table>
                </div>

                <!-- Dynamische Filter-Divs -->
                <div id="filter-container">
                    <div id="age-div" class="filter-div grid-container border mb-2 p-2" style="display: none;">
                        <label>Alter:</label>
                        <div class="inputs-container">
                            <label class="d-flex align-items-center gap-1">
                                <input type="radio" name="age-status" value="20"> 20
                            </label>
                            <label class="d-flex align-items-center gap-1">
                                <input type="radio" name="age-status" value="Über 20 Jahre"> Über 20 Jahre
                            </label>
                        </div>
                    </div>

                    <div id="married-div" class="filter-div grid-container border mb-2 p-2" style="display: none;">
                        <label>Verheiratet:</label>
                        <div class="inputs-container">
                            <label class="d-flex align-items-center gap-1">
                                <input type="radio" name="married-status" value="Verheiratet"> Verheiratet
                            </label>
                            <label class="d-flex align-items-center gap-1">
                                <input type="radio" name="married-status" value="Nicht verheiratet"> Nicht verheiratet
                            </label>
                        </div>
                    </div>
                </div>

                <!-- Filterabschnitt -->
                <div class="filter-section grid-container">
                    <label><strong>Gefiltert nach:</strong></label>
                    <div>
                        <input type="checkbox" id="filter-age"> Alter
                        <input type="checkbox" id="filter-married" class="ms-3"> Verheiratet
                    </div>
                </div>

            </div>
        </div>
    </div>

    <script>

        $(document).ready(function () {
            const increment = 50;
            const baseHeight = 300;

            $('#increase').on('click', function () {
                const newHeight = $('#table-container').height() + increment;
                $('#table-container').height(newHeight);
            });

            $('#decrease').on('click', function () {
                const newHeight = $('#table-container').height() - increment;
                if (newHeight >= increment) {
                    $('#table-container').height(newHeight);
                }
            });

            function adjustTableHeight() {
                let filtersHeight = 0;
                if ($('#age-div').is(':visible')) filtersHeight += $('#age-div').outerHeight(true);
                if ($('#married-div').is(':visible')) filtersHeight += $('#married-div').outerHeight(true);

                const newHeight = baseHeight - filtersHeight;
                $('#table-container').height(newHeight >= 50 ? newHeight : 50);
            }

            function sendFilterData() {
                const ageStatus = $('input[name="age-status"]:checked').val() || '';
                const marriedStatus = $('input[name="married-status"]:checked').val() || '';
                const filterAge = $('#filter-age').is(':checked');
                const filterMarried = $('#filter-married').is(':checked');

                const filterData = {
                    age: ageStatus,
                    married: marriedStatus,
                    filterAge: filterAge,
                    filterMarried: filterMarried
                };

                console.log(filterData);
               

                fetch('data.prg', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify(filterData)
                })

                    .then(response => {
                        if (!response.ok) {
                            throw new Error('Netzwerkantwort war nicht ok');
                        }
                        return response.json();
                    })

                    .then(data => {

                        console.log(data);
                    })

                    .catch(error => console.error('Fehler beim Laden der Daten:', error));

            }

            $('#filter-age').change(function () {
                $('#age-div').toggle(this.checked);
                adjustTableHeight();
                sendFilterData(); // Call the fetch function
            });

            $('#filter-married').change(function () {
                $('#married-div').toggle(this.checked);
                adjustTableHeight();
                sendFilterData(); // Call the fetch function
            });

            $('input[name="age-status"]').change(function () {
                sendFilterData(); // Call the fetch function
            });

            $('input[name="married-status"]').change(function () {
                sendFilterData(); // Call the fetch function
            });
        });


    </script>
</body>

</html>
 



data.prg

Code: Select all  Expand view


REQUEST DBFCDX
REQUEST DBFFPT

function main()
    local hdata := {=>} 
    local hPost:= {=>}
    local nIdx := 0
    local aParameters := {}
    local speedtest :=  hb_milliseconds()
    //------------------------------------------------------------------------------
   
    logline( "<H>", time() )
   
    hb_jsondecode( AP_Body(), @hPost )
    logline( "-> hPost", ValToChar( hPost ) )
   
    aParameters :=  hb_HKeys( hPost )
    logline( "hb_HKeys", ValToChar ( aParameters ) )
   
    FOR nIdx := 1 to len( aParameters )
        logline( aParameters[ nIdx ],  ValToChar ( hb_HGetDef( hPost, aParameters[ nIdx ], "---" ) )   )
    next
   
    logLine("---", )

       
    hData['error'] := 'false'
    hData[ 'posted' ] := 'Data Was Posted Successfully'
    hData['hspeed'] := str( hb_milliseconds() - speedtest )
       
    hData['response'] := 'false'
       
    hData['data'] := 'data'
           
    AP_SetContentType( "application/json" )
       
    ?? hb_jsonEncode( hdata, .T. )  // T=pretty
       
return NIL
//----------------------------------------------------------------------------------------//
   

       
   
INIT PROCEDURE PrgInit
       
    SET CENTURY ON
    SET EPOCH TO YEAR(DATE())-98
       
    SET DELETED ON
    SET EXCLUSIVE OFF
       
    REQUEST HB_Lang_DE
       
    HB_LangSelect("DE")
       
    SET DATE TO GERMAN
       
    rddsetdefault( "DBFCDX" )
       
    EXTERN DESCEND
       
    RETURN
    //----------------------------------------------------------------------------//  
   
    {% MemoRead(  "c:\harbourinoFiles\LOGLINE" ) %}
 
********************************************************************
mod harbour - Vamos a la conquista de la Web
modharbour.org
https://www.facebook.com/groups/modharbour.club
********************************************************************
User avatar
Otto
 
Posts: 6332
Joined: Fri Oct 07, 2005 7:07 pm

Return to mod_harbour

Who is online

Users browsing this forum: No registered users and 4 guests