Render a razor component in a new browser tab and pass a list of objects to it

I’m trying to create a control that opens a razor component and displays the objects sent to the page after a user clicks a Print button. Using the IJSRuntime.InvokeAsync() the blank page opens to a blank component page but the objects are not being captured.
Here is the button onclick event-

    public async Task PrintViewableOrderPackingSlips()
    {
        OrdersToPrint = OrdersToPack.Where(o => o.IsOrderVisible).ToList();        

        await IJS.InvokeAsync<object[]>("open", "/PrintPackingSlip", "_blank", new object[]{ OrdersToPrint });   


    }

That invokes this razor component page

@layout Blazing_Fruit.Pages.BlankLayout

@page "/PrintPackingSlip"


<body onload="window.print()">

    <Div>print page</Div>  
          

</body>


@code{


    [Parameter]
    public JsonContent orders { get; set; }

    [Parameter]
    public List<UnshippedOrder> value { get; set; }

    
}

Obviously I’m not capturing the object passed over because I get this error in the originating page

Uncaught (in promise) TypeError: cyclic object value

and this error in the new component window

Uncaught (in promise) Error: Found malformed component comment at Blazor:{“sequence”:0,”type”:”server”,”prerenderId”:”aa8c4268d131416eb85784e2fcf8549f”,”descriptor”:

How can I pass my list of objects to the new component?

Skip starting blank rows in Excel with XLSX in JavaScript Sheet-JS

I have the following code that works great when the header row is row 1

  readerData(rawFile) {         
      return new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.onload = e => {
          const data = e.target.result;
          const workbook = XLSX.read(data, { type: "array" });
          const firstSheetName = workbook.SheetNames[0];
          const worksheet = workbook.Sheets[firstSheetName];
          const header = this.getHeaderRow(worksheet);
          const results = XLSX.utils.sheet_to_json(worksheet,{ header: 0, range: 0, defval: ""});
          this.generateData({ header, results });
          this.loading = false;
          resolve();
        };
        reader.readAsArrayBuffer(rawFile);
      });
    },
    generateData({ header, results }) {
      this.excelData.header = header;
      this.excelData.results = results;
      this.excelData.original_results = [...results];
      this.onSuccess && this.onSuccess(this.excelData);

         var grid = this.$refs.membersGrid.ej2Instances;                         
        grid.dataSource = this.excelData.results;
          grid.refresh(); 
    },
      getHeaderRow(sheet) {
      const headers = [];
      const range = XLSX.utils.decode_range(sheet["!ref"]);
      let C;
      const R = range.s.r;
      /* start in the first row */
      for (C = range.s.c; C <= range.e.c; ++C) {
        /* walk every column in the range */
        const cell = sheet[XLSX.utils.encode_cell({ c: C, r: R })];
        /* find the cell in the first row */
        let hdr = "UNKNOWN " + C; // <-- replace with your desired default
        if (cell && cell.t) hdr = XLSX.utils.format_cell(cell);
        headers.push(hdr);
      }
      return headers;
    },

It works great and put all of the Header values into the excelData.header and it put all of the named array data into the excelData.results. My problem is it all goes to a mess when the first row or first two rows are blank or I need to skip them. I’ve tried

  https://github.com/SheetJS/sheetjs/issues/463

but I’m using “xlsx”: “^0.17.1” . When I used

    range.s.r = 1;

I was able to change my range to A2 but I could not get my named array of data. Any help is appreciated .

I’m trying to access to my get method with the route api/products and it throws me this error, what could be the cause?

THIS IS THE ERROR

[Error: ENOENT: no such file or directory, open ‘C:UsersUsuarioDesktopproductos.txt’] {
errno: -4058,
code: ‘ENOENT’,
syscall: ‘open’,
path: ‘C:UsersUsuarioDesktopproductos.txt’
}

[on screen it only shows me an empty obj or nothing directly]

[INDEX.JS]

const express = require("express");
const app = express();
const http = require("http").Server(app);
const fs = require("fs").promises;
const path = require("path")
const routes = require("./routes");
const io = require("socket.io")(http);
const { customAlphabet } = require("nanoid");
const nanoid = customAlphabet("1234567890", 5);

app.set("view engine", "ejs");
app.use(express.static(path.join(__dirname, "public")));
app.set("views", "./views");
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

app.get("/", (req, res) => {
  fs.readFile("productos.txt", "utf-8");
  res.render("form");
});

app.post("/productos", async (req, res) => {
  fs.readFile("productos.txt", "utf-8")
    .then((data) => {
      const type = JSON.parse(data);
      const newProduct = {
        id: nanoid(),
        ...req.body,
      };
      type.push(newProduct);
      fs.writeFile("productos.txt", JSON.stringify(type));
      res.render("productos", {
        type,
      });
    })
    .catch((err) => {
      if (!req.body.title && !req.body.price && !req.body.thumbnail) {
        res.status(400).json({
          error: "Bad Request",
          message: "Title, price and thumbnail are required",
        });
      }
      console.log(err);
    });
});

app.get("/productos", (req, res) => {
  fs.readFile("productos.txt", "utf-8").then((data) => {
    res.render("productos", {
      type: JSON.parse(data),
    });
  });
});

routes(app);

const port = 8080;
app
  .listen(port, () => {
    console.log(`App listening on port ${port}!`);
  })
  .on("error", (err) => {
    console.log(err);
    throw err;
  });

[API.JS]

const fs = require("fs").promises;
const { customAlphabet } = require("nanoid");
const nanoid = customAlphabet("1234567890", 5);

class Productos {
  constructor(txtNameFile) {
    this.txtNameFile = txtNameFile;
    this.productos = [];
  }

  async fileInJSON() {
    let fileTxt = await fs.readFile(this.txtNameFile, "utf-8");
    let type = JSON.parse(fileTxt);
    return type;
  }

  async fileSaving(item) {
    let type = JSON.stringify(item);
    await fs.writeFile(this.txtNameFile, type);
  }

  async obtenerProductos() {
    try {
      const productos = await this.fileInJSON();
      return productos;
    } catch (error) {
      console.log(error);
    }
  }

  async obtenerProducto(id) {
    try {
      const productos = await this.fileInJSON();
      const producto = productos.find((producto) => producto.id === id);
      return producto;
    } catch (error) {
      console.log(error);
    }
  }

  async create(data) {
    try {
      // create and adding product using fs module system
      const newProduct = {
        id: nanoid(),
        ...data,
      };
      this.productos.push(newProduct);
      await this.fileSaving(this.productos);
      return newProduct;
    } catch (error) {
      console.log(error);
    }
  }

  async eliminarProducto(id) {
    try {
      const productos = await this.fileInJSON();
      const producto = productos.find((producto) => producto.id === id);
      const index = productos.indexOf(producto);
      productos.splice(index, 1);
      await this.fileSaving(productos);
    } catch (error) {
      console.log(error);
    }
  }

  async actualizarProducto(id, data) {
    try {
      const productos = await this.fileInJSON();
      const producto = productos.find((producto) => producto.id === id);
      const index = productos.indexOf(producto);
      productos[index] = {
        ...producto,
        ...data,
      };
      await this.fileSaving(productos);
      return productos[index];
    } catch (error) {
      console.log(error);
    }
  }
}

module.exports = Productos;

[ROUTES/PRODUCTOSROUTER.JS]

const express = require("express");
const Productos = require("../api");
const router = express.Router();
const itemService = new Productos("../productos.txt");

router.get("/", async (req, res) => {
  res.json(await itemService.obtenerProductos());
});

router.post("/", async (req, res) => {
  if (!req.body.title && !req.body.price && !req.body.thumbnail) {
    res.status(400).json({
      error: "Bad Request",
      message: "Title, price and thumbnail are required",
    });
  }
  const producto = itemService.create(req.body);
  res.json(await producto);
});

router.get("/:id", async (req, res) => {
  const producto = itemService.obtenerProducto(req.params.id);
  if (producto) {
    res.json(await producto);
  } else {
    res.status(404).json({ error: "Producto no encontrado" });
  }
});

router.put("/:id", async (req, res) => {
  const id = req.params.id;
  const producto = itemService.actualizarProducto(id, req.body);
  res.json(await producto);
});

router.delete("/:id", async (req, res) => {
  const id = req.params.id;
  itemService.eliminarProducto(id);
  res.json({ message: "Producto eliminado" });
});

module.exports = router;

[ROUTES/INDEX.JS]

const productosRouter = require("./productosRouter");

function routes(app) {
  app.use("/api/productos", productosRouter);
}

module.exports = routes;

How can I use querySelectorAll with htmx

I am working with htmx which is a fantastic library, I have a little problem that’s not very clear on how to solve. htmx uses queryselector to locate elements to swap/update example with hx-swap="...", hx-target="..." how do I do a querySelectorAll. example I have a class bookmark, when a user bookmarks a post, I want to update all the classes with .bookmark

How to make a simple count down timer from milliseconds with Javascript/REACT

I’m wondering the best way to make a simple minute/second countdown timer to next refresh. This component’s prop get updated every 5 minutes and counts down until the next refresh per second. This seems to get out of sync pretty quickly. Any thoughts?

I’m not sure if timeRemaining < 0 makes sense — if we subtract 1000 from 1100 we would have 100 (how do I account for that?) I’m hoping there’s a better way. Should I convert this back to seconds?

Thanks for any feedback.

...

const CountdownToNextRefresh = ({milliToNextRounded5Min}) => {
  const [, setCount] = useState(0);
  const [timeRemaining, setTimeRemaining] = useState(milliToNextRounded5Min);

  useEffect(() => {
    const oneSecond = 1000;
    const interval = setInterval(() => {
      setCount((prevCount) => prevCount + 1);
      setTimeRemaining(timeRemaining < 0 ? milliToNextRounded5Min : timeRemaining - oneSecond);
    }, oneSecond);
    return () => clearInterval(interval);
  }, [timeRemaining, milliToNextRounded5Min]);

  function msToTime(milliseconds) {
    // Get hours from milliseconds.
    const hoursFromMilli = milliseconds / (1000*60*60);
    const absoluteHours = Math.floor(hoursFromMilli);
    const h = absoluteHours > 9 ? absoluteHours : `0${absoluteHours}`;

    // Get remainder from hours and convert to minutes.
    const minutesfromHours = (hoursFromMilli - absoluteHours) * 60;
    const absoluteMinutes = Math.floor(minutesfromHours);
    const m = absoluteMinutes > 9 ? absoluteMinutes : `0${absoluteMinutes}`;

    // Get remainder from minutes and convert to seconds.
    const seconds = (minutesfromHours - absoluteMinutes) * 60;
    const absoluteSeconds = Math.floor(seconds);
    const s = absoluteSeconds > 9 ? absoluteSeconds : `0${absoluteSeconds}`;

    return h === "00" ? `${m}:${s}` : `${h}:${m}:${s}`;
  }

return (<div>{msToTime(timeRemaining)}</div>)
}

How does YouTube’s dynamic loading on the homepage work?

I’m making an extension that will put a button under each video on the home page but I found out very quickly that this wouldn’t be possible by just using a selectAll statement. The only thing that a selectAll statement does is it retrieves the first 3 rows of videos on the homepage. I think what’s happening is there are videos being loaded after the page has loaded thus the elements don’t exist at “document_end”(this being when my chrome extension is injecting the js/css onto the page). I would be looking for something like an event listener but an explanation as to why this is happening would be appreciated as well.

People.People.searchDirectoryPeople() query with multiple email addresses

In a Google Apps Script I need to query the Google user profile picture URL of many coworkers.

Here is a working example for a single user:

searchDirectoryPeople('[email protected]');

function searchDirectoryPeople(query) {
  const options = {
    query: query,
    readMask: 'photos,emailAddresses',
    sources: ['DIRECTORY_SOURCE_TYPE_DOMAIN_PROFILE']
  }
  const people = People.People.searchDirectoryPeople(options);
  if(people && people.people) {
    Logger.log('size: '+people.people.length);
    people.people.forEach(person => {
      let url = '';
      let email = '';
      if(person) {
        if(person.photos && person.photos[0]) {
          url = person.photos[0].url;
        }
        if(person.emailAddresses && person.emailAddresses.length) {
          person.emailAddresses.forEach(item => {
            if(item.metadata && item.metadata.sourcePrimary) {
              email = item.value;
            }
          });
        }
      }
      Logger.log('email: '+email+': '+url);
      //Logger.log('person: %s', JSON.stringify(person, null, 2));
    });
  } else {
    Logger.log('no people.people');
  }
}

I found out that I can query all jimmy people:

searchDirectoryPeople('jimmy');

I have the email address of all employees. I could loop through a big list of 1000+ employees one by one, but this is not practical. I am looking for a way to query multiple email addresses. The docs at https://developers.google.com/people/api/rest/v1/people/searchDirectoryPeople are cryptic for the query. I tried many things like these but nothing works:

I am looking for a query by list of email addresses as input, such as:

[ '[email protected]', '[email protected]', '[email protected]' ]

Possible?

What would be the easiest way to do a list of skills on a page that open in a popup?

I’m trying to list a bunch of skills on my website and have each of them open in a pop-up on click to give a little more info.
Basically, I want it to look like this, not as a button.
Image for reference

I’ve tried some codes from the internet but they either don’t work or break my WordPress theme.

The closest I’ve gotten was with this piece of code:

“Example code

How to throw unwanted data from form?

I am making a site to add product to a database.
The site has a type switcher for multiple types of the product.
by changing the type the input fields are changed by using display attribute CSS.
so there is a problem where the invisible inputs are still in the form and they are also submitted with the form. Also i wanted to validate the data but it have the same problem how do i validate the data shown to the user?.
i tried to change the whole by js document.create methods so i can create the inputs and labels i want but it was really complex . is there anyway i can fix this problem?

Here is the code im using:

            function TypeListener() {
              var x = document.getElementById("productType").value;
                document.getElementById("Hidden_Div1").style.display = x == 1 ? 'block' : 'none';
                document.getElementById("Hidden_Div2").style.display = x == 2 ? 'block' : 'none';
                document.getElementById("Hidden_Div3").style.display = x == 3 ? 'block' : 'none';
            }
.Type {
display: none;
}
<form id="product_form" action="<?=$_SERVER['PHP_SELF'] ?>" method="post">
<label for="Type Switcher">Type Switcher</label><select name="typeSwitcher" id="productType" onchange="TypeListener()">
                                                    <option value="" disabled selected>Type Switcher</option>
                                                    <option value="1" id="DVD">DVD</option>
                                                    <option value="2" id="Furniture">Furniture</option>
                                                    <option value="3" id="Book">Book</option>
                                                </select><br><br>
        <div id="Hidden_Div1" class="Type">
            <label for="Size">Size (MB)</label><input type="number" name="size" id="size" min="0"><br>
            <h5>Please, provide size in MB.</h5><br>
        </div>
        <div id="Hidden_Div2" class="Type">
            <label for="Height">Height (CM)</label><input type="number" name="height" id="height" min="0"><br>
            <label for="Width">Width (CM)</label><input type="number" name="width" id="width" min="0"><br>
            <label for="Length">Length (CM)</label><input type="number" name="length" id="length" min="0"><br>
            <h5>Please, provide dimensions in CM.</h5><br>
        </div>
        <div id="Hidden_Div3" class="Type"> 
            <label for="Weight">Weight (KG)</label><input type="number" name="weight" id="weight" min="0"><br>
            <h5>Please, provide weight in KG.</h5><br>
        </div>
</form>

FilePond | onprocessfiles callback is not working if I uploaded files both the file type is supported and is not supported

I tried to duplicate this issue but I couldn’t since it needs to set some server side codes and couldn’t do it on codesandbox.

But you can at least see working code on code sandbox.

And this is a record gif for my problem.

So, when I upload multiple files and if one of the file types is not supported, user won’t be able to click the save button, but if user removes the file that is not supported and has only files that are supported, user should be able to click the save button.

But the issue I have is user can’t click the save button even though he has only files that are supported.

I disabled and abled the save button based with imagesUploading value that changes value depending on the file uploading status like start uploading, uploading right now or uploaded successfully.

function App() {
  const [files, setFiles] = useState([]);
  const [imagesUploading, setImagesUploading] = useState(false);

  return (
    <div className="App">
      <FilePond
        files={files.map((file) => file.file)}
        maxFiles={50}
        maxFileSize="10MB"
        acceptedFileTypes={["image/jpg", "image/jpeg", "image/png"]}
        allowMultiple={true}
        onupdatefiles={(fileItems) => {
          setFiles(fileItems);
        }}
        onaddfilestart={() => {
          console.log("onaddfilestart");
          setImagesUploading(true); // make the save button disabled
        }}
        onprocessfilestart={() => {
          console.log(`onprocessfilestart`);
          setImagesUploading(true); // make the save button disabled
        }}
        onprocessfiles={() => {
          console.log(`onprocessfiles`);
          setImagesUploading(false); // make the save button abled
        }}
        imageResizeTargetWidth={1920}
        allowImageTransform={true}
        imageTransformOutputQuality={70}
      />
      <button disabled={imagesUploading || files.length < 1}>Save</button>
    </div>
  );
}

On the console, I see onaddfilestart and imagesUploading: true twice since there are two files are about to start uploading process.

Also, I see just onprocessfilestart letter and no value for imagesUploading and I assume because one of the file types is not supported, so process failed and the rest of callbacks(onprocessfiles) never got called where set imagesUploading value false to abled the button.

Does anybody have the same issue?

React/Jest: Testing React Context methods (Result: TypeError: setScore is not a function)

I’m using React Context API to create a game.

In one of my components (GameStatus) I pull in some methods from the provider:

const context = React.useContext(MyContext);
const { setGameStart, setGameEnd, setScore } = context;

And in the component I invoke these three methods onClick of the start game button, which in turn sets the state back in the provider.

GameStatus Component

import React from 'react';
import { MyContext } from './Provider';

const GameStatus = ({ gameStart, gameEnd, score, total, getPlayers }: { gameStart: boolean, gameEnd: boolean, getPlayers: () => void, score: number, total: number }) => {
    const context = React.useContext(MyContext);
    const { setGameStart, setGameEnd, setScore } = context;
    return (
        <>
            {!gameStart && (
                <button onClick={() => {
                    getPlayers();
                    setScore(0);
                    setGameStart(true);
                    setGameEnd(false);
                }}>
                    Start game
                </button>
            )}
            {gameEnd && (
                <p>Game end - You scored {score} out {total}</p>
            )}
        </>
    )
}

export default GameStatus;

Then in my test file below I want to test that when the start game button is clicked the game is started (check the DOM has removed the button and is now showing the game).

But I’m not sure how to pull in the methods in to the test file as I get:

Result: TypeError: setScore is not a function

I tried just copying:

const context = React.useContext(MyContext);
const { setGameStart, setGameEnd, setScore } = context;

But then I get an invalid hook call as I can’t use React hooks inside the test.

Any ideas? Or a better approach to testing this? Thanks

GameStatus Test

import React from 'react';
import { shallow } from 'enzyme';
import { MyContext } from '../components/Provider';
import GameStatus from '../components/GameStatus';

test('should start the game', () => {
    const getPlayers = jest.fn();

    const uut = shallow(
        <MyContext.Provider>
            <GameStatus
                getPlayers={getPlayers}
            />
        </MyContext.Provider>
    ).dive().find('button');

    uut.simulate('click');

    console.log(uut.debug());
});

Next Js + Amplify @sls-next/-component error on deployment

I’ve deployed my app on amplify, the backend deployment is all good to go.

I’ve connected the frontend to my gitlab repo, and after debugging it is finally compiled successfully. Immediately after the compiling, I get this error.

Starting SSR Build...
[ERROR]: Error: Command failed: npm install @sls-next/[email protected] --prefix /root/./

I have tried to override the env with the following commands (I’ve tried previous versions of both next and node, however it doesnt pass the compiling phase unless I use the following)

Amplify CLI - latest
Next.js version - latest
Node.js version - 17

This is my amplify.yml

version: 1
frontend:
  phases:
    preBuild:
      commands:
        - npm ci
    build:
      commands:
        - npm run build
  artifacts:
    baseDirectory: .next
    files:
      - '**/*'
  cache:
    paths:
      - node_modules/**/*

My node_module versions on the project are

 "next": "^12.1.7-canary.16",
 "react": "^18.0.0",
 "react-dom": "^18.0.0",
 "aws-amplify": "^4.3.23"

my node version is 17.0.9 and my local amplify cli is 8.2.0

I should note my build passes locally

What am I missing? I dont have serverless installed anywhere on my project, it appears to be something amplify is trying to install. Perhaps I should be exporting after the build? But this is an ssr app, not static. I have a feeling this is a problem with conflicting versions.

,