Can anyone explain this TypeScript error?

I do not undestand of TS error

Here is an example of code

interface initParams {
    withoutAuth?: any;
    scripts?: any; // ссылка на файл, который выдает скрипты
    setProxy: any;
}

class LoaderService {
    bar1: any;

    constructor() {

    }
}
class ServerBaseService<T extends LoaderService> {
    constructor(
        private readonly _createClass: { new(): T },

        private readonly isKube: boolean,
    ) {
 
    }
}

class HttpLoaderService extends LoaderService {
    constructor({ withoutAuth, scripts, setProxy }: initParams) {
        super()
    }

}

class ServerHTTPService<T extends HttpLoaderService> extends ServerBaseService<T> {
    constructor(
        private readonly _createClasss: new () => T, //{ new(): T }
        private readonly withoutAuth: boolean,
        private readonly setProxy: boolean,
        private readonly isKubee: boolean
    ) {
        super(_createClasss, isKubee) //_createClasss,  // будет вызван конструктор ServerBaseService
    }
}

class AuthLoaderService extends HttpLoaderService {
}

class AuthenticationServerService extends ServerHTTPService<AuthLoaderService> {

    constructor({ withoutAuth, setProxy, isKube }: { withoutAuth: boolean, setProxy: boolean, isKube: boolean }) {
        super(AuthLoaderService,withoutAuth,setProxy,isKube)

    }

}

TS playground

Why here is an error

Argument of type 'typeof AuthLoaderService' is not assignable to parameter of type 'new () => AuthLoaderService'.
  Types of construct signatures are incompatible.
    Type 'new ({ withoutAuth, scripts, setProxy }: initParams) => AuthLoaderService' is not assignable to type 'new () => AuthLoaderService'

In AuthenticationServerService when calling super it refer on ServerHTTPService constructor.
But error message is refer on HttpLoaderService constructor signature.
Why???
Can anybody explain this error?

Details disclosure using Ctrl+F in Firefox

The <details> element is searchable in Chrome. In other words, I can have something like this:

<details>
  <summary>John Doe</summary>
  <div>123 Main Street</div>
  <div>Springfield, OK</div>
  <div>73000</div>
</details>

Then if I Ctrl+F for “Main Street” Chrome will automatically open the details for me.

However, Firefox doesn’t do this!

How to I get the same behavior in Firefox? Even a custom solution? Basically I want content to be hidden by default, but so I can toggle it open and also use Ctrl+F to find it.

FCM Cloud Messaging – Provide Credentials Manually

In attempting to follow the instructions detailed at https://firebase.google.com/docs/cloud-messaging/migrate-v1?authuser=2#provide-credentials-manually, I’m getting

Error sending message: Error: exec is not supported

I think the issue lies in something needing to be changed for the manual approach, but aside from adding the headers section to the message I don’t know what to do.

Regarding the code below, assume that getAccessToken() is implemented and verified to be working properly, and I’m aware that registrationToken is defined such that an invalid token error should be rendered, the entire issue is that I’m not getting to that error as expected.

const { JWT } = require('google-auth-library');
const admin = require('firebase-admin');

exports = async function(){

  var accessToken = getAccessToken();

  admin.initializeApp();

  // This registration token comes from the client FCM SDKs.
  const registrationToken = 'YOUR_REGISTRATION_TOKEN';
  
  const message = {
    headers: {
      'Authorization': 'Bearer ' + accessToken
    },
    notification: {
      title: 'Test title',
      body: 'You need to X!'
    },
    token: registrationToken
  };
  
  // Send a message to the device corresponding to the provided
  // registration token.
  admin.messaging().send(message)
    .then((response) => {
      // Response is a message ID string.
      console.log('Successfully sent message:', response);
    })
    .catch((error) => {
      console.log('Error sending message:', error);
    });
  
};

Why Click event does not work in audio tag [duplicate]

I want to fire a function name shake dots when clicked in audio apparently it does not fire here is the code

   <div class="lyric-word">
                  <span class="sho">.</span>
                  <span class="sho">.</span>
                  <span class="sho">.</span>
                  <span class="sho">.</span>
                  <span class="sho">.</span>
                  <span class="sho">.</span>
            </div>      
<div class="btm">
            <audio id="thisaudio" onclick="shakeDots()" src="https://pagalworld.com.mx/wp-content/uploads/2023/02/Until-I-Found-You.mp3"
                  controls controlsList="nodownload noplaybackrate"></audio>
      </div>

JS

   function shakeDots() {
            const $click= document.querySelectorALL('.shok');
            $click.classList.add('shak'); 
            console,log("Clicked")
        }

Thanks

In OpenTelemetry typescript, can you exclude instruments from view?

Let’s say I have something like this:

const fooView = new View({
      instrumentName: "*_foo",
      // options for foo instruments
    });

const barView = new View({
      instrumentName: "*_bar",
      // options for bar instruments
    });

Then I also want to have something like this:

const defaultView = new View({
      // do something here to capture all instruments EXCEPT foo and bar ones
    });

And just for full context, these will then be used like this:

var meterProvider = new MeterProvider({
      views: [fooView, barView, defaultView],
      // other options here
    });

My question is: how do I define defaultView to achieve this? The problem is instrumentName is not a standard regexp. And the view documentation from here doesn’t mention any way of excluding specific names. Is there a workaround for this?

Resizing KonvaJS Dynamically Similar to Canvas

I have a need to adjust the KonvaJS Container/Stage in a similar way to a canvas element. I’ve provided an example below showcasing my exact need. In this code the source of the image to select is the canvas, and so I need the canvas and the Konva elements to dynamically update to the screen size and perfectly align while still maintaining dimensions of 1000×1000.

However, for some reason the Konva container’s (select-container) width is not adjusting the same way as the canvas does with the same css. In addition, it seems that the stage is not adjusting to the fit within the container. Is there any built in solution to fix my issue?

<!DOCTYPE html>
<html>
  <head>
    <script src="https://unpkg.com/[email protected]/konva.min.js"></script>
    <meta charset="utf-8" />
    <title>Canvas to Konva Object</title>
    <style>
      body {
        margin: 0;
        padding: 0;
        overflow: hidden;
        background-color: #f0f0f0;
      }
      .over-container {
            top: 100%;
            display: flex;
            flex-wrap: nowrap; /* Prevent wrapping */
            flex-direction: column;
            width: 100%;
            height: 100vh;
            align-items: center;
            margin-top: 35px;
        }

        #canvas-container {
            position: relative;
            width: 100%;
            overflow: hidden;
            /* Aspect Ratio */
            padding-top: 100%;
            display: flex;
            justify-content: center;
        }

        #select-container {
            cursor: crosshair;
            position: absolute;
            top: 0;
            left: 50%;
            transform: translate(-50%);
            max-width: calc(100vw - 10px); /* Subtracting 5px margin on both sides */
            /* Set explicitly or let JavaScript control */
            border: 1px solid #571bfa;
            flex: 1;
        }

        #canvas {
            cursor: crosshair;
            position: absolute;
            top: 0;
            left: 50%;
            transform: translate(-50%);
            /* Adjust size based on viewport */
            max-width: calc(100vw - 10px); /* Subtracting 5px margin on both sides */
            touch-action: none; 
            image-rendering: pixelated; 
            flex: 1;
            border: 1px solid #000;
        }
    </style>
  </head>
  <body>
    <div class="over-container" id="Ocontainer">
        <div id="canvas-container">
            <canvas id="canvas"></canvas>
            <div id="select-container"></div>
        </div>
    </div>
    <script>
        var width = 1000;
        var height = 1000;

        // Get the canvas and set dimensions
        var canvas = document.getElementById('canvas');
        canvas.width = width;
        canvas.height = height;

        var ctx = canvas.getContext('2d');
        ctx.fillStyle = 'blue';
        ctx.fillRect(50, 50, 300, 200);
        ctx.fillStyle = 'orange';
        ctx.fillRect(400, 100, 200, 300);

        // Create Konva stage and layer
        var stage = new Konva.Stage({
            container: 'select-container',
            width: width,
            height: height,
        });

        var layer = new Konva.Layer();
        stage.add(layer);

        var selectionRectangle = new Konva.Rect({
            fill: 'rgba(0, 0, 255, 0.5)',
            visible: false,
            listening: false,
        });
        layer.add(selectionRectangle);

        var x1, y1, x2, y2, selecting = false, activeObject = null;
        var copiedObject = null;
        var objects = [];
        var transformers = []; // Keep track of transformers for cleanup

        // Helper function to create a transformer for a given object
        function createTransformer(object) {
            const transformer = new Konva.Transformer({
            nodes: [object],
            });
            layer.add(transformer);
            transformer.update();
            transformers.push(transformer); // Keep track of the transformer
            return transformer;
        }

        stage.on('mousedown touchstart', (e) => {
            if (e.target !== stage) return;

            e.evt.preventDefault();
            x1 = stage.getPointerPosition().x;
            y1 = stage.getPointerPosition().y;
            selecting = true;

            selectionRectangle.setAttrs({
            visible: true,
            x: x1,
            y: y1,
            width: 0,
            height: 0,
            });
        });

        stage.on('mousemove touchmove', (e) => {
            if (!selecting) return;

            e.evt.preventDefault();
            x2 = stage.getPointerPosition().x;
            y2 = stage.getPointerPosition().y;

            let width = Math.abs(x2 - x1);
            let height = Math.abs(y2 - y1);

            // Check if Shift key is held
            if (e.evt.shiftKey) {
            // Force square by setting width and height to the smaller of the two
            const sideLength = Math.min(width, height);
            width = sideLength;
            height = sideLength;

            // Adjust x2 and y2 to maintain the square shape
            if (x2 < x1) x2 = x1 - width; // Adjust for leftward movement
            else x2 = x1 + width; // Adjust for rightward movement

            if (y2 < y1) y2 = y1 - height; // Adjust for upward movement
            else y2 = y1 + height; // Adjust for downward movement
            }

            selectionRectangle.setAttrs({
            x: Math.min(x1, x2),
            y: Math.min(y1, y2),
            width: width,
            height: height,
            });

            layer.batchDraw();
        });

        stage.on('mouseup touchend', () => {
            if (!selecting) return;
            selecting = false;
            selectionRectangle.visible(false);

            const box = selectionRectangle.getClientRect();
            const sx = box.x;
            const sy = box.y;
            const sw = box.width;
            const sh = box.height;

            if (sw === 0 || sh === 0) return;

            // Get the selected area from the visible canvas
            const imageData = ctx.getImageData(sx, sy, sw, sh);

            // Remove the selected portion from the canvas
            ctx.clearRect(sx, sy, sw, sh);

            // Create an off-screen canvas to crop the image data
            const tempCanvas = document.createElement('canvas');
            tempCanvas.width = sw;
            tempCanvas.height = sh;
            const tempCtx = tempCanvas.getContext('2d');
            tempCtx.putImageData(imageData, 0, 0);

            // Create a Konva.Image from the cropped area
            const image = new Konva.Image({
                x: sx,
                y: sy,
                image: tempCanvas,
                draggable: true,
            });
            layer.add(image);
            objects.push(image);

            // Create a transformer for this object
            createTransformer(image);

            activeObject = image;
            layer.batchDraw();
        });

        window.addEventListener('keydown', (e) => {
            if (e.ctrlKey && e.key === 'c' && activeObject) {
                // Copy the active object
                const clonedCanvas = document.createElement('canvas');
                clonedCanvas.width = activeObject.image().width;
                clonedCanvas.height = activeObject.image().height;
                const clonedCtx = clonedCanvas.getContext('2d');
                clonedCtx.drawImage(activeObject.image(), 0, 0);

                copiedObject = {
                    x: activeObject.x() + 10,
                    y: activeObject.y() + 10,
                    image: clonedCanvas,
                    rotation: activeObject.rotation(),
                    scaleX: activeObject.scaleX(),
                    scaleY: activeObject.scaleY(),
                };
            }
            if (e.ctrlKey && e.key === 'v' && copiedObject) {
                // Paste the copied object
                const newCopy = new Konva.Image({
                    x: copiedObject.x,
                    y: copiedObject.y,
                    image: copiedObject.image,
                    draggable: true,
                    rotation: copiedObject.rotation,
                    scaleX: copiedObject.scaleX,
                    scaleY: copiedObject.scaleY,
                });
                layer.add(newCopy);
                objects.push(newCopy);

                // Create a transformer for the new copy
                createTransformer(newCopy);

                activeObject = newCopy;
                layer.batchDraw();
            }
        });

        stage.on('click tap', (e) => {
            if (activeObject && e.target === stage) {
            // Apply all objects back to the canvas
            objects.forEach((obj) => {
                const image = obj.image(); // Get the underlying image
                const rotation = obj.rotation(); // Rotation angle in degrees

                // Use getClientRect to get transformed position
                const clientRect = obj.getClientRect();
                const boundingBoxCenterX = clientRect.x + clientRect.width / 2;
                const boundingBoxCenterY = clientRect.y + clientRect.height / 2;

                // Use the original image dimensions
                const originalWidth = image.width;
                const originalHeight = image.height;

                // Use scaleX and scaleY for scaling
                const scaledWidth = originalWidth * obj.scaleX();
                const scaledHeight = originalHeight * obj.scaleY();

                // Save the canvas context state
                ctx.save();

                // Translate to the bounding box center
                ctx.translate(boundingBoxCenterX, boundingBoxCenterY);

                // Rotate the canvas context around the center of the bounding box
                ctx.rotate((rotation * Math.PI) / 180);

                // Draw the object onto the canvas using the original dimensions and scaling
                ctx.drawImage(
                image,
                -scaledWidth / 2, // Center the image horizontally
                -scaledHeight / 2, // Center the image vertically
                scaledWidth,
                scaledHeight
                );

                // Restore the canvas context state
                ctx.restore();
            });

            // Destroy all Konva objects and transformers
            objects.forEach((obj) => obj.destroy());
            transformers.forEach((transformer) => transformer.destroy());
            transformers = [];

            // Reset state variables
            objects = [];
            activeObject = null;

            // Redraw the layer
            layer.batchDraw();
            }
        });

        const selectContainer = document.getElementById('select-container');
        const canvasContainer = document.getElementById('canvas-container');
        let imageSize = 1000;

        resizeCanvas();
        window.addEventListener('resize', resizeCanvas);
        window.addEventListener('load', resizeCanvas);
        document.body.addEventListener('resize', function(event) {
            document.body.style.zoom = 1;
        });

        function resizeCanvas() {
            const screenWidth = window.innerWidth;
            const screenHeight = window.innerHeight;
            const spaceToBottom = window.innerHeight - (canvasContainer.offsetTop + canvasContainer.offsetHeight);
            const topElementsHeight = screenHeight;

            canvas.width = Math.round(imageSize);
            canvas.height = Math.round(imageSize);

            canvas.style.maxHeight = topElementsHeight + 'px';
            canvas.style.height = topElementsHeight - spaceToBottom + 'px';

            selectContainer.style.setProperty('width', Math.round(imageSize) + 'px', 'important');
            selectContainer.style.setProperty('height', Math.round(imageSize) + 'px', 'important');

            selectContainer.style.setProperty('max-height', topElementsHeight + 'px', 'important');
            selectContainer.style.setProperty('height', topElementsHeight - spaceToBottom + 'px', 'important');


            const newWidth = Math.round(imageSize);
            const newHeight = Math.round(imageSize);

            stage.width(newWidth);
            stage.height(newHeight);
            stage.batchDraw();

            redrawCanvas();
        }

        function redrawCanvas() {
            // Clear the main canvas and set the background to white
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            ctx.fillStyle = 'blue';
            ctx.fillRect(50, 50, 300, 200);
            ctx.fillStyle = 'orange';
            ctx.fillRect(400, 100, 200, 300);
        }
    </script>
  </body>
</html>

Cannot get seconds from Timestamp object from Firestore

I am creating a Todo app in Angular using Firestore as my backend. I am really new to Firebase and I am having trouble storing and fetching dates.

When I get the dates from firebase and console.log them i get something like this: date: _Timestamp { seconds: 1736290800, nanoseconds: 0 }

I understand that to get a human readable date out of this i need to access the seconds part of this and convert it. I have tried to use date.seconds and date.getSeconds() but .seconds gives me this error:

[ERROR] TS2339: Property ‘seconds’ does not exist on type ‘Date’. [plugin angular-compiler]

src/app/pages/todolist/todolist.component.ts:79:47:
  79 │         console.log("date: ",todo.deadlineDate.seconds);

And date.getSeconds() is not recognized as a function.

It seems like the date retrieved from firebase is not recognized as a Timestamp object.

I have tried making a Timestamp object of the variable using Timestamp.fromDate() but it did not work.

I am using Angular 18 with AngularFirestore and the dates are from user input with MatDatePickerModule and MatNativeDateModule and is stored as a Date in the object sent to firebase.

Appreciate if someone would have a solution to this.

Error Building .AAB File: Could Not Find com.facebook.react:react-native-gradle-plugin

I’m encountering an issue when trying to build the .aab file using the command:
./gradlew bundleRelease
The error message is as follows:

A problem occurred configuring root project 'ProjectName'.
> Could not resolve all files for configuration ':classpath'.
   > Could not find com.facebook.react:react-native-gradle-plugin:.
     Required by:
         project :

It says the build failed because it “could not resolve all files for configuration ‘:classpath'” and could not find com.facebook.react:react-native-gradle-plugin.

The component exists in my project and is specified as a dependency in the build.gradle. It is also applied correctly. Here is a sample of my build.gradle:

Path: C:\ProjectName\front\android\build.gradle

dependencies {
    classpath("com.android.tools.build:gradle:8.0.0")
    classpath("com.facebook.react:react-native-gradle-plugin")
    classpath("com.google.gms:google-services:4.3.15")
    classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.0")
}

I have tried updating versions and making changes in both build.gradle and package.json as per recommended solutions, but the issue persists.
Here is the output of the npx react-native info command for reference:

info Fetching system and libraries information...
(node:8980) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
(Use `node --trace-deprecation ...` to show where the warning was created)
System:
  OS: Windows 11 10.0.26100
  CPU: (12) x64 Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz
  Memory: 56.05 GB / 63.75 GB
Binaries:
  Node:
    version: 22.11.0
    path: C:Program Filesnodejsnode.EXE
  Yarn: Not Found
  npm:
    version: 10.9.0
    path: C:Program Filesnodejsnpm.CMD
  Watchman: Not Found
SDKs:
  Android SDK:
    API Levels:
      - "33"
      - "34"
      - "35"
    Build Tools:
      - 30.0.3
      - 34.0.0
      - 35.0.0
    System Images:
      - android-35 | Google Play Intel x86_64 Atom
    Android NDK: Not Found
  Windows SDK: Not Found
IDEs:
  Android Studio: AI-242.23339.11.2421.12550806
  Visual Studio: Not Found
Languages:
  Java: 17.0.13
  Ruby: Not Found
npmPackages:
  "@react-native-community/cli": Not Found
  react: Not Found
  react-native: Not Found
  react-native-windows: Not Found
npmGlobalPackages:
  "*react-native*": Not Found
Android:
  hermesEnabled: true
  newArchEnabled: false
iOS:
  hermesEnabled: Not found
  newArchEnabled: Not found

Search within a string and stops immediately at the first found

I’m working with CSV files and need to check newline character.

This function works fine:

function detectNewlineCharacter(csvContent)
{
  if (csvContent.includes('rn')) {
    return 'rn'; // Windows-style CRLF
  } else if (csvContent.includes('n')) {
    return 'n'; // Unix-style LF
  } else if (csvContent.includes('r')) {
    return 'r'; // Old Mac-style CR
  } else {
    return null; // No recognizable newline characters found
  }
}

function fixCsv()
{
  // ...code...
  const newlineCharacter = detectNewlineCharacter(fileContent);
  const rows = fileContent.split(newlineCharacter);
  // ...code...

}

Problem:

csvContent is very large. I need a method that stops immediately at the first found, just like .some() for array. ChatGPT said .includes() stops at the first found, but I’m no sure, can’t find that in the documentation. So does .match(), .indexOf(), etc.

My last resort would be to limit the string using .substring() before searching.

Bubbling up mouse events outside of an IFrame

I have a transparent IFrame that covers the whole page (imagine an IFrame on top of the Google Maps). The IFrame has some UI elements (like menus) so I would like to be able to handle mouse events inside the IFrame in order to interact with the menus, but I want to bubble them up to the parent window if the events were not handled by the IFrame UI.

I am able to successfully pass events to the parent using messaging. I am also able to trigger the events using dispatchEvent, however some UI elements on the page don’t behave as expected. For instance, the text can’t be selected, the texbox doesn’t get the focus, the button is not visually clicked (event though it does get the click event).

Am I trying to do something impossible? Is there a way to trigger events “for real” as if they were caused by the user actions?

Here is he main page:

window.addEventListener('message', (event) => {

  let mouseEvent = new MouseEvent(event.type, event.data);
  let elements = document.elementsFromPoint(mouseEvent.x, mouseEvent.y);
  if (elements.length < 2) return;

  let element = elements[1];
  mouseEvent.currentTarget = element;
  mouseEvent.target = element;

  element.dispatchEvent(new MouseEvent(event.data.type, event));
});
<input type="text" id="text" />
        
<button onmousedown="console.log('clicked')">Click</button>

Some text

<iframe id="frame" style="position:absolute; top: 0px; left: 0px; width: 100%; height: 100%" src="iframe.html"></iframe>  

Here is the IFrame:

document.onmousedown = bubleUp;
document.onmouseenter = bubleUp;
document.onmouseleave = bubleUp;
document.onmousemove = bubleUp;
document.onmouseout = bubleUp;
document.onmouseover = bubleUp;
document.onmouseup = bubleUp;
document.ondblclick = bubleUp;
document.oncontextmenu = bubleUp;
document.onclick = bubleUp;

function bubleUp (event) 
{
    let pojo = {};

  // Strip off all the props that can't be serializaed into a mesasge
  for (let key in event)
  {
      let value = event[key];
      if (value instanceof Window || value instanceof Node || value instanceof InputDeviceCapabilities || typeof value === 'function')
          continue;

          pojo[key] = value;
  }

  sendMessageToParent(pojo);
}

function sendMessageToParent(data) {
  window.parent.postMessage(data, '*'); // Send message to parent window
}
<body style="width:100%; height: 100%;">

</body>

Change color of other slices on hover with Chart.js 4

I’m trying to change the color of other slices when hovering one of them, but is a little buggy sometimes and I’m trying to understand why.

As shown in the example, there is a flickering when hovering from one slice to another and the colors don’t change right away, and also when leaving the slice the colors should reset, but it doesn’t work sometimes.

const colors = ["red", "green", "blue"];
const colorsRGBA = ["rgba(255,0,0,.25)", "rgba(0,255,0,.25)", "rgba(0,0,255,.25)"];

new Chart(document.getElementById("doughnut"), {
  type: "doughnut",
  data: {
    labels: ["A", "B", "C"],
    datasets: [
      {
        data: [1, 2, 3],
        backgroundColor: ["red", "green", "blue"],
        hoverBackgroundColor: ["red", "green", "blue"]
      }
    ]
  },
  options: {
    plugins: {
      tooltip: {
        enabled: false
      },
      legend: {
        display: false
      }
    },
    onHover(event, elements, chart) {
      if (event.type === "mousemove") {
        if (elements.length) {
          chart.getDatasetMeta(0).data.forEach((data, index) => {
            if (index !== elements[0].index) {
              data.options.backgroundColor = colorsRGBA[index];
            }
          });
        } else {
          chart.getDatasetMeta(0).data.forEach((data, index) => {
            data.options.backgroundColor = colors[index];
          });
        }
      }
    }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.1/chart.umd.min.js"></script>
<div style="height: 350px">
  <canvas id="doughnut"></canvas>
</div>

Array of specific Object in Javascript

I am working on a Vue.js application where I am managing a list of clients. I want to ensure I am applying Object-Oriented Programming (OOP) concepts by using a Client class for each client in my array. However, my clients array is currently just a plain array of objects, not instances of a Client class.

Here is what I am trying to achieve:

I want to fetch the client data from an API.
After fetching, I would like to instantiate a Client class for each client in the response data, so that each client has methods and properties defined in the class.
Currently, my clients array is just an array of plain JavaScript objects, not instances of the Client class.
Here’s what I have so far:

export default class Client {
  constructor(id, cin, nom, prenom) {
    this.id = id;
    ****

  }

}

Fetching data and mapping it to the Client class:

import Client from './Client';
import clientService from '../services/clientService';

export default {
  data() {
    return {
      clients: [],
    };
  },
  created() {
    this.fetchClients();
  },
  methods: {
    fetchClients() {
      clientService.findAll()
        .then(response => {
          // Mapping the raw data to instances of the Client class
          this.clients = response.data.map(clientData => new Client(clientData.id, clientData.cin, clientData.nom, clientData.prenom));
        })
        .catch(error => {
          console.error('Error fetching clients:', error);
        });
    },
  },
};

After calling this.clients = response.data.map(...), I expected the clients array to contain instances of the Client class. However, it seems to be a simple array of plain JavaScript objects.

In java for example , I can specify the type of instances that I will be storing inside my array. In JavaScript, we typically don’t enforce class-based structures for simple data models.

I mean why not :

clients: [] of Clients

Sorry if the question sounds dumb because everything is working fine , but I need some clarification.

Vue 2 to 3 upgrade: simplifying checkbox question component

I have a checkbox form question component I made in Vue 2 and i’m migrating to Vue 3 so want to simplify it as much as possible and make sure i’ve taken advantage of all the new feature of Vue 3.

After a lot of trial and error i’ve managed to get it to work, but have I over complicated it?

The component should take a question using v-slot and answers in the ‘options’ array prop.
It should let the user select as many answers as applicable but if they select ‘none’ it should clear all other answers.
If they have ‘none’ selected then they select another answer it should clear ‘none’ so it isn’t checked anymore.

Parent component

<template>
    <div>
        <h2>title</h2>

        <InputCheckboxGroup
            name="question_one"
            :required="checkIsRequired('question_one')"
            :error="formRef.errors.get('question_one')"
            v-model="formRef.question_one"
            :options="getField('question_one')['options']"
            @update:modelValue="handleAffectedByInput"
        >
            <template v-slot:question>
                {{ getField("question_one").question }}
            </template>
        
        </InputCheckboxGroup>

    </div>
</template>

<script setup>
import InputCheckboxGroup from "../Form/InputCheckboxGroup";
import Form from "../../../../Form";
import { isRequired } from "./formHelper.js";
import { ref, markRaw, defineExpose } from "vue";

const props = defineProps({
    step: Object,
    fields: Object,
    formData: Object
})


let formRef = markRaw(ref(new Form({
    question_one: []
}, props.formData)))

const getField = (fieldName) => {
   return props.fields[fieldName];
}

const checkIsRequired = (fieldName) => {
   var fieldData = getField(fieldName);
   return isRequired(fieldData, formRef.value);
}

function handleAffectedByInput(values) {
    if (values.length && (values[values.length - 1] === 'none')) {
        formRef.question_one = [values[values.length - 1]];
        return;
    }
   clearCheckboxValues(['none'], values, 'question_one');
}

function clearCheckboxValues(previousAnswers, values, formField) { 
    for (const answer of previousAnswers) {
        if (values.includes(answer)) {
            formRef.value[formField] = values.filter(value => value !== answer);
        }
    }
}

defineExpose({
 formRef
})
</script>

Child/Checkbox questionn componet

<template>
    <div class="form-group">
        <fieldset :aria-describedby="name">
            <legend>
                <p v-if="$slots.question" class="input__question">
                    <slot name="question"></slot>
                </p>
            </legend>
            <div
                class="input_checkbox"
                v-for="opt in options"
                v-bind:key="opt.value"  
            >           
                <label v-if="opt.value == 'none'">
                    <input
                        type="checkbox"
                        value="none"
                        v-model="model"
                        @click="onCheckboxChange"
                    />
                    <span>None</span>
                </label>    
                <div v-else class="form-group form-check">
                    <input  
                        type="checkbox"
                        class="form-check-input"
                        :value="opt.value"
                        @click="onCheckboxChange"
                        v-model="model"
                    />
                    <label :for="opt.value" class="form-check-label">
                        {{ opt.label }}
                    </label>    
                </div>
            </div>
        </fieldset>
    </div>
</template>
  
<script setup>
import { defineModel, defineEmits } from "vue";
const props = defineProps({
    error: String,
    name: String,
    options: Array,
    required: Boolean
});

const model = defineModel()

const emit = defineEmits(['update:modelValue'])

function optionIsChecked (value) {
    return model.value.includes(value);
}

function onCheckboxChange($event) {
    var previouslySelected = model.value || [];
    var newValue = [];
    if ($event.target.checked) {
        newValue = [...previouslySelected, $event.target.value];
    } else {
        newValue = previouslySelected.filter(
            (x) => x != $event.target.value
        );
    }
    
    if ($event.target.value === 'none' || $event.target.value === 'involved_none_above') {
        newValue = [$event.target.value];
    }
    
    model.value = newValue
    
    emit("update:modelValue", newValue);
}

</script>

PrimeVue DataTable Lazy Loading not working with API calls

I’m implementing lazy loading with virtual scrolling in a PrimeVue DataTable. The data should be dynamically fetched from an API as the user scrolls. While the initial data fetch (first 25 accounts) works and displays correctly in the table, subsequent data loaded on scroll does not appear in the UI, even though the virtualAccounts array updates correctly and matches the expected length.

I also checked the official PrimeVue documentation about lazy loading and virtual scrolling (https://primevue.org/datatable/#lazy_virtualscroll) and tried to follow everything there, but still, it doesn’t work.

Due to the size of my project (over 22,000 lines of code) and the fact that the relevant module is nearly 1,000 lines long, I can only share the essential portions of my implementation:

DataTable configuration:

<DataTable
  dataKey="ID"
  ref="table"
  :value="virtualAccounts"
  scrollable
  scroll-height="calc(100vh - 23rem)"
  :virtualScrollerOptions="{
    lazy: true,
    onLazyLoad: loadAccountsLazy,
    itemSize: 46,
    delay: 200,
    showLoader: true,
    loading: lazyLoading,
    numToleratedItems: 10,
  }"
/>

LazyLoading logic:

const virtualAccounts = ref(
  Array.from({ length: 100 }, () => ({
    ID: null,
    Rating: 0,
    CompanyName: "",
    Remark: "",
    TradeRegisterNumber: "",
    CreditLimit: 0,
    Revenue: 0,
    FoundationYear: null,
    Employees: 0,
    VatID: "",
    CreatedAt: "",
    UpdatedAt: "",
  }))
);

const lazyLoading = ref(false);

const fetchAccountsOnDemand = async (first, last) => {
  try {
    const response = await axios.get("/api/data/list", {
      params: { first, last },
    });
    return response.data || { data: [] };
  } catch (error) {
    console.error("Error fetching data:", error);
    return { data: [] };
  }
};

const loadAccountsLazy = async (event) => {
  lazyLoading.value = true;

  try {
    const { first, last } = event;
    const response = await fetchAccountsOnDemand(first, last);

    if (Array.isArray(response.data)) {
      const _virtualAccounts = [...virtualAccounts.value];

      for (let i = first; i < last; i++) {
        _virtualAccounts[i] = { ID: null, Rating: 0, CompanyName: "", Remark: "", TradeRegisterNumber: "", CreditLimit: 0, Revenue: 0, FoundationYear: null, Employees: 0, VatID: "", CreatedAt: "", UpdatedAt: "" };
      }

      const processedData = response.data.map((account) => ({
        ID: account.ID || null,
        Rating: account.Rating || 0,
        CompanyName: account.CompanyName || "",
        Remark: account.Remark || "",
        TradeRegisterNumber: account.TradeRegisterNumber || "",
        CreditLimit: account.CreditLimit || 0,
        Revenue: account.Revenue || 0,
        FoundationYear: account.FoundationYear || null,
        Employees: account.Employees || 0,
        VatID: account.VatID || "",
        CreatedAt: account.CreatedAt || "",
        UpdatedAt: account.UpdatedAt || "",
      }));

      _virtualAccounts.splice(first, processedData.length, ...processedData);
      virtualAccounts.value = JSON.parse(JSON.stringify(_virtualAccounts));
    }
  } catch (error) {
    console.error("Error during lazy loading:", error);
  } finally {
    lazyLoading.value = false;
  }
};

Initial data fetch:

onMounted(async () => {
  try {
    const initialData = await fetchAccountsOnDemand(0, 25);

    if (Array.isArray(initialData.data)) {
      const processedData = initialData.data.map((account, i) => ({
        ID: account.ID || i++,
        Rating: account.Rating || 0,
        CompanyName: account.CompanyName || "",
        Remark: account.Remark || "",
        TradeRegisterNumber: account.TradeRegisterNumber || "",
        CreditLimit: account.CreditLimit || 0,
        Revenue: account.Revenue || 0,
        FoundationYear: account.FoundationYear || null,
        Employees: account.Employees || 0,
        VatID: account.VatID || "",
        CreatedAt: account.CreatedAt || "",
        UpdatedAt: account.UpdatedAt || "",
      }));

      virtualAccounts.value = Array(100).fill({
        ID: null,
        Rating: 0,
        CompanyName: "",
        Remark: "",
        TradeRegisterNumber: "",
        CreditLimit: 0,
        Revenue: 0,
        FoundationYear: null,
        Employees: 0,
        VatID: "",
        CreatedAt: "",
        UpdatedAt: "",
      });
      virtualAccounts.value.splice(0, processedData.length, ...processedData);

      console.log("MOUNT:", virtualAccounts.value);
    }
  } catch (error) {
    console.error("Error during initial data fetch:", error);
  }
});

To troubleshoot further, I even tested the virtual scrolling functionality with a testing component where the data is generated entirely in the frontend (no backend calls). In that case, the virtual scrolling works perfectly, suggesting the issue lies somewhere in the integration with the backend-fetching logic or how the virtualAccounts array is updated/reactive.

Working example:

<template>
  <div class="card">
    <DataTable
      :value="virtualCars"
      scrollable
      scrollHeight="500px"
      tableStyle="min-width: 50rem"
      :virtualScrollerOptions="{
        lazy: true,
        onLazyLoad: loadCarsLazy,
        itemSize: 46,
        delay: 200,
        showLoader: true,
        loading: lazyLoading,
        numToleratedItems: 10,
      }"
    >
      <Column field="id" header="Id" style="width: 20%">
        <template #loading>
          <div
            class="flex items-center"
            :style="{ height: '17px', 'flex-grow': '1', overflow: 'hidden' }"
          >
            <Skeleton width="60%" height="1rem" />
          </div>
        </template>
      </Column>
      <Column field="vin" header="Vin" style="width: 20%">
        <template #loading>
          <div
            class="flex items-center"
            :style="{ height: '17px', 'flex-grow': '1', overflow: 'hidden' }"
          >
            <Skeleton width="40%" height="1rem" />
          </div>
        </template>
      </Column>
      <Column field="year" header="Year" style="width: 20%">
        <template #loading>
          <div
            class="flex items-center"
            :style="{ height: '17px', 'flex-grow': '1', overflow: 'hidden' }"
          >
            <Skeleton width="30%" height="1rem" />
          </div>
        </template>
      </Column>
      <Column field="brand" header="Brand" style="width: 20%">
        <template #loading>
          <div
            class="flex items-center"
            :style="{ height: '17px', 'flex-grow': '1', overflow: 'hidden' }"
          >
            <Skeleton width="40%" height="1rem" />
          </div>
        </template>
      </Column>
      <Column field="color" header="Color" style="width: 20%">
        <template #loading>
          <div
            class="flex items-center"
            :style="{ height: '17px', 'flex-grow': '1', overflow: 'hidden' }"
          >
            <Skeleton width="60%" height="1rem" />
          </div>
        </template>
      </Column>
    </DataTable>
  </div>
</template>

<script setup>
import { ref, onMounted } from "vue";

const cars = ref([]);
const virtualCars = ref(Array.from({ length: 100000 }));
const lazyLoading = ref(false);
const loadLazyTimeout = ref();

// Generate car data
const generateCar = (id) => {
  const brands = ["Toyota", "Ford", "BMW", "Honda", "Mazda"];
  const colors = ["Red", "Blue", "Green", "Black", "White"];

  return {
    id,
    vin: `VIN${id}`,
    year: 2000 + (id % 23), // Random year from 2000 to 2023
    brand: brands[id % brands.length],
    color: colors[id % colors.length],
  };
};

onMounted(() => {
  cars.value = Array.from({ length: 100000 }).map((_, i) => generateCar(i + 1));
});

const loadCarsLazy = (event) => {
  !lazyLoading.value && (lazyLoading.value = true);

  if (loadLazyTimeout.value) {
    clearTimeout(loadLazyTimeout.value);
  }

  // Simulate remote connection with a timeout
  loadLazyTimeout.value = setTimeout(() => {
    let _virtualCars = [...virtualCars.value];
    let { first, last } = event;

    console.log(event);

    // Load data of required page
    const loadedCars = cars.value.slice(first, last);

    // Populate page of virtual cars
    Array.prototype.splice.apply(_virtualCars, [
      ...[first, last - first],
      ...loadedCars,
    ]);

    virtualCars.value = _virtualCars;
    lazyLoading.value = false;
  }, Math.random() * 1000 + 250);
};
</script>

Issue:

  1. Initial Load: The first 25 records fetch and display correctly.

  2. Subsequent Loads: When scrolling to load more data, the virtualAccounts array updates as expected, but the UI does not reflect the changes.

Troubleshooting steps:

  1. Verified that the virtualAccounts array updates correctly with the fetched data.

  2. Confirmed that the DataTable renders correctly when using a frontend-only example with generated data.

  3. Ensured I followed the PrimeVue documentation for lazy loading and virtual scrolling.

Questions:

  1. Are there any specific steps missing in my implementation based on the PrimeVue guidelines?

  2. Could the issue be related to how the virtualAccounts array is updated or its reactivity?