How to require at least one checkbox be checked using Yup and VeeValidate with Vue 3?

I am trying to ensure a user checks at least one checkbox. The web API I am calling requires at least one value be passed in an array of strings. For this example, the user must choose at least one favorite color where the web API expects at least one of red, green or blue, e.g. { "colors": ["red"] } would be valid, but { "colors": [] } would not.

I am using:

  • vue 3.4.29
  • vee-validate 4.14.7
    • vee-validate/rules and vee-validate/i18n are at the same version, if that matters.
  • yup 1.6.1

The code I am using:

<template>
  <Form id="app-form" @submit="onSubmit" @invalid-submit="onInvalidSubmit" v-slot="{ values, errors }" :validation-schema="rules">
    <fieldset>
      <legend>Choose your favorite colors:</legend>

      <label>
        <Field type="checkbox" name="colors[0]" value="red" /> Red<br>
      </label>

      <label>
        <Field type="checkbox" name="colors[1]" value="green" /> Green<br>
      </label>

      <label>
        <Field type="checkbox" name="colors[2]" value="blue" /> Blue<br>
      </label>

      <ErrorMessage name="colors" />
    </fieldset>

    <p>
      <button type="submit">Save Preferences</button>
    </p>
  </Form>
</template>

<script setup>
import { array, date, object, string } from 'yup';

const rules = object().shape({
  colors: array(string()).required('Choose at least one color.')
});
</script>

<script>
  import { Form, Field, ErrorMessage } from 'vee-validate';
  import { configure } from 'vee-validate';

  configure({
    validateOnBlur: false,
    validateOnChange: false
  });

  export default {
    name: 'AppForm',
    components: {
      Form,
      Field,
      ErrorMessage
    },
    methods: {
      onSubmit(values) {
         console.log('Form submitted without errors.');
      },
      onInvalidSubmit(form) {
        console.log('form is invalid', form);
      }
    }
  }
</script>

In the browser’s console, the onInvalidSubmit method is not being called as I expected when no checkboxes are checked. Instead, the onSubmit method is called and I see Form submitted without errors in the browser’s console.

Some other variations I tried:


// form was valid without check marking anything
const rules = object().shape({
  colors: array(string())
    .ensure()
    .required('Choose at least one color.')
});

// form was invalid, but every checkbox said it was required, which is not what I want.
const rules = object().shape({
  colors: array(string().required())
    .required('Choose at least one color.')
});


// form was valid without check marking anything
const rules = object().shape({
  colors: array(string())
    .ensure()
    .min(1, 'Choose at least 1.')
});

// form was invalid without check-marking anything, but was also invalid when check-marking 2 colors.
const rules = object().shape({
  colors: array(string())
    .ensure()
    .length(1)
});

How can I require at least one checkbox be checked using Yup, VeeValidate, and Vue?

I need two things to work:

  1. The form is invalid if no checkboxes are marked.
  2. When no check boxes are marked, display an error message to the user.
    • Note: Use of the <ErrorMessage /> tag is optional as long as I can show the message.

I am very new to Vue. I’ve cobbled together what appears to be a cohesive set of tools, but I just might not be using them right. I feel like I’m fighting the framework here.

jqGrid json data records lines by cols transposed display

Keep track of certain resort rent payments recorded through the following json file data, loading as url in jqGrid 5:

{"rows":[{"id":"tzct01","trsctNo":"001","oprtDte":"22/01/24","amntToPay":440,"pymntTerm":"21/01/24-31/01/24","dueDate":"24/01/24","notes":"period full advnce payment",
       "rcpts":[{"rcptNo":"0280","rcptDte":"18/01/24","rcptAmnt":440,"descrpt":"plata în avans prda: 21/01/24-31/01/24"}]
    },
     {"id":"tzct02","trsctNo":"002","oprtDte":"06/02/24","amntToPay":1160,"pymntTerm":"01/02/24-29/02/24","dueDate":"20/02/24","notes":"plata integrl in avans perioada rfrita",
    "rcpts":[{"rcptNo":"0284","rcptDte":"04/02/24","rcptAmnt":1160,"descrpt":"plata în avans prda: 01/02/24-29/02/24"}]
    },
{"id":"tzct03","trsctNo": "003","oprtDte": "24/03/24","amntToPay":800,"pymntTerm":"01/03/24-20/03/24","dueDate":"10/03/24","notes":"plata partial priada rfrita",
    "rcpts":[{"rcptNo":"0291","rcptDte":"05/03/24","rcptAmnt":100,"descrpt":"plata prtl. în avans prda: 01/03/24-20/03/24"}]
    },
{"id":"tzct04","trsctNo":"004","oprtDte":"14/04/24","amntToPay":440,"pymntTerm":"21/03/24-31/03/24","dueDate":"01/04/24","notes":"fara plati ...",
   "rcpts":"No detailed data for tzct04"},
{"id":"tzct05","trsctNo":"005","oprtDte":"22/04/24","amntToPay":1200,"pymntTerm":"01/04/24-30/04/24","dueDate":"11/04/24","notes":"fara plati ...",
   "rcpts":"No detailed data for tzct05"},
{"id":"tzct06","trsctNo":"006","oprtDte":"05/05/24","amntToPay":1240,"pymntTerm":"01/05/24-31/05/24","dueDate":"14/05/24","notes":"plata partial prioade anterioare",
  "rcpts":[{"rcptNo":"0311","rcptDte":"02/05/24","rcptAmnt":500,"descrpt":"Detailed explanation for tzct06"}]
    },
{"id":"tzct07","trsctNo":"007","oprtDte":"14/06/24","amntToPay":1000,"pymntTerm":"01/06/24-30/06/24","dueDate":"25/06/24","notes":"plata partial prioade anterioare",
     "rcpts":[{"rcptNo":"0323","rcptDte":"10/06/24","rcptAmnt":1200,"descrpt":"Detailed explanation for tzct07_1"},
    {"rcptNo":"0324","rcptDte":"25/06/24","rcptAmnt":1000,"descrpt":"Detailed explanation for tzct07_2"}]
    },
...
{"id":"tzct10","trsctNo":"010","oprtDte":"29/09/24","amntToPay":1000,"pymntTerm":"01/09/24-30/09/24","dueDate":"27/09/24","notes":"plata partial prioade anterioare",
   "rcpts":[{"rcptNo":"0349","rcptDte":"26/09/24","rcptAmnt":1000,"descrpt":"plata chirie pt sptmbr.'24"}]
  },
{"id":"tzct11","trsctNo":"011","oprtDte":"26/10/24","amntToPay":1000,"pymntTerm":"01/10/24-31/10/24","dueDate":"27/10/24","notes":"plata partial prioade anterioare",
   "rcpts":[{"rcptNo":"0352","rcptDte":"25/10/24","rcptAmnt":1000,"descrpt":"plata chirie pt oct.'24"}]
     },
{"id":"tzct12","trsctNo":"012","oprtDte":"29/11/24","amntToPay":1000,"pymntTerm":"01/11/24-30/11/24","dueDate":"28/11/24","notes":"plata partial prioade anterioare",
    "rcpts":[{"rcptNo":"0356","rcptDte":"25/11/24","rcptAmnt":1000,"descrpt":"plata chirie pt nov.'24"}]
     }]
}

As one would notice there is some sort of principal record line starting with that id:tzct01 which has some corresponding “rcpts” subrecord detail
And through the following jq/jqGrid code

$(function()
{
 $("#jqGrd").empty()
 $('<div id="divDoi" style="width:78%; overflow:auto;"><table id="list2" cellspacing="0" cellpadding="0" width="60%"></table>' +
        '<div id="gridpgr2"></div></div>').appendTo('#jqGrd')
 $.ajax({url:'json/pmnts_ops.json',dataType:'json',
        success:function(data)
        { 
         var rows = data.rows, 
rowHdrs=['IB (Init.Blnc)','Oprt Dte','Amnt Pay','Stay Period','Due Date','Totl Amnt To Pay','Cashed In','Pymnt Date','Ending Sum (FB)','Notes'], numColsPerPag=3,crrntPag=1,totPges=Math.ceil(rows.length/numColsPerPag),lastCB=0,pagDtaCache={}

    function loadPage(page)
    {
     if(pagDtaCache[page])
      {
       updateGrid(pagDtaCache[page])
       return
      }
      var start = (page-1)*numColsPerPag, end = Math.min(start+numColsPerPag,rows.length),
         pagedData = rowHdrs.map(function(header) 
                    {
                     return {header:header}
                    })
      for(i=start;i<end;i++)
      {
       var row = rows[i]
        rowHdrs.forEach(function(header,idx)
                {
                 var cellData = ''
                 switch(header)
                 {
                case 'IB (Init.Blnc)':
                 cellData = i === start ? lastCB : pagedData[8]['rcrd'+(i-1)] || 0
                 break
                case 'Oprt Dte':
                 cellData = row.oprtDte || ''
                 break
                case 'Amnt Pay':
                 cellData = row.amntToPay || 0
                 break
                case 'Stay Period':
                 cellData = row.pymntTerm || ''
                 break
                case 'Due Date':
                 cellData = row.dueDate || ''
                 break
                case 'Totl Amnt To Pay':
                 var prevRst = pagedData[0]['rcrd'+i] || 0
                 cellData = (row.amntToPay || 0) + prevRst
                 break
                case 'Cashed In':
                  var chtnteArray = Array.isArray(row.rcpts) ? row.rcpts:[]
                  cellData = chtnteArray.reduce(function(sum,item)
                                  {
                                return sum + (item.rcptAmnt || 0)
                                  }, 0)
                 break
                case 'Pymnt Date':
    cellData = row.rcpts && row.rcpts.length > 0 ? row.rcpts[row.rcpts.length-1].rcptDte : ''
                 break    
                case 'Ending Sum (FB)':
            var TotlAmntPay = pagedData[5]['rcrd'+i] || 0, pyd = pagedData[6]['rcrd'+i] || 0
                 cellData = TotlAmntPay - pyd
                 lastCB = cellData
                 break
                case 'Notes':
                 cellData = row.notes || ''
                 break
                }
       pagedData[idx]['rcrd'+i] = cellData // trnspse rcrds hdrs; do not touch this!
          })
      }
    pagDtaCache[page] = {data:pagedData,colModel:generateColModel(start,end,page)}
    updateGrid(pagDtaCache[page])  // grid update with the new data
  }

    function generateColModel(start,end,page)
    {
     var colModel = [{name:'header',label:'CrrtNo',width:150,align:'left',sortable:false}]
     for(i=start;i<end;i++) // for each page, dynamically update label
      colModel.push({name:'rcrd'+i,label:i+1,width:150,align:'left',sortable:false})
       
     return colModel
    }

    function updateGrid(pageData)
    {
     if($("#list2").jqGrid('getGridParam','data'))
$("#list2").jqGrid('clearGridData').jqGrid('setGridParam',{data:pageData.data,
colModel:pageData.colModel,datatype:'local'}).trigger('reloadGrid')
     else // initialize the grid if not already created
$("#list2").jqGrid({datatype:'local',data:pageData.data,colModel:pageData.colModel,
rowNum:rowHdrs.length,pager:'#gridpgr2',viewrecords:true,height:'auto',caption:'Rsrt's Rents Paymnts Display', altRows:true,loadonce:true,autowidth:false,shrinkToFit:false})
 }

    function updatePagination()
    {
     $('#gridpgr2').html('')
     if(totPges>1)
     {
      $('<span style="margin-left:6cm;font-size:15px;">Page:</span>').appendTo('#gridpgr2')
      for(i=1;i<=totPges;i++)
      {
      var link = $('<a href="#" style="left-margin:1px;padding:2 5px;font-size:16px;">'+i+ '</a>')
       if(i==crrntPag)
       {     
        link.css("font-weight",'bold') // no effect whatsoever, and dont't get it why ..
        link.css("text-decoration",'underline')
      }    
         link.appendTo('#gridpgr2').on('click',(function(i)
                          {
                           return function(e)
                              {
                               crrntPag = i
                               loadPage(crrntPag)
                               updatePagination()
                               }
                           })(i))
       }
    }
 }

  loadPage(crrntPag)
  updatePagination()
  },
   error:function() {
            console.error("Failed to load json data")
        }
  })
})

one could correctly somehow displays transposed rows with columns data along with computed data also. Reason for this transposition (switch lines with columns and vice-versa) is that all this
displaying will be made over the mobile devices.
The actual display with the current jq code is showcased through the Fig1.1 to Fig1.3
pic1
pic2
pic3

In the following, one’ll briefly describe all the specs and requirments for this showcase presenting what is already done it and also a couple of requirments which need to be done
As one could see from this code snippet (and also from the Fig. 1.1 and Fig1.2 etc), json data is succesfully manually and programmaticaly displayed, with transposed rows data as columns and columns as rows..
Only need to display 3 columns at a time (and, of course through this paginations .. one will reach other pages as well)
Next one’ll precisely depict how columns will gonna look like
(Notice some of them are physically present through the json data file while others are calculated fields)
I’ll mention along which ones bounds physical data and which one are computed fields
Here one goes over each column’s row:

1.1 First Column (header cells)

1.’CrrntNo’- sequential numbering json data records (keeps track of every and each record);

2.’IB'(Initial Balance)
– Opening Amount for the whole cash in operations; at first (the very first record) this
amount is 0; but starting with the second one, this value reads the previous record closing balance/ending; very important to keep this in mind

3.’Operation/Transact Date’

  • The Date at which this very transaction was made;

4.’Amnt Pay’

  • Amount to Pay strictly for the given time period;

5.’Stay Period’

  • That’s the Recourse Period for which one will gonna have to pay;

6.’Due Date’

  • Due Date by which the Rent (Amount to Pay at least) should be paid;

7.’Totl Amnt To Pay’
– Here’s the Total Amount to Pay which is the ‘IB’ + ‘Amnt Pay’

8.’Cashed In’
– That’s the Total Amount payed within current month; which is sum of all ‘rcptAmnt’
associated with this particular record

9.’Pymnt Date’
– Payment Date – the Date at which the payment (‘Cashed In’) has been made

10.’Ending Sum (FB)’
– That’s the Closing Balance (CB) or the Remaining Payment Amount is ‘Totl Amnt To Pay’ minus ‘Cashed In’; this CB will be reported then as next record’s opening balance as already mentioned when talked about Initial Balance at the first point; very important to get this

11.’Notes’
– short transact description

Next I’ll precisely describe some of the values (both physical as well as computed) which appear up in the jqGrid

1.2 Second Column (Data Cells)

  1. 1 – only this one is header (header cell)

    • 0 – computed field (0 required if this is the very first row, else become ‘Ending Sum (FB)’ of the prev. line)
    • “22/01/24” – physical data cell
    • 440 – physical data cell
    • “21/01/24-31/01/24” – physical data cell
    • “24/01/24” – physical data cell
    • 440 – computed field (‘IB’ + ‘Amnt Pay’)
    • 440 – computed data cell (SUM (‘rcptAmnt’) from the corresponding ‘rcpts’ subgrid data)
    • “18/01/24” – physical data cell (last ‘rcptDte’ field from “rcpts” subgrid data)
    • 0 – computed data cell (‘Totl Amnt To Pay’ – ‘Cashed In’)
    • ‘period full advnc pymnt’ – physical data cell

1.3 Third Column (Data Cells)

    • 2 header sequence value (only this one is header)
    • 0 – computed field (Ending Sum (FB)’ of the prev. line)
    • “06/02/24” – physical data cell
    • 1160 – physical data cell
    • “01/02/24-29/02/24” – physical data cell
    • “20/02/24” – physical data cell
    • 1160 – computed field (‘IB’ + ‘Amnt Pay’)
    • 1160 – computed data cell (SUM (‘rcptAmnt’) from the corresponding subgrid data)
    • “04/02/24” – physical data cell (last ‘rcptDte’ field from “rcpts” subgrid data)
    • 0 – computed data cell (‘Totl Amnt To Pay’ – ‘Cashed In’)
    • ‘period full advnc pymnt’ – physical data cell
      and so on and so forth..

All the computations are practically the same as they were when everything was horizontally displayed.

Basically how it is right now all the computations reads quite well on all pages but still there are some requirments which are presented bellow

Requirments:

  1. Very important, one should programatically have that very first row
    ‘NrCrt 1 2 3’ as main Header; which over the first line is exactly the same,
    but for the other pages that top header, horizontally will have to read

                   'NrCrt  4 5  6 '(second page)
    
                   'NrCrt  7 8  9 '(third page) 
    

and so on
Extremely important .. so I repeat one will just vertically (over the columns) numbering the records by vertically placing the record numbers as columns names (also spanning across pages)
by sticking the following cell headers

                'NrCrt 1 2 3' (1st page)

                   'NrCrt 4 5 6' (2nd page)

                   'NrCrt 7 8 9' (3rd page)

                     'NrCrt 10 ' (4th page) 

so there will only be roughly 12 rows on all pages
Unfortunatelly right now
‘NrCrt 1 2 3’ appears over all four pages !
3. Only three data lines on the page (which becomes columns)
4. Every vertical column will have a receipts subgrid data ALSO as columns..
This should be displayed as some sort of 1 to n relationship
As one could notice from fig.2.1, there should be added a plus sign between square brackets on a ‘Cashed In’ line for every single column indicating that this record has a subsequent one or more receipts sub-records associated (at least 1) with it. And also when hovering mouse over the +/- sign the mouse simbol will turn into that little palm symbol
And if one will click over that plus sign (+) this line (column actually) will expand, showing all the corresponding receipts data detail. And when expanded, that (+) plus sign turns over the minus sign (-) which when clicked will collapse that entire vertical receipts subgrid data, reverting back on the initial display.
All the corresponding receipts are regarded as a sub-record(s) for that principal transaction record as depicted through the Fig2.2
enter image description here
enter image description here
enter image description here

Please you guys also check the pics where everything appears exactly how it is right now and also how things should stand based on all the stuff mentioned
Thank you all, and please assist me through this matter

How to delete a nested object inside array of objects

I am trying to delete an object property. I used splice method, also delete method, but I receive “TypeError: Cannot delete property.”. I have a comments array, whose each comment object has replies array of objects, and I am trying to delete one of the object (reply object whose id is 1) inside replies array. And then replacing this comment object with its old version inside commments array. I tried:

comments[localId].replies.splice(1, 1)  // cannot delete property
comments[localId].replies = comments[localId].replies.filter((reply) => reply.id !== 1)  // nothing changing unexpectedly

How can I handle this issue?

Using jQuery Datepicker and dateFormat, altFormat and altField with $(this)

Using jQuery Datepicker, I can’t get dateFormat, altFormat and altField to work correctly and output variables formatted as altFormat and using jQuery $(this)?

In the example below, the text input to the datepicker shows i.e., 20241218, while the console.logs shows i.e., Date Thu Dec 26 2024 00:00:00 GMT-0700. I’m trying to get the varibles startDate and endDate updated each time a date is selected by the user so that the variables in the format yymmdd can be used in a URL query string.

I realize that datepicker("option", "minDate", startDate) in the second and third function blocks seems to overwrite the options in the first function .datepicker({dateFormat: "MM dd, yy", altFormat: "yymmdd", altField: "#start_date_picker"}). But what I’ve tried throws Javascript errors.

The difference seems to have something to do with using $(this)this when generating the variables startDate and endDate in the second and third function blocks.

How can I use dateFormat, altFormat and altField so that they work? And output startDate and endDate in the format yymmdd?

https://jsfiddle.net/b302qwhd/

$(function() { 
            $("#start_date_picker")
            .datepicker({dateFormat: "MM dd, yy",
            altFormat: "yymmdd", altField: "#start_date_picker"});
            $("#end_date_picker")
            .datepicker({dateFormat: "MM dd, yy",
            altFormat: "yymmdd", altField: "#end_date_picker"});
            }); 

        $('#start_date_picker').on("change",function(){ 
            startDate = $(this).
            datepicker('getDate'); 
            $("#end_date_picker").
            datepicker("option", "minDate", startDate); 
            console.log(startDate);
        }) 

        $('#end_date_picker').on("change",function(){ 
            endDate = $(this).
            datepicker('getDate'); 
            $("#start_date_picker").
            datepicker("option", "maxDate", endDate); 
            console.log(endDate);

        });

sanitize-html converts special character such as ‘&’ to ‘&amp’

How do I handle this type of situation? I have a nodejs application with nextjs frontend. Users want to use character such as ‘&’. However I realized that after sanitizing it with this package sanitizeHtml before storing in my database. I realized that it changes character such as “&” to ‘&amp’.
This is for user input. Characters coming from rich text editor are rendered well.
I wanted to prevent users from ever using “&” but I realized that they can simply copy and paste the content.
So, how do I handle this?
Here is my code:

const sanitizeText = (text)=>{
 const clean = sanitizeHtml(text, {
  allowedTags: [],
  allowedAttributes: {},
  allowedIframeHostnames: [],
  allowedSchemes: [],
});

return clean;
 }

is there a way I can make the character ‘&’ to be saved as ‘&’ in my database instead of the ‘&amp’? Or is there an efficient way to completely prevent users from using such character

JavaScript – reduce the valuses of a specific object field from an array of objects to a single string [closed]

I have an array of insurance policy objects. The policy object has several fields, one of which is policyNumber. I am trying to create a string of the policy numbers using JavaScript.

My code thus far is as follows:

const policies = [
    {
        name: "betty",
        policyNumber: "12345",
        policyKindCode: "ABC"
    },
    {
        name: "debbie",
        policyNumber: "67890",
        policyKindCode: "DEF"
    },
    {
        name: "judy",
        policyNumber: "13579",
        policyKindCode: "GHI"
    },
    {
        name: "rudy",
        policyNumber: "24680",
        policyKindCode: "JKL"
    }
  ] 

  const policiesString = policies.reduce((str, policy) => {
    str += (policy.policyNumber + ", ");
    return str;
  });

  console.log('policiesString = ' + policiesString);

The console.log is returning the following:

"policiesString = [object Object]67890, 13579, 24680, "

If I comment out the first object it just puts the [object Object] where 67890 would go and then has the last two policy numbers, and so on.

What am I doing wrong?

Unable to initialize JavaScript cache storage

I am getting the following warning when building an angular project. Below is the configuration.

Angular CLI: 18.2.12
Node: 20.17.0
Package Manager: npm 10.8.2
OS: win32 arm64

Angular: 18.2.13
... animations, common, compiler, compiler-cli, core, forms
... platform-browser, platform-browser-dynamic, router

Package                         Version
---------------------------------------------------------
@angular-devkit/architect       0.1802.12
@angular-devkit/build-angular   18.2.12
@angular-devkit/core            18.2.12
@angular-devkit/schematics      18.2.12
@angular/cli                    18.2.12
@schematics/angular             18.2.12
rxjs                            7.8.1
typescript                      5.5.4
zone.js                         0.14.10

Here is the warning

▲ [WARNING] Unable to initialize JavaScript cache storage. [plugin angular-compiler]

  No native build was found for platform=win32 arch=arm64 runtime=node abi=115 uv=1 armv=8 libc=glibc node=20.17.0
  This will not affect the build output content but may result in slower builds.

If I ignore this warning and run the application in browser, I see a strange behavior.
When i go back and forth between 2 routes (component A & component B), I see that the css files are download from the server every time I visit the route, as a result for a split second (until the css is downloaded from the server) I see that loaded html is distorted due to missing css. Not sure if this behavior is related to the warning.

NextJS / Vercel – Import failing on deployment, but not locally

This one is doing my head in. My local build works fine, but when I push to Vercel the deployment fails and I get this error “Module not found: Can’t resolve ‘@/scripts/fadeInElements'”.

This is what I’m using to input the script

import fadeInElements from '@/scripts/fadeInElements'

And this is the script at this stage

export default function fadeInElements() {
  let pElements = document.querySelectorAll('p')
  console.log(pElements)
}

How to avoid form resubmission on page refresh after failed request?

I have a nodejs server with server side rendering of relatively simple html pages with handlebars.

Let’s say there are two endpoints:

GET /task/new -> open the page with a form for creating a new task
POST /task -> create a new task and redirect to /task/<new task id>
GET /task/:id -> show task details

Tasks have only title with minimal length of 3 symbols:

<form method="post" action="/task">
    <label for="title">Title:</label>
    <input name="title" value="{{titleFromFailedRequest}}"/>
    {{#if validationErrors.title}}
      <span class="error">{{validationErrors.title}}</span>
    {{/if}}
</form>

In this way, if a user opens the page for the first time, the form will be empty.
After the form is submitted there are two scenarios:

  1. If request succeeds, the server will return a redirect to the page with a new task.
  2. If request fails (for example because of too short title), the server will return the same form, with prefilled invalid title and an error message.

The problem is that if I refresh the page after a failed request, the form will be resubmitted. This is not what I want.

I’ve found information about POST/REDIRECT/GET pattern and I already use it for successful cases. However, I can’t find any best practices for failed requests..

JSON file is not showing in Vite JS build

I am creating my dist folder with the command ‘npm run build’ but Vite JS is not compiling a JSON file that I need to retrieve the data from. Why is Vite not passing the JSON file in the dist folder?. I am working in a repo that was shared to me. This is the folder: “LuisDeAnda/src/assets/…” I moved the JSON file to public folder since I read it is better for compiling it. I am assuming the public folder is ‘LuisDeAnda’. But still I cant retrieve the data after npm run build and previewing it. By the way, my app only works fine in my local environment when I launch ‘npm run dev’.

This is my main.js file to:

import "./main.scss";
import "./assets/fonts/Marcheile-Bold-Condensed.woff";
import "./assets/fonts/Marcheile-Bold-Condensed.woff2";
/* DO NOT EDIT ABOVE THIS LINE. You can start editing here. */



let iconCart = document.querySelector(".icon-cart");
let body = document.getElementById("app");
let closeCart = document.querySelector(".close");
let listProductHTML = document.querySelector(".listProduct");
let listCartHTML = document.querySelector(".listCart");
let iconCartSpan = document.querySelector(".icon-cart span");

let listProducts = [];
let carts = [];
let totalBox = document.getElementById("total");

const menuContainer = document.getElementById("menu-container");

// Aqui es para abrir y cerrar el carrito de compras
iconCart.addEventListener("click", () => {
  body.classList.toggle("showCart");
});
closeCart.addEventListener("click", () => {
  body.classList.remove("showCart");
});
// Aqui se llama la Data del archivo menu json
const initApp = () => {
  fetch("/menu.json")
    .then((response) => {
      if (!response.ok) {
        throw new Error(`HTTP error! Status: ${response.status}`);
      }
      return response.json();
    })
    .then((data) => {
      // listProducts = data;
      listProducts = data.menuList;
      console.log(listProducts);
      addDataToHTML();
    })
    .catch((error) => {
      console.error("Error:", error);
    });
};

// Aqui se renderiza la Data en el navegador
const addDataToHTML = () => {
  menuContainer.innerHTML = "";
  // listProducts = menuData.menuList;
  if (listProducts.length > 0) {
    listProducts.forEach((item) => {
      let newMenuItem = document.createElement("div");
      newMenuItem.classList.add("mybox");
      newMenuItem.dataset.id = item.id;
      newMenuItem.innerHTML = `
          <img src="${item.menuImage}" alt="food" />
          <h3>${item.menuName}</h3>
          <p>${item.menuDescription}</p>
          <p class="price">$${item.menuPrice.toFixed(2)}</p>
          <button class="addCart">Add to cart</button>
      `;
      menuContainer.appendChild(newMenuItem);
    });
  }
};

initApp();

this is the menu.json:

{
  "developerHints": {
    "forbidden": [
      "Do not edit this "developerHints" property",
      "Do not move this json file from this location"
    ],
    "allowed": [
      "You can edit "menuList" property as you like",
      "It is not necessary, but you can add more properties if you need it"
    ]
  },
  "menuList": [
    {
      "id": 0,
      "menuImage": "src/assets/images/gastro/gasto-item-chicken-buger.webp",
      "menuName": "Chicken Burger",
      "menuDescription": "We placed 70 years of delicious into this sandwich. Taste our legendary hand-battered chicken.",
      "menuPrice": 10.0
    },
    {
      "id": 1,
      "menuImage": "src/assets/images/gastro/gasto-item-chicken-wings.webp",
      "menuName": "Chicken wings",
      "menuDescription": "Get your fill with the Texas-Sized Meal: 3PC Legs & Thighs, 2 regular sides plus a signature jalapeño pepper.",
      "menuPrice": 9.0
    },
    {
      "id": 2,
      "menuImage": "src/assets/images/gastro/gasto-item-beer.webp",
      "menuName": "Beer",
      "menuDescription": "Just simple and fresh beer.",
      "menuPrice": 2.2
    },
    {
      "id": 3,
      "menuImage": "src/assets/images/gastro/gasto-item-french-fries.webp",
      "menuName": "French fries",
      "menuDescription": "Our fries are extra-long and center-cut from Grade A potatoes. They are cooked to a golden-brown crisp while remaining slightly soft in the middle. Every batch is cooked to order in our premium quality Canola Oil blend, then lightly salted immediately after leaving the fryer, so the taste melts onto each fry.",
      "menuPrice": 12.0
    },
    {
      "id": 4,
      "menuImage": "src/assets/images/gastro/gasto-item-milkshake.webp",
      "menuName": "Milshake",
      "menuDescription": "Our creamy Milkshakes are hand-spun the old-fashioned way each time and feature delicious. Topped with whipped cream and a cherry (except when served via delivery).",
      "menuPrice": 1.8
    },
    {
      "id": 5,
      "menuImage": "src/assets/images/gastro/gasto-item-fried-chicken.webp",
      "menuName": "Fried chicken",
      "menuDescription": "We use premium chicken breast tenderloins to make the most tender chicken fingers possible. Our special marinade tenderizes the chicken, locks in moisture and adds flavor.",
      "menuPrice": 8.0
    }
  ]
}


vuejs unexpected content behavior when changing array of child components with slots

I want to make a custom carousel which displays multiple slides at once and moves by one slide on a button press. In the main view this is how it is defined:

<MultiItemCarouselComponent>
  <CarouselItemComponent title="i am the first child">
    I am some text content of the first slide
  </CarouselItemComponent>
  <CarouselItemComponent  title="i am the second child">
    I am some text content of the second slide
  </CarouselItemComponent>
  <!-- arbitrary number of additional items. This can also be other components or standard html elements -->
</MultiItemCarouselComponent>

The important part of the MultiItemCarouselComponent is as follows:

  <div class="container-fluid px-0 multi-item-carousel">
    <div class="d-inline-flex multi-item-carousel-inner" ref="multiItemCarouselContainer">
      <div class="me-3" v-for="(child, index) in childComponents" :key="index">
        <component :is="child" />
      </div>
    </div>
  </div>

With a button a trigger a function called transitionNext() in my script section. This will copy the first item of my child components to the end of the array and handle the animations:

<script setup>
import { useSlots, ref } from 'vue'

var slots = useSlots()
const childComponents = ref(null)
childComponents.value = slots.default()

const multiItemCarouselContainer = ref(null)
const translateAmount = 100 / (childComponents.value.length + 1)

function transitionNextFinished() {
  childComponents.value.shift()
  multiItemCarouselContainer.value.id = null
  multiItemCarouselContainer.value.style.transition = 'none'
  multiItemCarouselContainer.value.style.transform = 'none'
  multiItemCarouselContainer.value.removeEventListener('transitionend', transitionNextFinished)
}

function transitionNext() {
  var firstChildElement = childComponents.value[0]
  childComponents.value.push(firstChildElement)
  multiItemCarouselContainer.value.id = 'transitionNextElement'
  multiItemCarouselContainer.value.style.transition = 'transform .6s ease-in-out'
  multiItemCarouselContainer.value.style.transform = 'translateX(-' + translateAmount + '%)'
  multiItemCarouselContainer.value.addEventListener('transitionend', transitionNextFinished)
}
</script>

All of this works as expected…apart from the slot content of the CarouselItemComponent. After the transition is finished, these seem to be updated to their original order. This means after the transition the pages shows:

______________________  _______________________
|i am the last child |  |i am the first child |
|____________________|  |_____________________|
|I am some text      |  |I am some text       | ...
|content of the first|  |content of the second| 
| slide              |  | slide               |
______________________  _______________________

I am assuming this is due to some render update vue does under the hood and my way of copying and moving the last array item to the 0th position is problematic, but I am unsure how to solve this problem. The only solution a can come up with at the moment is to pass the content of the child components as parameter as well (just like the title), but I would like to keep the flexibility slots provide.

Simulate a browser inside a javascript application

So I have an input where the user can type any url, and I want to render that webpage in my own javascript application. (render a website inside other website, basically)

An Iframe with a src is what I want to achieve, the problem is that some websites won’t let you do that due to X-Frame-Options = ‘deny’ | ‘sameorigin’

So I can fetch manually the url, receiving the HTML code, but the next problem is that the css won’t appear, because the requests for the css comes with my localhost domain.

Example:

  1. fetch(www.tailwindcss.com) 200 OK
  2. inside tailwindcss application there are script files which starts to download, one of them being the css, but the request comes out like localhost:8080/file.css instead www.tailwindcss.com/file.css

Is this even possible? Or am I stuck with Iframe?
Thanks

Qualtrics Javascript behaving unpredictably

TLDR Main issue: The same JS code has been replicated over multiple blocks of qualtrics to time out each block within 35 minutes. But, in one of of 10 implementions, one of the blocks is randomly getting timed out too soon. And it is never happening in the first block.

Details: I am using Qualtrics blocks that should time out in 35 minutes, I have an embedded variable called “limit” that is set at 35 minutes (2100000ms).
Each block has multiple loops (using a feature called loop-and-merge). The first question of the first loop of every block defines the start-time for the block:

Part 1:

    

    
    let loop_count = parseInt("${lm://CurrentLoopNumber}");

    if (loop_count === 1) {
        var starttime = new Date().getTime();
        Qualtrics.SurveyEngine.setEmbeddedData('start', Number(starttime)); 

        Qualtrics.SurveyEngine.setEmbeddedData('time_so_far', Number(0)); 

Part 2: Next, the main questions of each block contain the following JS code to keep track of the time spent so far, and times out the page when the limit is reached. I use a display condition to ensure that none of the following pages within that block is shown any longer.


    var endtime = new Date().getTime();
    var endtime = Number(endtime);
    var starttime = Number(Qualtrics.SurveyEngine.getEmbeddedData('start')); 
    var time_so_far = (endtime - starttime); 
    //alert("time_so_far = " + time_so_far);
    var limit = Number(Qualtrics.SurveyEngine.getEmbeddedData('limit')); 
    
    Qualtrics.SurveyEngine.setEmbeddedData("time_so_far", time_so_far); 
     
    let loop_count = parseInt("${lm://CurrentLoopNumber}");
    Qualtrics.SurveyEngine.setEmbeddedData("loop_count", loop_count); 
    
    //The setInterval() method calls a function at specified intervals (in milliseconds). The setInterval()         method continues calling the function until clearInterval() is called, or the window is closed.
    
    setTimeout(function() {
    let time_so_far = limit+1;
    Qualtrics.SurveyEngine.setEmbeddedData("time_so_far", time_so_far);     
    jQuery("#NextButton").click();
    }, limit + 1000 - time_so_far);

Any help would be hugely appreciated. (I tried posting on Qualtrics forum, but somehow I do not have the rights to make a new post!)

Hide text if it overflow

Is there any efficient way to show/hide text in an element based on its width?
For example, there is an element with a dynamic width that can change and the text displayed in it. I want the text to be displayed only when the width of the element is wide enough to show it completely, and to hide when it overflowing the element (no line breaks, only one line)

I tried to write a react component that would display such text, but it works for me only for a static width, when changing, the text does not disappear/appear. Also, I don’t like the way it’s done here:

const AdaptiveText: React.FC<Props> = ({ text }) => {
    const textRef = useRef<HTMLDivElement>(null);
    const [isOverflowing, setIsOverflowing] = useState(false);

    useEffect(() => {
        const checkOverflow = () => {
            if (textRef.current) {

                setIsOverflowing(textRef.current.scrollWidth > textRef.current.clientWidth);
            }
        };
        checkOverflow();
        window.addEventListener("resize", checkOverflow);
        return () => {
            window.removeEventListener("resize", checkOverflow);
        };
    }, [text]);

    return (
        <div
            style={{
                width: "100%",
                whiteSpace: "nowrap",
                overflow: "hidden",
                textOverflow: "ellipsis",
                display: isOverflowing ? "none" : "block",
                position: "relative",
            }}
            ref={textRef}
            title={isOverflowing ? text : ""}
        >
            {text}
        </div>
    );
};