5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

kintone UI Component v1で作った要素にsetAttributeする

Last updated at Posted at 2021-04-20

はじめに

kintone UI Componentがベータ版から正式版(v1)になって、とてもシンプルにいい感じになりました。
便利に使わせてもらっております!

import { Text } from 'kintone-ui-component'

kintone.events.on(['app.record.edit.show', 'app.record.create.show'], (event) => {
  const text = new Text({ value: '2021-04-20' })
  kintone.app.record.getHeaderMenuSpaceElement().appendChild(text)
})

ただ、やっぱり使っていると「ここは<input type="date">にしたいなぁ」とか、UI Componentで対応してない属性を使いたい場合とかあるんですよ。
そのやり方がちょっぴり複雑だったのでメモしておきます。

TL;DR

kintone.events.on(['app.record.edit.show', 'app.record.create.show'], (event) => {
  const text = new Text({ value: '2021-04-20' })
  kintone.app.record.getHeaderMenuSpaceElement().appendChild(text) // appendしないと配下はnull
  setTimeout(() => {
    // レンダリング終わるまで待ってからsetAttribute
    text.querySelector('input').setAttribute('type', 'date')
  }, 1000)
})

JSで普通にHTML要素を作成する場合

setAttributeを使って色々と属性を設定できます。

kintone.events.on(['app.record.edit.show', 'app.record.create.show'], (event) => {
  const text = document.createElement('input')
  text.value = '2021-04-20'
  text.setAttribute('type', 'date')
  kintone.app.record.getHeaderMenuSpaceElement().appendChild(text)
})

image.png

kintone UI Componentの場合

失敗例1

こうすれば行けそうな気がしますが、まったくダメです。

kintone.events.on(['app.record.edit.show', 'app.record.create.show'], (event) => {
  const text = new Text({ value: '2021-04-20' })
  text.setAttribute('type', 'date')
  kintone.app.record.getHeaderMenuSpaceElement().appendChild(text)
})

UI Componentで作ったtext要素は、実はけっこう複雑なHTML構造をしてて、
こんな感じで<div> <span>がたくさんネストされてます。
一番外側のタグは<kuc-text>みたいなWeb Componentsのカスタムタグでした!

<kuc-text>
  <style>
    ()
  </style>

  <div class="kuc-text__text" style="width: 192.5px;">
    <label class="kuc-text__text__label" for="xxxxxxxx-xxxx-4xxx-xxxx-xxxxxxxxxxxx-label" hidden="">
      <span class="kuc-text__text__label__text"></span
      ><span class="kuc-text__text__label__required-icon" hidden="">*</span>
    </label>
    <div class="kuc-text__text__input-form">
      <div class="kuc-text__text__input-form__prefix-outer">
        <span class="kuc-text__text__input-form__prefix-outer__prefix" hidden=""></span>
      </div>
      <div class="kuc-text__text__input-form__input-outer" style="width: 100%;">
        <input
          class="kuc-text__text__input-form__input-outer__input"
          type="text"
          id="xxxxxxxx-xxxx-4xxx-xxxx-xxxxxxxxxxxx-label"
          placeholder=""
          textalign="left"
          aria-required="false"
          aria-invalid="false"
          aria-describedby="xxxxxxxx-xxxx-4xxx-xxxx-xxxxxxxxxxxx-error"
        />
      </div>
      <div class="kuc-text__text__input-form__suffix-outer">
        <span class="kuc-text__text__input-form__suffix-outer__suffix" hidden=""></span>
      </div>
    </div>
    <div class="kuc-text__text__error" role="alert" id="xxxxxxxx-xxxx-4xxx-xxxx-xxxxxxxxxxxx-error" hidden=""></div>
  </div>
</kuc-text>

developer Networkの記事kintoneライクなスタイルシートの利用にある構造と似てます。これを意識せずにパッと使えるようにしてくれているライブラリということですね。

上に書いたJSだと、<kuc-text type="date">になるだけで、何の意味もないわけですね。
子要素の中心部<input>を狙ってsetAttributeしてやらないといけません。

失敗例2

<input>要素はコンポーネント配下に1つしかないので、これで行けそうな気がしますが、やっぱダメです。

kintone.events.on(['app.record.edit.show', 'app.record.create.show'], (event) => {
  const text = new Text({ value: '2021-04-20' })
  text.querySelector('input').setAttribute('type', 'date')
  kintone.app.record.getHeaderMenuSpaceElement().appendChild(text)
})

この場合、text.querySelector('input')の結果がnullになっちゃうんですね。
一方、ブラウザのコンソールでtext.querySelector('input')を直接走らせると、ちゃんと動く。
レンダリングの時間が少しかかるので、それを待たねばならぬのです。

失敗例3

1秒ほど待機してみることにしましょう。

kintone.events.on(['app.record.edit.show', 'app.record.create.show'], (event) => {
  const text = new Text({ value: '2021-04-20' })
  setTimeout(() => {
    text.querySelector('input').setAttribute('type', 'date')
    kintone.app.record.getHeaderMenuSpaceElement().appendChild(text)
  }, 1000)
})

これも、結果はダメ。
実はnew Text()しただけでは、いくら待っても配下の要素はnullのままで、appendChild()で表示中のページに追加して初めて、配下のレンダリングが始まるようです。このあたりの背景は詳しくないんですが、パフォーマンス上の理由かと思われます。

成功例

appendChild()してから待機することで、うまく動きました!

kintone.events.on(['app.record.edit.show', 'app.record.create.show'], (event) => {
  const text = new Text({ value: '2021-04-20' })
  kintone.app.record.getHeaderMenuSpaceElement().appendChild(text) // appendしないと配下はnull
  setTimeout(() => {
    // レンダリング終わるまで待ってからsetAttribute
    text.querySelector('input').setAttribute('type', 'date')
  }, 1000)
})

この「1秒待ち」というのはあてずっぽうなので、PCのスペックなどによってはもっと待たなければいけなかったり、もっと短くて十分な場合もあります。

本当にちゃんとやるなら、MutationObserver使ってしっかりと監視することになるのでしょうが、そこまではオーバーキルだと思うので、まぁ「ちょっと待機」で十分かなーと個人的には思います。

サイボウズさんへ

こんな風に、カスタムアトリビュートを自由に設定できるようになると嬉しいっす!
循環参照ですが、Issue送ってみました。

new Text({ value: '2021-04-20', attribute: { type: 'date' } })

でも「ラベルのattribute」とか「途中のdivのattribute」とか
言い出すと切りがないから難しいかなぁ。。。

追記)
やはりカスタムAttributeは無理ってことでした。
Date系のコンポーネントはそのうち出るらしいので、
そのあたりが充実したら、setAttributeする機会も減るでしょうね。

では今日はこの辺で~。またお会いしましょう :sunglasses:

5
2
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?