Search code examples
javascriptelectronipc

What's the right way to pass some initial data to a new window?


I'm just learning Electron, and I'm trying to open a file and display it in a new window. I click a button in the initial (root) window, which then opens an "open file" dialog, from which I can get a file path. I would like to then open that file, create a window, and pass the contents of the file to the new window. My issue is in getting the string with the file contents into the callback function for when the window is ready; i that even possible? My main.js code:

function createWindow (templateFile, initialData) 
{  console.log("Creating window....")
   newWindow = new BrowserWindow({width: 800,
                                  height: 600,
                                  webPreferences: {preload: path.join(__dirname, 'preload.js'),
                                                   nodeIntegration: true}
                                 })
    newWindow.loadFile(templateFile)
    newWindow.webContents.openDevTools()
//This is what doesn't work; I want to take the initialData argument to the createWindow function, 
//and get it into the 'did-finish-load' callback function
    newWindow.webContents.on('did-finish-load', (event, initialData) => 
    {   windowsArray[newWindow.webContents.id] = newWindow 
        console.log(initialData)
        newWindow.webContents.send("initialDataLoad", initialData)
    })
    newWindow.on('closed', function () {newWindow.object = null})
}

ipcMain.on("new-sheet", (event, gameDefinitionFile) => 
{    console.log("Loading " + gameDefinitionFile)
     let gameDefContents
     fs.readFile(gameDefinitionFile, 'ascii', (err, gameDefContents) => {})
     createWindow("defaultSheet.html", gameDefContents)
})

I have read that you can just make a new attribute on the webContents object and then refer to it from the renderer process, but that does not sound like the actual right thing to do. So, what should I do instead?

full code at https://gitlab.com/sjbrown8/osiris


Solution

  • The webContents way should solve your problem in an easy way. It's straightforward: you create and collect all data the window needs in advance, then you spawn the window with that.

    There a a few constraints with the webContents way, because the webContents object that you create on main is serialized and send to render- meaning, both processes have independent versions of the object:

    1. Changes you apply to you object on the render process version of your data will not be reflected into main (and vice-versa)
    2. You can't pass functions (like using that to call a function from main on the render process)

    But with plain initialization data: it's easy and understandable.

    Edit:

    Sure, the send methods needs a channel name and an optional objekt, that is the data that you want to send. You can write a function like so:

    sendPersonData(personData) => {
       webContents.send('person-data-updated', personData);
    );
    

    Or if you want a function that takes a callback for flexible sending:

    updatePersonData(personData, senderCallback) => {
       updatedPersonData = functionToUpdatePersonData(personData);
       senderCallback(personData);
    }
    
    // and use that like so:
    updatePersonData({name: 'John Doe'}, sendPersonData);