20191028のNode.jsに関する記事は3件です。

WebComponents だけでアプリの骨格を作ってみる

はじめに

WebComponents、何がいいかというとブラウザでネイティブに対応してるとこですね。

そろそろ本家、3rdパーティーともライブラリが充実してきて、Vue とか React とかのフレームワークを使わずに WebComponents だけでいけそうな感じになってきたので、とりあえず下のようなアプリの骨格を作って検証してみます。

画面収録-2019-10-28-13.05.57.gif

オフィシャルの material.ioGitHub にはメジャーな

  • material-components-android
  • material-components-ios
  • material-components-web
  • material-components-flutter

の他に

ってのがあります(名前が非常にまぎらわしい><)。GUI コンポーネントとして今回これを使わせてもらいます。

また router として、

を使わせてもらいます。

早速作ってみましょう。

準備

node, npm をダウンロードします

https://nodejs.org
この記事は node v12.4.0, npm 6.12.0 で検証しています

ディレクトリを掘って cd して node のプロジェクトを作成します。

$ mkdir wc
$ cd wc
$ yarn init

es-dev-server をインストールして動かしておきます。

$ npm install -g es-dev-server
$ es-dev-server --node-resolve --watch

8000 番のポートが使われてなければ
es-dev-server started on http://localhost:8000
と表示されます。ブラウザでここにアクセスしてください。

--node-resolve オプションが何故必要かについて知りたい場合は以下を参照してください。

https://github.com/material-components/material-components-web-components/blob/master/README.md

作成

使うコンポーネントを登録

$ yarn add @material/mwc-drawer
$ yarn add @material/mwc-icon-button
$ yarn add @material/mwc-top-app-bar
$ yarn add @vaadin/router

メイン画面に表示する WebComponent を作成

Main.js
class
Main extends HTMLElement {
    constructor() {
        super();
        this.attachShadow( { mode: 'open' } ).innerHTML = '<div>MAIN</div>'
    }
}
customElements.define( 'jp-main', Main )
About.js
class
About extends HTMLElement {
    constructor() {
        super();
        this.attachShadow( { mode: 'open' } ).innerHTML = '<div>ABOUT</div>'
    }
}
customElements.define( 'jp-about', About )

まとめあげ

index.html
<!doctype html>
<html lang=zxx>
    <title>drawer demo</title>

    <link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500" rel="stylesheet">
    <link href="https://fonts.googleapis.com/css?family=Material+Icons&display=block" rel="stylesheet">

<body>
    <script type="module">
        import '@material/mwc-drawer'
        import '@material/mwc-top-app-bar'
        import '@material/mwc-icon-button'
        import './Main'
        import './About'

        import { Router } from '@vaadin/router'

        class
        App extends HTMLElement {
            constructor() {
                super();
                this.attachShadow( { mode: 'open' } ).innerHTML = `
                    <mwc-drawer hasHeader type="dismissible">
                        <span slot="title">Drawer Title</span>
                        <span slot="subtitle">subtitle</span>
                        <div>
                            <p>Drawer content</p>
                            <a href="/">Main</a><br />
                            <a href="/About">About</a><br />
                        </div>
                        <div slot="appContent">
                            <mwc-top-app-bar>
                                <mwc-icon-button slot="navigationIcon" icon="menu"></mwc-icon-button>
                                <div slot="title">Title</div>
                            </mwc-top-app-bar>
                            <div id="outlet" />
                        </div>
                    </mwc-drawer>
                `

                const router = new Router( this.shadowRoot.getElementById( 'outlet' ) )
                router.setRoutes(
                    [   { path: '/'         , component: 'jp-main'  }
                    ,   { path: '/about'    , component: 'jp-about' }
                    ]
                )

                const drawer = this.shadowRoot.querySelector( 'mwc-drawer' )
                drawer.parentNode.addEventListener(
                    'MDCTopAppBar:nav'
                ,   _ => drawer.open = !drawer.open
                )
            }
        }
        customElements.define( 'jp-app', App )

    </script>
    <jp-app />
</body>

勘所

navigationIcon

mwc-top-app-bar の navigationIcon スロットに入れられたボタンが押されると、mwc-drawer の parentNode に対して MDCTopAppBar:nav イベントが送られます。(すみません、ドキュメントが見当たりません><、レポジトリの demos/drawer/のあたりを読んで発見しました)

shadowRoot

WebComponent は constructor の中で attachShadow ってやると、shadowRoot が使えるようになります。プライベートな DOM みたいなものです。これを実際の DOM に connect して表示するようになります。
constructor の中ではまだ実際の DOM にコネクトされていないので、shadowRoot に対して getElement... とか querySelector とか発行して、エレメントをとります。

shadowRoot に対して、今回の記事では innerHTML をスタティックテキストでセットしているだけですが、実際にはダイナミックに生成したい場合が多いと思われます。そんなときは polymer の lit-element を使うという選択肢があります。lit-html という HTML テンプレートライブラリをラップしたものです。

古いブラウザのサポート

Edge とか IE11 とかをサポートするにはポリフィルが必要です。詳しくは以下を参照してください。

https://github.com/material-components/material-components-web-components/blob/master/README.md

最後に

shadowRoot を更新する際に既存のエレメントを再利用して描画の高速化を図るようなベースクラスをたぶんそのうち誰かが作ってくれる気がします。もし知っていたら教えてください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[養老乃瀧ハッカソン2019]ひたすらビールを注文するコースターを作った

目次

  • はじめに
  • 機能概要
  • 実装コード
  • 感想と反省点

はじめに

◆飲み会で、周囲の人のグラスが空いていたら、「何か飲まれますか?」と聞いて飲み物を注文せねばならぬ。
◆俺はただ、ビールが飲みたいんだ!飲む物は決まってるんだから注文せずにズッて持ってこいや!

って思うこと、ありませんか?

少なくとも、私は常々思ってます。
なので、空いたグラスをドッと置いたらビールがズッと来るデバイスを作れないだろうかと思って、実際に作りました。
また、居酒屋にあると嬉しい(主に私が)ので、養老乃瀧ハッカソン2019で発表してきました。

機能概要

タイトル:自動お酒注文コースター「飲ま飲まWay!」
DSC_0037_copy.JPG

コメント 2019-10-28 224058.png

☆使い方☆
1.コースターに空いたグラスを置く
DSC_0028_copy.JPG

2.LEDが光るので、店員が飲み物を提供!
コメント 2019-10-28 224017.png

3.飲む!

実装コード

nomanomaWay.js
'use strict';

// Obniz用定数
var Obniz = require('obniz');
var obniz = new Obniz('yourObnizNumber');
var obniz2 = new Obniz('yourObnizNumber');
var stringWeight;

// Obniz関数
obniz.onconnect = async function ()
{
    obniz.display.clear();
    // ロードセル
    var hcsr04 = obniz.wired("hx711", { gnd: 0, dout: 1, sck: 2, vcc: 3 });

    while (true)
    {
        hcsr04.offset = 88815;
        hcsr04.scale = 88925;
        var val = await hcsr04.getValueWait(1);
        stringWeight = Math.abs(roundFloat(val, 4) * 10000);
        console.log(Math.abs(roundFloat(val, 4) * 10000));
        await obniz.wait(10);
        if (Number.isInteger(stringWeight) == false)
        {
            hcsr04.powerDown();
            obniz.close();
        };
    }
    function roundFloat(number, n)
    {
        var _pow = Math.pow(10, n);
        return Math.round(number * _pow) / _pow;
    }
}

obniz2.onconnect = async function ()
{
    obniz2.display.clear();
    // ソレノイド
    var solenoid = obniz2.wired('Solenoid', { gnd: 0, signal: 1 });
    await obniz2.wait(3000);
    if (obniz.connectionState == 'closed')
    {
        solenoid.on();
        await obniz2.wait(3000);
        solenoid.off();
        obniz2.close();
    }
}

感想と反省点

実際に作ったとき、これは賞とれるだろ!と思ったのですが、取れませんでした。
原因を自分なりに考えた時、養老乃瀧ハッカソン2019のテーマが「皆が笑顔になる」ことがメインだったようで、それとはちょっと合わなかったのかなと。
また、ほかの作品たちがインパクトありまくりで、印象に残らなかったというのもあったのかなと。ぶっちゃけ自分の作品地味だし

ただ、結構悔しかったので、リメイクしてリベンジしたいと思ってます。
改善案としては、コースターを光らせたり、なにか音を出したりすることを考えてます。

なお、個性的な作品たちの詳細は、以下サイトをチェックだ!
protopedia
※タグ検索で 'yorohack2019' を入力してね!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ESLint v6.6.0

v6.5.0 | 次 (2019/11/23 JST)

ESLint 6.6.0 がリリースされました。
小さな機能追加とバグ修正が行われています。

これまで ESLint は隔週リリースを行ってきましたが、アクティブなメンテナが減少していることから、今後は月間リリースとなります。

質問やバグ報告等ありましたら、お気軽にこちらまでお寄せください。

? 日本語 Issue 管理リポジトリ
? 日本語サポート チャット
? 本家リポジトリ
? 本家サポート チャット

? 本体への機能追加

context.getCwd()

? RFC035, #12389

各ルールから現在のディレクトリを取得できるようになりました。

module.exports = {
    meta: {},
    create(context) {
        console.log(context.getCwd()) //→ /home/a-user/projects/foo
        return {}
    },
}

この値は基本的に process.cwd() と同じですが、いくつかの場合に異なる値になります。

  • CLIEngine オブジェクトを初期化した後に process.chdir() 関数によって CWD を変更した場合
  • CLIEngine のコンストラクタ オプションとして独自の CWD を指定した場合

また、ブラウザ上で実行している場合など、CWD が不明な場合は undefined を返します。

? 新しいルール

特になし。

? オプションが追加されたルール

no-param-reassign ignorePropertyModificationsForRegex

? #11275

指定した正規表現にマッチする名前の引数について、プロパティの変更を許可するオプションが追加されました。

/*eslint no-param-reassign: [error, { props: true, ignorePropertyModificationsForRegex: ["^req"] }]*/

//✘BAD
function f1(req, res) {
    res.value = 0
}

//✔GOOD
function f2(req, res) {
    req.value = 0
}

Online demo

use-isnan enforceForIndexOf

? #12379

浮動小数点数の仕様により、NaNとの比較は常にfalseになります。これを報告するuse-isnanルールに、Array#indexOfメソッドも報告するオプションが追加されました。代わりに、Array#findIndexメソッドを利用したり、NaN を特別に処理する新しいメソッドArray#includesを利用したりしましょう。

/*eslint use-isnan: [error, { enforceForIndexOf: true }]*/

//✘BAD
var indexOfNaN= myArray.indexOf(NaN)
var hasNaN = myArray.indexOf(NaN) >= 0

//✔GOOD
var indexOfNaN = myArray.findIndex(Number.isNaN)
var hasNaN = myArray.includes(NaN)

Online demo

no-unsafe-negation enforceForOrderingRelations

? #12414

比較演算子の左辺をオペランドとする否定演算子を警告するオプションが追加されました。

/*eslint no-unsafe-negation: [error, { enforceForOrderingRelations: true }] */

//✘BAD
if (! a < b) {}

//✔GOOD
if (!(a < b)) {}

Online demo

no-extra-parens enforceForNewInMemberExpressions

? #12436

new式に直接プロパティアクセスを繋げているときに、そのnew式を囲む括弧を無視するためのオプションが追加されました。

/*eslint no-extra-parens: [error, all, { enforceForNewInMemberExpressions: false }] */

//✔GOOD
var obj = (new Foo()).prop;

Online demo

✒️ eslint --fix をサポートしたルール

特になし。

⚠️ 非推奨になったルール

特になし。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む