電子メールでテキストエディタを作成する


で、基本的な構造を作成しました.私たちはディレクトリからファイルを読むことができました、サイドバーでタイトルをリストします、そして、我々はスクリーンで彼らの内容を読むことができました.
このチュートリアルでは、相互作用を追加します.メニューから始めましょう.私たち自身のメニューを指定していないので、電子は私たちにデフォルトで1つを与えます./main.js 我々は自分のボタンを作成することができますし、それらが必要なものを行う.例を見ましょう.
const { app, BrowserWindow, Menu } = require('electron')
...
app.on('ready', function(){
    devtools = new BrowserWindow()
    window = new BrowserWindow({ x: 0, y: 0, width:800, height:600})
    window.loadURL(path.join('file://', __dirname, 'static/index.html'))
    window.setTitle('Texty')
    Menu.setApplicationMenu(Menu.buildFromTemplate([
        {
            label: app.getName(),
            submenu: [
                {
                    label: `Hello`,
                    click: () => console.log("Hello world")
                }
            ]
        }
    ]))

})
我々はまずMenu 電子成分.次に、ロードするアプリケーションのメニューを作成するために使用します.上記は単なる例です.通常、最初のラベルは単にサブメニューを開きます.だからラベルのために、我々はアプリ名を使用しているし、我々を作成しているHello メッセージをまとめるボタン.
そのメニューを広げましょう.しかし、オブジェクトを巨大にすることができますので、別のコンポーネントでメニューを追加しましょう.
// ./main.js
const menu = require('./components/Menu')
app.on('ready', function(){
    window = new BrowserWindow({ x: 0, y: 0, width:800, height:600})
    ...
    Menu.setApplicationMenu(menu(window))

})
それはナビゲーションが分割できる方法です.
を作成しましょう./components/Menu.js 関数を返すファイル.
const {app, Menu } = require('electron')
module.exports = function(win){
    return Menu.buildFromTemplate([
        {
            label: app.getName(),
            submenu: [
                { label: `Hello`, click: () => console.log("Hello world") }
            ]
        },
        {
            label: 'Edit',
            submenu: [
                {label: 'Undo', role: 'undo'  },
                {label: 'Redo', role: 'redo'  },
                {label: 'Cut', role: 'cut'  },
                {label: 'Copy', role: 'copy'  },
                {label: 'Paste', role:'paste'  },
            ]
        },
        {
            label: 'Custom Menu', 
            submenu: [/* We'll add more actions */]
        }

    ])    
}
電子は私たちに1セットを与えるroles は、フードの下に重い持ち上げます.利用できるすべての役割を見るために、関連に続いてください.
この点から、我々はすべてのナビゲーションをサブメニューとして加えるつもりですCustom Menu - それを面白い保つ!

新しいドキュメントの作成


これまでのところ、我々のアプリケーションの状態は、ディスクからファイルを読み込み、コンテンツを表示するようなものです.(このアプローチにおける落とし穴は最後に論じられる)
新しい文書を追加する機能を追加しましょう.
我々はナビゲーションにボタンを追加することから始めます.それで./components/Menu.js 次を追加します.
const { NEW_DOCUMENT_NEEDED } = require('../actions/types')
module.exports = function(window){
...
{
    label: 'Custom Menu', 
    submenu: [
        {
            label: 'New',
            accelerator: 'cmd+N',
            click: () => {
                window.webContents.send(NEW_DOCUMENT_NEEDED, 'Create new document')
            }
        }
    ]
それはNew メニューのボタン.accelerator プロパティはボタンをショートカットにすることです.ボタンをクリックすると、アプリケーションのレンダリング部分にメッセージを送ります!
私はこれが把握するのが複雑であるという状態を読んだいくつかのチュートリアルであるが、考えると、ストアと通信する唯一の方法はリスニングとディスパッチメッセージを通してあります.それはまったく同じです.
The ./main.js バックエンドを扱ってください.それは電子メールのモジュールへのアクセス(メニューのように、ウェブカメラへのアクセスを希望するとすべてのソート)を与える.
オール・イン./static/scripts/*.js 上記の機能へのアクセスを持っていません.コードのこの部分はDOMを操作するだけである.コードのこの部分をどんなfs操作(下記でより多くの)のために使用するのにも強いケースさえあります.
バック・イン./static/scripts/index.js 私たちはNEW_DOCUMENT_NEEDED .
const { ipcRenderer } = require('electron'); 
const { NEW_DOCUMENT_NEEDED } = require(path.resolve('actions/types'))
ipcRenderer.on(NEW_DOCUMENT_NEEDED, (event , data) => {
    let form = document.getElementById('form')
        form.classList.toggle('show')
    document.getElementById('title_input').focus()
    form.addEventListener('submit', function(e){
        e.preventDefault()
        // write file here ?
    })
})
私たちはNEW_DOCUMENT_NEEDED 伝送我々がそれを聞くとき、我々はフォーム(通常のCSSクラストグル)を示します.

フォームが送信されると、新しいファイルを書く必要があります.
この単純なアプリケーションのために、我々は使用するfs.writeFile ちょうど下// write file here ? . しかし、これが大きいプロジェクトなら、レンダリング側のファイルシステム操作をしたくないでしょう.アプリケーションが巨大であれば./main.js 操作を扱うことができません(明らかに、あなたは我々の範囲を越えている新しいウインドウを必要とします).しかし、主にそれがどのように行われるかもしれないかを調査するために、我々は./main.js システムに書き込む.
const { ipcRenderer } = require('electron'); 
const {  WRITE_NEW_FILE_NEEDED } = require(path.resolve('actions/types'))
...
form.addEventListener('submit', function(e){
    e.preventDefault()
    // write file here ?
    ipcRenderer.send(WRITE_NEW_FILE_NEEDED, {
        dir: `./data/${fileName}.md`
    })
})
上記のオブジェクトを送信するWRITE_NEW_FILE_NEEDED チャンネル(そのチャンネル名は、あなたが好きである何でもありえます)
ヘッディング./main.js ファイルを作成し、メッセージを送信します.
ipcMain.on(WRITE_NEW_FILE_NEEDED, (event, {dir}) => {
    fs.writeFile(dir, `Start editing ${dir}`, function(err){
        if(err){ return console.log('error is writing new file') }
        window.webContents.send(NEW_FILE_WRITTEN, `Start editing ${dir}`)
    });
})
全く同じ考えWRITE_NEW_FILE_NEEDED が送信されているdir それはそのチャンネルを通して送られて、そのディレクトリにファイルを書いて、書き込みプロセスが完了したメッセージを送り返します.
最後に./statics/scripts/index.js
form.addEventListener('submit', function(e){
    e.preventDefault()
    let fileName = e.target[0].value
    ...
    ipcRenderer.on(NEW_FILE_WRITTEN, function (event, message) {
        handleNewFile(e, `./data/${fileName}.md`, message)
    });
})
それがそうです.
もちろん、あなたはrepository フル写真を取得します.The handleNewFile アプリケーションを開いている時間のためだけにフォームをクリックして、イベントをクリックします.ページにコンテンツを表示します.
const handleNewFile = function(form, dir, content){ 
    let fileName =form.target[0].value
    form.target.classList.remove('show')
    let elChild = document.createElement('li')
    elChild.innerText = fileName
    readFileContentOnClick(dir, elChild) // read file on click
    form.target[0].value = ''
    form.target.parentNode.insertBefore(elChild,form.target.nextSibling);
    document.getElementById('content').innerHTML = content;
}
iPcrendererとiPcmainの間のコミュニケーションのまわりで私の頭を得ている方法は、考えることによってあります...我々がRedux店と通信する方法は、全く同じです.
これまでのコードのダイアグラムです

ご覧のように、2つのプロセス間のこのダンスは、私たちがしていることに対する過ちです.しかし、この種のものはUIをブロックしないために起こります.私が言ったように、チャンスもこれはより大きなアプリケーションで十分ではないでしょう.私はそれが機能ではないと思う、それはバグです.

変更の保存


最後に、シリーズのこの部分のために、我々は変化を保存する必要があります.
Macのパターンに続いて、ファイルが保存された後、ファイルが保存された後に削除されることを示すためのビジュアル表示が必要です.で始まる./static/scripts/index.js
document.getElementById('content').onkeyup = e => { 
    if(!document.title.endsWith("*")){ 
        document.title += ' *' 
    }; 
    ipcRenderer.send(SAVE_NEEDED, { // alerting ./component/Menu.js
        content: e.target.innerHTML,
        fileDir
    })
}
onkeyup その場合は、アスタリスクをタイトルに追加して送信する場合、何かが入力されたことを意味しますSAVE_NEEDED メインプロセスまで.入力された情報と影響を受けるファイルディレクトリが必要になります.
今度は我々は耳を貸さない./main.js でも./components/Menu.js (もちろん、同じプロセスの一部です).
let contentToSave = ''
ipcMain.on(SAVE_NEEDED, (event, content) => {
    contentToSave = content 
})
module.exports = function(window){
    return Menu.buildFromTemplate([
        ...
        {
            label: 'Save',
            click: () => {
                if(contentToSave != ''){
                    fs.writeFile(contentToSave.fileDir, contentToSave.content, (err) => {
                        if (err) throw err;
                        window.webContents.send(SAVED, 'File Saved')
                    });
                }
            },
            accelerator: 'cmd+S'
        }
On SAVE_NEEDED コンテンツを送信します.毎回Save を選択すると、その内容をチェックします.そして、一旦ファイルが書き込まれるならば、我々はメッセージと共にReleaseセクションにアラートを送りましたFile Saved , ここで我々はそれを扱う./static/scripts/index.js
ipcRenderer.on(SAVED, (event , data) => { // when saved show notification on screen
    el = document.createElement("p");
    text = document.createTextNode(data);
    el.appendChild(text)
    el.setAttribute("id", "flash");
    document.querySelector('body').prepend(el)
    setTimeout(function() { // remove notification after 1 second
        document.querySelector('body').removeChild(el);
        document.title = document.title.slice(0,-1) // remove asterisk from title
    }, 1000);
});
そして最後の結果は以下の通りです:

今日はこれだ!
しかし、私は私が明らかに述べる必要があると感じます.私は電子の基礎に焦点を当てるつもりです.したがって、あなたが気づいたように、私は確認の上で全く焦点を合わせませんでした.
我々が生産のために最小の標準を満たすためにこれをする必要がある多くのもののほとんど:
  • ファイルが既に存在するかどうかを調べます.
  • それらの間を移動するときに保存されていないファイルを扱う.
  • 実際にコンテンツをMarkdownに変換します.
  • ストアコンテンツ使用innerText むしろinnerHTML と同じ
    最後のチュートリアルで指摘しました.
  • そして、上記よりさらに重要かもしれないより多くのもの.
  • しかし、どれも電子的なものではないので、電子の学習に寄与しないコードを書き、説明するのに時間を費やさないことを選んだ.
    もう一つのウインドウを加えて、ユーザー選好に取り組むことを見ますこのミニシリーズで、もう一つのチュートリアルが、あります.
    一方、プロジェクトをチェックアウトgithub, branch: part2