Generate Document Without Syncing? "Print Instantly"?

I'm familiar with generating PDFs using a "Bot". However I need to print the document very quickly so syncing to trigger the "create a new file" step that renders pdfs and pops 'em in a file store ends up being a little to slow or temporally inconsistent. I don't really want to store them anyway since the data is fairly dynamic - i'd rather render them when needed. I'm wondering what solutions there might be.

One option I was considering was making a webpage that renders the document from information contained in the URI. This way I just have to generate a link and it opens in the browser.

Another way would be sending the data in the JSON body of an outbound webhook / API call, but I think automations don't trigger til sync right?

There must be some better ways I haven't quite understood from my research. Maybe someone can explain it in a way I will understand.

Anyway, what I want to do is generate a label and print it almost instantly after a form is completed. Bonus points if I can select the printer and print details within appsheets or print automatically on form save.

Solved Solved
0 8 452
1 ACCEPTED SOLUTION

Example App 

For those who are interested I finally made that โ€œinstant printโ€ thing...

I made a quick react app (use npx create-react-app or vite).

Uses React Router's "useSearchParams" to grab the URI query parameters and plop their value in the component's fields, generating a label with a QR code.

Once rendered, window.print()  opens the print dialog.

//Test URI: lotlabel?lotId=dHVhjE7f28cJ3NNNB27Dsu&sku=PAINT0016&jobName=GARD%2010574&jobId=23717&release=1of1&kit=23717-R1-K1

import { useSearchParams } from "react-router-dom";
import { useEffect } from "react";
import QRCode from "react-qr-code";

function LotLabel() {
  let [searchParams] = useSearchParams();
  const allParams = {};
  for (const [key, value] of searchParams.entries()) {
    allParams[key] = value;
  }
  const { lotId, sku, jobName, jobId, release, kit } = allParams;

  useEffect(() => {
    window.print();
  }, []);

  return (
    <div className="label">
      <div className="lot-info">
        <p>BMP SKU:</p>
        <h1>{sku}</h1>
        <h3>
          Lot ID: <i>{lotId}</i>
        </h3>
        <QRCode size={512} value={lotId} level={"H"} />
      </div>
      {jobId && (
        <div className="job-info">
          <p>
            Job Name: <b>{jobName}</b>
          </p>
          <p>
            Job ID: <b>{jobId}</b>
          </p>
          <p>
            Release: <b>{release}</b>
          </p>
          <p>
            Kit: <b>{kit}</b>
          </p>
        </div>
      )}
    </div>
  );
}

export default LotLabel;

Over in my AppSheet app, my Item page has a 'dynamic' link action button.

CONCATENATE(
  "https://labelprinter-cra.vercel.app/lotLabel?lotId=",[Lot ID],
  IF(ISNOTBLANK([SKU]),CONCATENATE("&sku=",[SKU]),""),
  IF(ISNOTBLANK([Job Name]),CONCATENATE("&jobName=",[Job Name]),""),
  IF(ISNOTBLANK([Job ID]),CONCATENATE("&jobId=",[Job ID]), ""),
  IF(ISNOTBLANK([Release]),
    CONCATENATE("&release=",[Release].[Release Number],"of",[Job ID].[Total Releases]), 
    ""
  ),
  IF(ISNOTBLANK([Kit ID]),CONCATENATE("&kit=",[Kit ID]),"")
)

With this I can render the document right when I make a change; without syncing!

Keep in mind this is a pretty limited use-case. URIs have a character limit. But you could do some stuff with a database and webhooks if you needed to pull more information (though at that point you might as well make a webapp).

I guess you could do it in raw js/html too with

document.addEventListener('DOMContentLoaded', function() {
  const params = new URLqueryParams(window.location.search);
  const lotID = params.get('lotId')
  populateDocument(lotId)
  window.print();
}

function populateDocument(lotID) {
  document.getElementById('lotId').textContent = `Lot ID: ${lotID}`;
}

or whatever it would be.

 

View solution in original post

8 REPLIES 8

Is the problem that your app takes 30+ seconds to load?

  • And therefor you can't afford to wait for this to happen?

___________________________________________________________________________________

Ultimately I feel like you're going to face the same problem no matter what:

  • In order to get data out of the app, it has to go through the sync process - though the order of operations is that the record edits go first, so things will kick off right away (and you don't necessarily need to wait for the full sync to finish).

 

I'm working on using the "External Go to a Website" action.

The URI contains the relevant data from the row in query parameters.

 

 

 

 

CONCATENATE("www.mySite.com/print?name=",[Name],"&reason=",[Reason])

 

 

 

 


In the browser, the site renders a simple html page with the values from the query params, i.e. 

 

 

<h1>1</h1><p>My Fake Name</p>

 

 

This will be cool. Do you mind sharing further details. I had in my mind to do something like this to generate a PDF from data in a Data URI format using Node.js. This process typically involves converting the Data URI to a buffer and then using a PDF library in Node.js to create the PDF.

 

So, about that PDF generating problem with AppSheet - it's just way too slow, especially when there's a bunch of bots in the mix. It seriously feels like it takes forever to get one measly PDF created. I mean, we're living in a world where AI can whip up videos in seconds, and here we are waiting ages for a simple document. It's a total disaster, especially when you think about how AppSheet could step up their game and fix this issue. Come on, AppSheet team, we're counting on you to tackle these real-life problems!

Example App 

For those who are interested I finally made that โ€œinstant printโ€ thing...

I made a quick react app (use npx create-react-app or vite).

Uses React Router's "useSearchParams" to grab the URI query parameters and plop their value in the component's fields, generating a label with a QR code.

Once rendered, window.print()  opens the print dialog.

//Test URI: lotlabel?lotId=dHVhjE7f28cJ3NNNB27Dsu&sku=PAINT0016&jobName=GARD%2010574&jobId=23717&release=1of1&kit=23717-R1-K1

import { useSearchParams } from "react-router-dom";
import { useEffect } from "react";
import QRCode from "react-qr-code";

function LotLabel() {
  let [searchParams] = useSearchParams();
  const allParams = {};
  for (const [key, value] of searchParams.entries()) {
    allParams[key] = value;
  }
  const { lotId, sku, jobName, jobId, release, kit } = allParams;

  useEffect(() => {
    window.print();
  }, []);

  return (
    <div className="label">
      <div className="lot-info">
        <p>BMP SKU:</p>
        <h1>{sku}</h1>
        <h3>
          Lot ID: <i>{lotId}</i>
        </h3>
        <QRCode size={512} value={lotId} level={"H"} />
      </div>
      {jobId && (
        <div className="job-info">
          <p>
            Job Name: <b>{jobName}</b>
          </p>
          <p>
            Job ID: <b>{jobId}</b>
          </p>
          <p>
            Release: <b>{release}</b>
          </p>
          <p>
            Kit: <b>{kit}</b>
          </p>
        </div>
      )}
    </div>
  );
}

export default LotLabel;

Over in my AppSheet app, my Item page has a 'dynamic' link action button.

CONCATENATE(
  "https://labelprinter-cra.vercel.app/lotLabel?lotId=",[Lot ID],
  IF(ISNOTBLANK([SKU]),CONCATENATE("&sku=",[SKU]),""),
  IF(ISNOTBLANK([Job Name]),CONCATENATE("&jobName=",[Job Name]),""),
  IF(ISNOTBLANK([Job ID]),CONCATENATE("&jobId=",[Job ID]), ""),
  IF(ISNOTBLANK([Release]),
    CONCATENATE("&release=",[Release].[Release Number],"of",[Job ID].[Total Releases]), 
    ""
  ),
  IF(ISNOTBLANK([Kit ID]),CONCATENATE("&kit=",[Kit ID]),"")
)

With this I can render the document right when I make a change; without syncing!

Keep in mind this is a pretty limited use-case. URIs have a character limit. But you could do some stuff with a database and webhooks if you needed to pull more information (though at that point you might as well make a webapp).

I guess you could do it in raw js/html too with

document.addEventListener('DOMContentLoaded', function() {
  const params = new URLqueryParams(window.location.search);
  const lotID = params.get('lotId')
  populateDocument(lotId)
  window.print();
}

function populateDocument(lotID) {
  document.getElementById('lotId').textContent = `Lot ID: ${lotID}`;
}

or whatever it would be.

 

Good one ! Did you manage to achieve using child rows. For example invoice with multiple line items ?

as long as you kept the url under 2000 characters you could do that. alternatively i the child rows were already synced you could send some kind of shorthand reference and grab that part of things from your data-source.

Thanks @generativegeorg. You might want to put this in Tips & Tricks. 

Hi bro, 

I request your kind attention here.

 

1. Could you please share here the printed output ? i mean the pics of the label which you have done .

2. In 2nd para you have mentioned that you have using custom url that brings the necessary content in browser . 

       2.1 does the webpage opens in the app sheet or externally ? Do you have any screenshot for reference ? 

  2.2 Have you done it through HTML ? 

3. You have used some API, web hooks... that everything functions inside the device (mobile, desktop) or it connects the server and get backs with file ?

4. Could you please share the demo app which you have created ? The sample which you have given connects with github . I need see in appsheet app.

 

Please help I having a case that is almost similar to your post. 

Top Labels in this Space