R note

Gatsby + Airtableサイトのトップページに記事をフラッグする機能を追加してみた

GatsbyJSAirtable

トップページのFLAGGEDアイテムのセクションのキャプチャ

Gatsbyで作っているこのサイトで、上のキャプチャのような感じで、オンゴーイングのノートをトップページの上部にフラッグする機能をつけようと思って作業をしています。

Airtableに項目を追加

Airtableの画面のキャプチャ

まずはAirtableにflagというチェックボックス(true / false)の項目を追加。この項目にチェックを入れるとトップページの「FLAGGED」セクションにノートを表示するようにしたい。

※最終的にはSingle selectに変更しました。

GraphQLのクエリでやってみる

まずは以下のGraphQLクエリを追加してフラッグを立てた記事を表示させてみました。filterを使ってflagにチェックの入った行だけを引っ張ってくるようにクエリを書きました(ハイライトされている4行目の部分)。

      flagged: allAirtable(
         filter: {
            table: {eq: "entry"},
            data: {flag: {ne: null}}         }
      ) {
         edges {
            node {
               data {
                  title
                  slug
               }
            }
         }
      }

ただ、これだとチェックされている項目がない場合に以下のエラーが出てしまいます。簡単に行かないですね。

error  Field "flag" is not defined by type airtableConnectionDataInputObject_2;

先ほど追加したAirtableのflagというフィールドにチェックが入っている項目が一つもないと、そのフィールド自体を引っ張ってこられない仕様になってるんですかね?この辺はソースプラグインの仕様なのか、それともGraphQLの仕様なのか?わからない...

ということで、記事一覧の出力用のクエリで引っ張ってきたデータをJavaScriptのfilter()メソッドを使ってflagのついた項目の有無を判別してやってみようと思います。

JavaScriptのfilter()メソッドを使う方法

以下、JSのfilter()を使って実装する方法の記録です。

Airtableの項目をSingle selectに変更

まず、追加したflagフィールドのfield typeSingle selectに変更してflaggedと空(Empty)のオプションを追加しました。

Airtableのflagのフィールドタイプを表示した画面

記事一覧用のクエリにflagを追加

記事一覧を引っ張ってくるクエリにflagを追加します。

      posts: allAirtable(
         sort: { fields: [data___date], order: DESC },
         filter: { table: {eq: "entry"}},
      ) {
         totalCount
         edges {
            node {
               data {
                  slug
                  title
                  date(formatString: "YYYY年M月D日")
                  flag               }
            }
         }
      }

記事一覧からフラッグされたものを抽出

上記クエリで取得したデータからfilter()を使って、flagflaggedが選択された記事を抽出します。

const posts = this.props.data.posts;
const flagged = flaggedPosts(posts);

function flaggedPosts(posts) {
   let flagged = posts.edges.filter((a) => {
      return a.node.data.flag === "flagged";
   });

   if(typeof flagged === "undefined" || flagged.length === 0){
      return null;
   }

   return(
      <div className={styles.flagged}>
         <h2>FLAGGED</h2>
         <ul>
            {flagged.map(({ node }, index) => (
               <li key={index}>
                  <Link
                     to={`${node.data.slug}/`}
                  >{node.data.title}
                  </Link>
               </li>
            ))}
         </ul>
      </div>
   )
}

1つもフラッグされてない場合にUnknown fieldエラーが出てしまう

記事一覧の出力用のクエリをJavaScriptのfilter()メソッドを使って...と思ったのですが、GraphQLのクエリにflagを追加すると、チェックされた項目が一つもない場合にエラーが出てしまいます。

error GraphQL Error Unknown field `flag` on type `data_2`

仕方がないので、さっきflagフィールドに追加した空のオプションを記事に追加。これで強制的にflagが選択された項目を作りました。JavaScriptのfilter()ではflaggedが選択された項目を抽出しているので、空(Empty)が選択された記事は抽出されません。

   let flagged = posts.edges.filter((a) => {
      return a.node.data.flag === "flagged";
   });

これでとりあえず実装は完了しました。

ハック的なやり方で好きじゃないですが、仕方がないので他の方法が見つかるまでは、この方法で妥協しておこうと思います。