Modal Not Displaying When Button Is Clicked in ASP.NET Web Forms

I am working on an ASP.NET Web Forms application where I have a GridView and a button for each row that should open a modal when clicked. However, the modal is not opening when I click the edit button.

I have a Button in my GridView, and I am trying to call a JavaScript function openUpdateTaskModal() using the OnClientClick attribute. The modal is functioning properly when I call the openUpdateTaskModal() function directly from the browser console, but it does not open when I click the button.

Here’s my code for DataGridView:

    <div  class="table-wrapper">
        
        <asp:GridView ID="gvTasks" runat="server" AutoGenerateColumns="False" CssClass="custom-table" OnRowCommand="gvTasks_RowCommand">
            <Columns>
                <asp:TemplateField HeaderText="Select">
                    <ItemTemplate>
                        <asp:Button ID="btnSelect" runat="server" 
                                    Text='<%# bool.Parse(Eval("IsActive").ToString()) ? "✔" : "❌" %>' 
                                    CommandName="MarkComplete" 
                                    CommandArgument='<%# Eval("TaskID") %>' 
                                    CssClass='<%# bool.Parse(Eval("IsActive").ToString()) ? "btn-edit-green" : "btn-edit-red" %>' />
                    </ItemTemplate>

                </asp:TemplateField>

       
                <asp:BoundField DataField="Title" HeaderText="Title" SortExpression="Title" />

                <asp:BoundField DataField="Description" HeaderText="Description" SortExpression="Description" />

      
                <asp:BoundField DataField="CategoryName" HeaderText="Category" SortExpression="CategoryName" />

      
                <asp:BoundField DataField="StartDate" HeaderText="Start Date" SortExpression="StartDate" />

      
                <asp:BoundField DataField="EndDate" HeaderText="End Date" SortExpression="EndDate" />

       
                <asp:TemplateField HeaderText="Completed">
                    <ItemTemplate>
                        <asp:CheckBox ID="chkCompleted" runat="server" Checked='<%# Eval("IsActive") %>' Enabled="False" />
                    </ItemTemplate>
                </asp:TemplateField>

                <asp:TemplateField HeaderText="Actions">
                    <ItemTemplate>
                        

                        <asp:Button ID="btnEdit" runat="server" CommandName="EditTask" CommandArgument='<%# Eval("TaskID") %>' Text="Edit" CssClass="btn-edit" />
                        <asp:Button ID="btnDelete" runat="server" CommandName="DeleteTask" CommandArgument='<%# Eval("TaskID") %>' Text="Delete" CssClass="btn-delete" />
                    </ItemTemplate>
                </asp:TemplateField>
            </Columns>
        </asp:GridView>
            
    </div>

</div>

Code For Modal:

        <div class="modal fade" id="uupdateTaskModal" tabindex="-1" aria-labelledby="uupdateTaskModalLabel" aria-hidden="true">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <asp:Label ID="ulModalTitle" runat="server" Text="Update Task" CssClass="modal-title"/>
                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
            </div>
            <div class="modal-body">

                


                <asp:TextBox ID="utxtTitle" runat="server" CssClass="form-control" placeholder="Title"></asp:TextBox>
                <asp:TextBox ID="utxtDescription" runat="server" CssClass="form-control mt-2" placeholder="Description"></asp:TextBox>
                <asp:DropDownList ID="uddlCategory" runat="server" CssClass="form-control mt-2">
                    <asp:ListItem Text="Select Category" Value="" />
                    
                </asp:DropDownList>
                <asp:TextBox ID="utxtStartDate" runat="server" CssClass="form-control mt-2" placeholder="Start Date" TextMode="Date"></asp:TextBox>
                <asp:TextBox ID="utxtEndDate" runat="server" CssClass="form-control mt-2" placeholder="End Date" TextMode="Date"></asp:TextBox>
                <asp:CheckBox ID="uchkIsActive" runat="server" Text="Is Active" CssClass="mt-2" />
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
                <asp:Button ID="ubtnSave" runat="server" Text="Update" CssClass="btn btn-primary"  />
            </div>
        </div>
    </div>
</div>

backend code:

protected void gvTasks_RowCommand(object sender, GridViewCommandEventArgs e)
{
if (e.CommandName == "EditTask")
{
    EditTask(Convert.ToInt32(e.CommandArgument));
    string script = "$('#uupdateTaskModal').modal('show')";
    ClientScript.RegisterStartupScript(this.GetType(), "modal", script, true);

  
}

            private void EditTask(int TaskID)
        {
            
            TheTask = clsTasks.Find(TaskID);
            if (TheTask != null)
            {
                ulModalTitle.Text = "Update Task";
                utxtTitle.Text = TheTask.Title;
                utxtDescription.Text = TheTask.Description;
                uddlCategory.SelectedIndex = TheTask.CategoryID;
                utxtStartDate.Text = TheTask.StartDate.ToString("dd/MM/yyyy");
                utxtEndDate.Text = TheTask.EndDate.ToString("dd/MM/yyyy");
                uchkIsActive.Checked = TheTask.IsActive;
            }
            else
            {
                // رسالة خطأ إذا كانت البيانات غير موجودة
                ulModalTitle.Text = "Error";
                utxtTitle.Text = "";
                utxtDescription.Text = "";
                uddlCategory.SelectedIndex = -1;
                utxtStartDate.Text = "";
                utxtEndDate.Text = "";
                uchkIsActive.Checked = false;
            }
           
        }


What am I missing or doing wrong?

tried placing the GridView inside the UpdatePanel, but it didn’t work. I tried many solutions from ChatGPT and searched on YouTube, but I couldn’t find the solution.

Note: When I run the function in the browser console, it works fine, but it doesn’t work when clicking the Edit button inside the GridView. Also, I noticed that the page seems to reload when the button is clicked, which hides the modal.

I really don’t know what to do. I hope for your help.

How to pass event and argument to AgGrid event?

My AgGrid is running a function on the onCellEdited event:

<template>
<AgGridVue
    :rowData="rows"
    :columnDefs="columns"
    :gridOptions="defaultOptions"
    @onCellEdited="onCellEdited"
  />
</template>

<script setup>
import AgGridVue from '@/components/AgGridVue.vue'

function onCellEdited(params){
  console.log(params)
}
</script>

I need this function to capture the event and an additional argument. I’ve tried doing this:

<template>
<AgGridVue
    :rowData="rows"
    :columnDefs="columns"
    :gridOptions="defaultOptions"
    @onCellEdited="event => onCellEdited(event, 'myargument')"
  />
</template>

<script setup>
import AgGridVue from '@/components/AgGridVue.vue'

function onCellEdited(params, arg){
  console.log(params, arg)
}
</script>

but the argument is always returning undefined. How can I make this work?

No output Using Visual Studio Code [closed]

I just downloaded Visual Studio Code and wrote my first program: HelloWorld, the problem is that there is no output. No bugs. Please let know the fix.

I tried reloading Visual Studio Code. Watching YouTube videos. Watching VSC tutorials.

The screen says: Activating task providers java

Is there a nicer way to work on an array in a loop? [closed]

Let’s say I have an array of 12 elements; each containing an integer.
I want to call a function. That function would get as parameter the place in the array. Then the function would read the value of this element in the array; let’s call it ‘nTime’. Set it to zero. And will increment by 1 the value contained in the next cells of the array for the ‘nTime’ given. Also if the next cell which value should be incremented is > to the number cells in the array then that value should be inscribed in the first cell of the array and so on. In a circle.

The LOGIC of the game is: 2 players with 6 ‘houses’ each. The first house of the active player is on the lower row to the lefts. So that the the other player who ‘sits’ in front of him has his first house on the upper row but diametrically opposed.

I’m trying to do that. I haven’t fully tested it yet but I think I’m trying to reinvent the wheel when a certainly easier solution exists:

Here is what I’ve done so far:

class classGroup {
  constructor(nbrPeas, occupied) {
    this.nbrPeas = 4;
    this.occupied = 'false';
    this.isFull = 'false';
  }
}

var myArray = [];

for (var i = 1; i < 13; i++) {
  myArray[i] = new classGroup(4, 'false', 'false');
}

function shiftCells(callingCell) {
  var byID
  var startLoop = callingCell + 1;
  var stopLoop = callingCell + myArray[callingCell].nbrPeas + 1;

  myArray[callingCell].nbrPeas = 0;


  for (var i = startLoop; i < stopLoop; i++) {
    if (myArray[i].isFull === 'false') {
      if (i < 13) {
        if ((myArray[i].nbrPeas + 1 < 12)) {
          myArray[i].nbrPeas = myArray[i].nbrPeas + 1;
        } else {
          myArray[i].isFull = 'true';
          spillOverflow(i);
          stopLoop = stopLoop + 1;
        }
      } else {
        myArray[i - 12].nbrPeas = myArray[i - 12].nbrPeas + 1;
        spillOverflow(i);
        stopLoop = stopLoop + 1;
      }
    } else {
      stopLoop = stopLoop + 1;
    }
  }

  for (var i = 1; i < 13; i++) {
    byID = 'cell' + i;
    document.getElementById(byID).innerText = myArray[i].nbrPeas;
    console.log(myArray[i].nbrPeas);
  }
}

function spillOverflow(cellNbr) {
  var freeFound = false;
  var cellIncr = cellNbr + 1;

  while (freeFound === false) {
    if (myArray[cellIncr].isFull === 'false') {
      freeFound = true;
      myArray[cellIncr].isFull = 'true';
    } else {
      cellIncr = cellIncr + 1;
    }
  }
}
table,
th,
td,
tr {
  border: 2px solid;
  text-align: center;
  padding: 10px;
  cursor: pointer;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">

<table style="width: 70%; margin-top: 10px; margin-left: 15%; font-size: 1.8em;">
  <tr style="text-align:center">
    <th id="cell12">4</th>
    <th id="cell11">4</th>
    <th id="cell10">4</th>
    <th id="cell9">4</th>
    <th id="cell8">4</th>
    <th id="cell7">4</th>
  </tr>
  <tr>
    <th id="cell1" onclick="shiftCells(1)">4</th>
    <th id="cell2" onclick="shiftCells(2)">4</th>
    <th id="cell3" onclick="shiftCells(3)">4</th>
    <th id="cell4" onclick="shiftCells(4)">4</th>
    <th id="cell5" onclick="shiftCells(5)">4</th>
    <th id="cell6" onclick="shiftCells(6)">4</th>
  </tr>
</table>

That is just a draft working on the logic for something bigger. Clicking on any cell in the bottom row should work and gives expected values.

Is there a ‘nicer’ way to get to the same result?

Paginate API response object with multiple sub arrays

I have this response from a REST API service:

{
    "Business": [
        {
            "link": "...",
            "og": "...",
            "source": "...",
            "source_icon": "...",
            "title": "..."
        },
    ],
    "Entertainment": [
        {
            "link": "...",
            "og": "...",
            "source": "...",
            "source_icon": "...",
            "title": "..."
        },
    ],
    "Health": [
        {
            "link": "...",
            "og": "...",
            "source": "...",
            "source_icon": "...",
            "title": "..."
        },
    ],
    "Science": [
        {
            "link": "...",
            "og": "...",
            "source": "...",
            "source_icon": "...",
            "title": "..."
        },
    ],
    "Sports": [
        {
            "link": "...",
            "og": "...",
            "source": "...",
            "source_icon": "...",
            "title": "..."
        },

    ],
    "Technology": [
        {
            "link": "...",
            "og": "...",
            "source": "...",
            "source_icon": "...",
            "title": "..."
        },
    ],
    "US": [
        {
            "link": "...",
            "og": "...",
            "source": "...",
            "source_icon": "...",
            "title": "..."
        },
    ],
    "World": [
        {
            "link": "...",
            "og": "...",
            "source": "...",
            "source_icon": "...",
            "title": "..."
        },
    ]
}

In my Vue app I’ve created this function to get the data and I’m using a ref to manage them inside my application:

import { ref, onMounted } from 'vue';
import axios from 'axios';
import { NewsResponse } from '@/utils';

const baseURL = '...';

const isLoading = ref<boolean>(true);
const newsFeed = ref<NewsResponse>();
//const pageSize = 12;


onMounted(() => {
  fetchNewsFeed();
});

const fetchNewsFeed = () => {
  axios.get(baseURL)
    .then((res) => {
      newsFeed.value = res.data;
      isLoading.value = false;
      console.log('api responsen',newsFeed.value);
      
    })
    .catch((err) => {
      console.log(err);
    })
}

Since each category into the response object will be an array, at the moment I’m managing it in my template using a v-for loop, all is working fine, but each array can have from 46 to 50 elements inside and this after looping the elements will result in a long age to scroll.

 <div 
    class="grid grid-cols-4 gap-4 py-3"
    v-for="(articlesCollection, cat) in newsFeed" 
    :key="cat">
    <div 
      class="card shadow-lg w-fit my-2 rounded-md"
      v-for="(article, index) in articlesCollection"
      :key="index"
    >
      <figure>
        <img class="w-full h-48" :src="article.og">
      </figure>
      <div class="card-body">
        <div class="badge badge-primary text-white">
          {{ cat }}
        </div>
        <a 
          class="no-underline" 
          :href="article.link"
        >
          <h1 class="card-title text-sm">
            {{ article.title }}
          </h1>
        </a>
        <div class="card-actions">
          <div class="badge badge-outline">
            {{ article.source }}
          </div>
        </div>
      </div>
    </div>
  </div>

My idea is to have 12 elements in a grid of 4 element for each single row and when the user reach the end of the page load new elements like the infinite scroll will do normally. How I can correctly paginate the elements in this case? I’ve looked at this question, but in my case not sure if I can use slice, due to the fact that I will have multiple array inside the response object.

Filter objects within objects by keys [closed]

I have a data structure like this :

const data = {
    "CHI" : { fr: "chien", en: "dog"},
    "CHA" : { fr: "chat", en: "cat"},
    "POU" : { fr: "poule", en: "chicken"},
    "OIS" : { fr: "oiseau", en: "bird"},
}

From wich I would like to create 2 objects like this :

// #1

const fr = {
    "CHI" : "chien"},
    "CHA" : "chat"},
    "POU" : "poule"},
    "OIS" : "oiseau"},
}

// #2

const en = {
    "CHI" : "dog"},
    "CHA" : "cat"},
    "POU" : "chicken"},
    "OIS" : "bird"},
}

Many thanks !

Error while using jsPDF( Error: Could not load dompurify: ReferenceError: dompurify is not defined) in Angular 14 application

I am using jsPDF for angular and created custom config file and added this code,

module.exports = {
    // ...
    externals: {
      canvg: "canvg",
      html2canvas: "html2canvas",
      dompurify: "dompurify"
    }
  };

The app runs using ng build –watch.
I have tried many options but it still gives me an error: ERROR Error: Uncaught (in promise): Error: Could not load dompurify: ReferenceError: dompurify is not defined
Error: Could not load dompurify: ReferenceError: dompurify is not defined

Is there any workround for this? Any suggestions will be helpful.

Not able to deploy a firebase cloud function that checks a Twitch channel’s status

I implemented the following function in my Firebase with Cloud Functions backend. Becasue, I want to be able to have my Twitch API token in an environment variable, because the frontend is pure HTML and JavaScript with no Node whatsoever.

import * as functions from "firebase-functions";
import * as admin from "firebase-admin";
import * as cors from 'cors';

admin.initializeApp();
const db = admin.firestore();
const corsHandler = cors({ origin: true });

import fetch from 'node-fetch';
export const twitchStatus = functions.https.onRequest(async (req, res) => {
  corsHandler(req, res, async () => {
    // const origin = req.get('origin') || req.get('referer');
    // if (origin !== allowedOrigin) {
    //   res.status(403).json({ error: "Access forbidden, Invalid origin" });
    //   return;
    // }

    try {
      const { channelName } = req.body;

      const url = `https://api.twitch.tv/helix/streams?user_login=${channelName}`;
      const clientId = process.env.TWITCH_CLIENT_ID as string;
      const accessToken = process.env.TWITCH_ACCESS_TOKEN as string;
  
      const response = await fetch(url, {
        method: 'GET',
        headers: {
          'Client-ID': clientId,
          'Authorization': `Bearer ${accessToken}`
        }
      });
      const data = await response.json();

      res.status(201).json({ message: "Twitch status created successfully", data: data});
    } catch (error) {
      res.status(500).json({ error: "An error occurred while creating the twitch status." });
    }
  });
});

When trying to deploy, I get the following error:

i  functions: updating Node.js 18 (2nd Gen) function twitchStatus(us-central1)...
Could not create or update Cloud Run service twitchstatus, Container Healthcheck failed. Revision 'twitchstatus-00001-rej' is not ready and cannot serve traffic. The user-provided container failed to start and listen on the port defined provided by the PORT=8080 environment variable within the allocated timeout. This can happen when the container port is misconfigured or if the timeout is too short. The health check timeout can be extended. Logs for this revision might contain more information.

Logs URL: ---
For more troubleshooting guidance, see https://cloud.google.com/run/docs/troubleshooting#container-failed-to-start

Functions deploy had errors with the following functions:
        twitchStatus(us-central1)
i  functions: cleaning up build files...
!  functions: Unhandled error cleaning up build images. This could result in a small monthly bill if not corrected. You can attempt to delete these images by redeploying or you can delete them manually at ---

I have four more similar functions that deploy as expected without errors.

What is going wrong here?

Application pushing content to client browser [closed]

I am writing a console application in C#, I’ve written the web-server and now I would like to implement a feature where the application can push content to clients.

So far when a client connects to the application/web-server the client IP is added to a list of subscribers. The intention is that the console application will push content to the list of subscribers and if an ACK response is not received from each client then that client will be removed from the subscriber list.

The question is how do I push content to the clients?

What makes an element unclickable when using locator(), but clickable when using $()?

I’m trying to automate posting content to Facebook. Assuming you don’t need to login, here is my code:

import { Page } from "npm:puppeteer";

// Initiate
const browser = await puppeteer.launch({
headless: false,
userDataDir: "./user_data",
args: minimal_args.concat(args),
});
const page = await browser.newPage();
await page.goto("https://facebook.com/");

// Create a post
await page.locator("::-p-text(What's on your mind)").click();
await page.keyboard.type(text);

// Click the next button
const nextButtonSelector = '*[aria-label="Next"][role="button"]';
await (await page.$(nextButtonSelector))!.click(); // this works
// await page.locator(nextButtonSelector).click(); // timeout

// Click the post button
await page.locator('*[aria-label="Post"][role="button"]').click();

At the “Click the next button” step, I have to use the Page.$() method, not Page.locator() one. I notice that when no character has been typed in yet, the button is gray out, indicates that it is disabled. I suspect this plays a role in here, but I cannot explain better. The excellent answer in Page.locator() is the recommended way to select and interact with elements. Why does it offer less functionalities than Page.$()? explains the difference between the two, but I don’t know how to apply the knowledge in here.

Generate a default mapping on TypeScript

I have an interface like this (but with over 20 fields)

export interface HouseDetails {
    title: string | undefined;
    url: URL;
    address?: string;
    logo?: URL;
    price?: number;
    sq_meters?: number;
}

And I want to generate a record with the keys of HouseDetails but all of its values set to a default value (for example, the empty string ""). This is the code I tried, but when I print it to a console, it is an empty object:

function generateDefaultMapping<T extends Object, D>(default_value: D): Record<keyof T, D> {
  return Object.fromEntries(Object.keys({} as T).map((key) => [key, default_value])) as Record<keyof T, D>;
}

// Use the function to create a default mapping for HouseDetails
const default_mapping<Record<keyof HouseDetails, string> = generateDefaultMapping<HouseDetails, string>("");
console.log(default_mapping) // {}

I think the problem is that this does not work like C++ templates, but, is there any way of doing it?

How to show previous values for missing rows when comparing two Excel datasets in JavaScript

I’m working on a script to compare two Excel files (an “old” and a “new” dataset) using JavaScript and the xlsx library. The goal is to highlight changes, additions, and deletions between the datasets. However, I’m encountering a specific issue:

When a row exists in the old dataset but is missing in the new dataset (e.g., its value is now blank), the script doesn’t display the previous value from the old dataset in the output.

For example:

Old dataset:
| ID | NAME | OCCUPATION |
| ——– | ————– | ————– |
| 1 | Walter White | Teacher |
| 2 | Skyler White | Homemaker |
| 3 | Jesse Pinkman | |
| 4 | Saul Gooodman | Lawyer |
| 5 | Hank Schrader | DEA agent |

New dataset:
| ID | NAME | OCCUPATION |
| ——– | ————– | ————– |
| 1 | Walter White | Teacher |
| 2 | Skyler White | Homemaker |
| 3 | Jesse Pinkman | Dealer |
| 4 | Saul Goodman | Lawyer |
| 5 | Hank Schrader | |

enter image description here

Expected output:

ID 5 should show the previous occupation (“DEA agent”) in the output, highlighted in red.

Here’s the relevant portion of my script that handles the comparison:

 document.getElementById('excelDataset1').addEventListener('change', function() {
updateFileList(this.files, 'excelFileList1');
});

 document.getElementById('excelDataset2').addEventListener('change', function() {
updateFileList(this.files, 'excelFileList2');
});

async function readExcel(file) {
return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = (e) => {
        const data = new Uint8Array(e.target.result);
        const workbook = XLSX.read(data, { type: 'array' });
        resolve(workbook);
    };
    reader.onerror = () => reject(reader.error);
    reader.readAsArrayBuffer(file);
});
}

async function parseSheet(sheet, pkColumns) {
const json = XLSX.utils.sheet_to_json(sheet, { header: 1 });
const headers = json[0];
const data = {};

for (let i = 1; i < json.length; i++) {
    const row = json[i];
    if (row.length > 0) {
        const id = pkColumns.map(pk => row[headers.indexOf(pk)]).join('_');
        data[id] = row;
    }
}

return { data, headers };
}

async function compareExcelFiles() {
const outputContainer = document.getElementById('outputContainer');
outputContainer.innerHTML = '';

const dataset1Files = document.getElementById('excelDataset1').files;
const dataset2Files = document.getElementById('excelDataset2').files;

if (dataset1Files.length === 0 || dataset2Files.length === 0) {
    outputContainer.textContent = 'Please select files for both datasets.';
    return;
}

const dataset1FileMap = {};
for (let file of dataset1Files) {
    dataset1FileMap[file.name] = file;
}

for (let file2 of dataset2Files) {
    if (dataset1FileMap[file2.name]) {
        const file1 = dataset1FileMap[file2.name];

        const predefined = predefinedFiles[file2.name] || null;
        const workbook1 = await readExcel(file1);
        const workbook2 = await readExcel(file2);

        const sheet1 = workbook1.Sheets[workbook1.SheetNames[0]];
        const sheet2 = workbook2.Sheets[workbook2.SheetNames[0]];

        const headers1 = XLSX.utils.sheet_to_json(sheet1, { header: 1 })[0];
        const headers2 = XLSX.utils.sheet_to_json(sheet2, { header: 1 })[0];

        if (headers1.join('t') !== headers2.join('t')) {
            outputContainer.innerHTML += `<div class="table-title">${file2.name}</div><p>Headers do not match between the files.</p>`;
            continue;
        }

        let pkColumns = predefined || headers1.filter(header => /id/i.test(header));
        if (pkColumns.length === 0) {
            pkColumns = [headers1[0]]; // Default to the first column if no 'id' fields exist
        }

        const { data: dataset1 } = await parseSheet(sheet1, pkColumns);
        const { data: dataset2 } = await parseSheet(sheet2, pkColumns);

        const dataset1Keys = new Set(Object.keys(dataset1));
        const dataset2Keys = new Set(Object.keys(dataset2));

        const commonKeys = [...dataset2Keys].filter(id => dataset1Keys.has(id));
        const addedKeys = [...dataset1Keys].filter(id => !dataset2Keys.has(id));
        const deletedKeys = [...dataset2Keys].filter(id => !dataset1Keys.has(id));

        let resultHtml = '';
        let differencesFound = false;

        commonKeys.forEach(id => {
            const row1 = dataset1[id];
            const row2 = dataset2[id];

            if (row1.join('t') !== row2.join('t')) {
                if (!differencesFound) {
                    resultHtml += `<div class="table-title">${file2.name}</div>`;
                    resultHtml += '<table class="tbl"><thead><tr>';

                    resultHtml += '<th>';
                    headers1.forEach(header => {
                        resultHtml += `<span class="tblhdrClmName">${header}</span> `;
                    });
                    resultHtml += '</th>';

                    resultHtml += '</tr></thead><tbody>';
                    differencesFound = true;
                }

                const highlightedRow = highlightDifferences(row2, row1);
                resultHtml += `<tr><td>${highlightedRow}</td></tr>`;
            }
        });

        addedKeys.forEach(id => {
            if (!differencesFound) {
                resultHtml += `<div class="table-title">${file2.name}</div>`;
                resultHtml += '<table class="tbl"><thead><tr>';

                resultHtml += '<th>';
                headers1.forEach(header => {
                    resultHtml += `<span class="tblhdrClmName">${header}</span> `;
                });
                resultHtml += '</th>';

                resultHtml += '</tr></thead><tbody>';
                differencesFound = true;
            }

            const row = dataset1[id];
            const highlightedRow = row.map(cell => `<span class="ok">${cell}</span>`).join(';');
            resultHtml += `<tr><td>${highlightedRow}</td></tr>`;
        });

        deletedKeys.forEach(id => {
            if (!differencesFound) {
                resultHtml += `<div class="table-title">${file2.name}</div>`;
                resultHtml += '<table class="tbl"><thead><tr>';

                resultHtml += '<th>';
                headers1.forEach(header => {
                    resultHtml += `<span class="tblhdrClmName">${header}</span> `;
                });
                resultHtml += '</th>';

                resultHtml += '</tr></thead><tbody>';
                differencesFound = true;
            }

            const row = dataset2[id];
            const highlightedRow = row.map(cell => `<span class="notok">${cell}</span>`).join(';');
            resultHtml += `<tr><td>${highlightedRow}</td></tr>`;
        });

        if (differencesFound) {
            resultHtml += '</tbody></table>';
            outputContainer.innerHTML += resultHtml;
        }
    }
}

}

How can I ensure that when a value is missing in the new dataset, the previous value from the old dataset is displayed correctly?

I would appreciate any guidance on how to fix this issue while making minimal changes to the existing script. Thank you!

Get request/response body in Neoload to manipulate it using Javascript code

I need to get the http request body using JavaScript in neoload to manipulate the request and further get the response body to manipulate it again.

Basically I need the neoload equivalent of the following code:

pm.request.body = {
    mode: 'raw',
    raw: JSON.stringify(encryptedRequestBody)
};

I have tried below but did not work

context.currentVirtualUser.getRequestBody();

How to use Access Tokens to implement Protected Routes in React

The token for my react application is stored in the AuthContext Provider and whenever the user is logged, the token is set using the setToken() function.

AuthContext.js

import { useContext, useState, createContext, useMemo } from "react";
import {createAxiosInstance} from '../services/axiosInstance';
import { useNavigate } from "react-router-dom";

const AuthContext = createContext();

const AuthProvider = ({children}) => {
    const [token, setToken] = useState(null);

    // creates a new instance of axios Instance when the token is changed
    const axiosInstance = useMemo(() => createAxiosInstance(token), [token]);

    // create the login and signup functions here
    const login = (email, password) => {
        const data = {"email": email, "password": password};
        console.log("Logging the user in");
        const response = axiosInstance.post('/auth', data);

        return new Promise((resolve, reject)=>{
            response.then((responseData) => {
                const {accessToken, message} = responseData.data;
                setToken(accessToken);
                return resolve(message);
            })
            .catch((err)=>{
                return reject("Wrong email or password");
            })
        })
    }

    return (
        <AuthContext.Provider value={{token,setToken, axiosInstance, login}}>
            {children}
        </AuthContext.Provider>
    );
}

const useAuth = () => {
    const context = useContext(AuthContext);
    if(context === undefined){
        throw new Error("The Context must be used under the AuthContextProvider");
    }
    return context;
}

export {AuthProvider as default, useAuth}

Inorder to implement protected routes in my application, I wrapped this component around all the routes that I wanted to be protected

import { jwtDecode } from "jwt-decode"
import { useAuth } from "../contexts/AuthProvider"
import { Navigate, Outlet } from "react-router-dom";

const ProtectedRoutes = ({ children }) => {
    const { token, setToken, axiosInstance } = useAuth();

    // if there is no token, send the user to the authentication route
    if (!token) {
        return <Navigate to='/users/authenticate' />
    }

    try {
        // get the expiry time from the token
        const { exp } = jwtDecode(token);
        const expired = (Date.now() >= exp * 1000);

        
        if (expired) {
            axiosInstance.post('/auth/refresh')     // if the token has expired, try to get a new token
                .then((response) => {
                    const { accessToken } = response.data;
                    setToken(accessToken)
                    return <Outlet />
                })
                .catch((err) => {   // if the user has no token stored in their cookies, then send them to login page
                    return <Navigate to="/users/authenticate" />
                })
        }
        else{
            return <Outlet />   // if the token has not expired, continue
        }
    }
    catch (err) {
        return <Navigate to='/users/authenticate' />
    }
}

export default ProtectedRoutes;

But whenever I refresh the page, the token is set to null and protected routes logs me out.
How can I save the access Token other than storing it in localStorage or sessionStorage as I heard that those methods are not safe to attacks.

How to catch lost internet connection with Server-Sent-Events (SSE) and prevent the browser from showing its error page?

I am working with SSE and realized that the browser always goes to its error page if the internet connection gets lost. In the moment of losing the connection it shows “This site can’t be reached”, then a moment later: “No Internet – ERR_INTERNET_DISCONNECTED”.

I am certain that the cause is the SSE connection because all other sites on my server without SSE get loaded and keep showing even if the internet connection is lost.

With Javascript I tried to catch the lost connection with:

sse_source.onerror = function(e) 
{
    e.preventDefault(); // prevent default, not working
    
    // close SSE source
    sse_source.close();
    
    // alert shows up and interrupts, but after clicking Okay the browser error page is displayed
    alert('Connection to Server lost. No Live Updates.');
}

But it does nothing.

I also tried:

window.addEventListener('offline', updateOnlineStatus);

function updateOnlineStatus(e)
{
    e.preventDefault(); // prevent default, not working

    sse_source.close();

    alert('Connection to Server lost. No Live Updates.');
}

Here it does not display the alert. The browser always goes to its error page.

Is there any other Javascript to keep showing the website?

Updates:

I can see in the console that the sse_source.onerror is still processed completely when the connection is lost. But cannot stop the error page from showing up from there.

After the sse_source.onerror the window.addEventListener('offline', updateOnlineStatus) is processed. Cannot stop error page from here either.

Ah, it actually shows the alert() with sse_source.onerror. I updated the first code block.