skip to Main Content

There is working code that opens a browse for file dialog in another question. I’d like to make this an await async call.

Here is the original question and it works in my tests.

I’ve added the snipper here for convenience:

// wait for window to load
window.addEventListener("load", windowLoad);

// open a dialog function
function openFileDialog (accept, multy = false, callback) { 
    var inputElement = document.createElement("input");
    inputElement.type = "file";
    inputElement.accept = accept; // Note Edge does not support this attribute
    if (multy) {
        inputElement.multiple = multy;
    }
    if (typeof callback === "function") {
         inputElement.addEventListener("change", callback);
    }
    inputElement.dispatchEvent(new MouseEvent("click")); 
}

// onload event
function windowLoad () {
    // add user click event to userbutton
    userButton.addEventListener("click", openDialogClick);
}

// userButton click event
function openDialogClick () {
    // open file dialog for text files
    openFileDialog(".txt,text/plain", true, fileDialogChanged);
}

// file dialog onchange event handler
function fileDialogChanged (event) {
    [...this.files].forEach(file => {
        var div = document.createElement("div");
        div.className = "fileList common";
        div.textContent = file.name;
        userSelectedFiles.appendChild(div);
    });
}
.common {
    font-family: sans-serif;
    padding: 2px;
    margin : 2px;
    border-radius: 4px;
 }
.fileList {
    background: #229;
    color: white;
}
#userButton {
    background: #999;
    color: #000;
    width: 8em;
    text-align: center;
    cursor: pointer;
}

#userButton:hover {
   background : #4A4;
   color : white;
}
<a id = "userButton" class = "common" title = "Click to open file selection dialog">Open file dialog</a>
<div id = "userSelectedFiles" class = "common"></div>

Here is what I have:

async browseForFile(acceptedTypes: string, multiple?: boolean): Promise<array> {
      var element = document.createElement("input");
      element.type = "file";
      element.accept = acceptedTypes;
      if (multiple) {
         element.multiple = multiple;
      }

      new Promise((resolve, reject) => {
         element.addEventListener("change", resolveCallback);
      })

      var resolveCallback = (event)=> {
         return event.currentTarget.files;
      }
      
      element.dispatchEvent(new MouseEvent("click")); 
   }

Suggested usage:

try {
   var files = await browseForFile("*.txt");
}
catch(error) {
   // no files selected
}

The answer I posted below seems to work if the user selects a file. But if they click cancel the promise is not rejected.

2

Answers


  1. Chosen as BEST ANSWER

    This seems to be working. I haven't tested in all browsers.

    function browseForFile(acceptedTypes, multiple) {
          var element = document.createElement("input");
          element.type = "file";
          element.accept = acceptedTypes;
          if (multiple) {
             element.multiple = multiple;
          }
    
          var filePromise = new Promise((resolve, reject) => {
             
             var resolveCallback = (event)=> {
                element.removeEventListener("change", resolveCallback);
                var input = event.currentTarget
                resolve(input.files);
             }
    
             var cancelCallback = (event)=> {
                element.removeEventListener("cancel", cancelCallback);
                resolve(null);
             }
    
             element.addEventListener("change", resolveCallback);
             element.addEventListener("cancel", cancelCallback);
          });
          
          element.dispatchEvent(new MouseEvent("click"));
    
          return filePromise;
       }
       
       async function openUploadDialog(event) {
          try {
             var files = await browseForFile("*.txt");
             console.log(files);
          }
          catch(error) {
             console.log(error)
          }
       }
       
        var button = document.querySelector("#button");
        button.addEventListener("click", openUploadDialog);
    <button id="button" title = "Click to open file selection dialog">Open file dialog</a>


  2. You need to return the promise so you can await the function.

    And the promise needs to call resolve() with the value that you want returned by await.

    function browseForFile(acceptedTypes, multiple) {
      var element = document.createElement("input");
      element.type = "file";
      element.accept = acceptedTypes;
      if (multiple) {
        element.multiple = multiple;
      }
    
      let p = new Promise((resolve, reject) => {
        element.addEventListener("change", event => resolve(resolveCallback(event)));
      })
    
      var resolveCallback = (event) => {
        return event.currentTarget.files;
      }
    
      element.dispatchEvent(new MouseEvent("click"));
      return p;
    }
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search