LoginSignup
0
1

More than 3 years have passed since last update.

【やってみた】kintoneアプリ「値を判定してサブテーブルへ値をコピー」

Last updated at Posted at 2019-07-11

cybozu developer network の kintone カスタマイズ フォーラム に上がった質問を考えてみました。

アプリの仕様

質問内容を見て下記の様に想定してみました。

要求仕様

フィールドの値を判定して、それが0(ゼロ)以外の場合にテーブルに追加したい。

要件定義

  • 数値フィールドの値を取得する。
  • 値がゼロ、未入力の時はテーブルに追加しない。
  • 数字以外はkintoneの標準の機能でエラーで保存出来ない。
  • 値がゼロ、未入力でも保存は出来る。
  • 編集モードで数値が入力されたら、テーブルに追加する。

動作画面

フォーム画面

スクリーンショット 2019-07-11 20.35.11.png

新規入力して保存押下

スクリーンショット 2019-07-11 20.37.08.png

スクリーンショット 2019-07-11 20.36.51.png

保存後

スクリーンショット 2019-07-11 20.37.26.png

編集入力

スクリーンショット 2019-07-11 20.38.05.png

編集入力後の保存

スクリーンショット 2019-07-11 20.38.35.png

コードについて

関数のテストコードです。

test.sample.js
'use strict';
const assert = require('assert');
const commonModule = require('../src/common');

describe('オブジェクト配列をフィルターする', () => {
    let aryFields;
    let recrods;
    const undef = undefined
        aryFields = ['num01', 'num02', 'num03', 'num04']
        recrods = {
            num01: {
                value: "0"
            },
            num02: {
                value: "100"
            },
            num03: {
                value: ""
            },
            num04: {
                value: undef
            }
        }
    })
    it('フィルターした結果の配列が返る', () => {
        assert(commonModule.filterFields(aryFields, recrods).length === 1)
    })
    it('フィルターした結果の配列が返る2', () => {
        assert(commonModule.filterFields(aryFields, recrods)[0] === "num02")
    })

})

関数の一部です。

フィールドの値を判別しています。
kintoneのレコードを配列で渡して、判別結果を array.filter で処理しています。

common.js
/**
* フィールドコードをフィルターして返す
* @param {Array} fieldCodes フィールドコードの配列
* @param {object} records kintoneフィールドのハッシュ
* @return {Array} filters フィルター後のフィールドコードの配列
* */
exports.filterFields = (fieldCodes, records) => {
  const filters = fieldCodes.filter(fieldCode => {
    if (records[fieldCode].value) { // null or undefined 以外
      if (parseInt(records[fieldCode].value) !== 0) { // 数値変換した結果が0以外
        return true
      }
    } else {
      return false
    }
  })
  return filters
}

メインの処理です。
途中でフィールド情報を取得したり、キャンセル処理をする為に、Promiseで実装しています。
テストコードを書く為に、大した処理もしてませんが処理の大部分を関数としています。

main.js
import Common from './common'
import Swal from 'sweetalert2'

/**
 * 値をチェックする対象のフィールドコード配列
 */
const fieldsDiv = [
  {
    fieldCode: 'num01',
  },
  {
    fieldCode: 'num02',
  },
  {
    fieldCode: 'num03',
  },
  {
    fieldCode: 'num04',
  },
  {
    fieldCode: 'num05',
  },
  {
    fieldCode: 'num06',
  }
]

const HANDLE_EVENT = [
  'app.record.create.submit',
  'app.record.edit.submit',
  'mobile.app.record.create.submit',
  'mobile.app.record.edit.submit'
]
/** @type { array } HANDLE_EVENT */
kintone.events.on(HANDLE_EVENT, async (event) => {
  const record = event.record
  // console.log(record)

  // データ取得対象のフィールドコードの配列をセットする
  const aryFields = Common.destructFields(fieldsDiv, 'fieldCode')

  // 0と空白以外のフィールドコードを絞り込む
  const filterFields = Common.filterFields(aryFields, record)

  // フィールド情報取得
  let kintoneConfig = {}
  await kintone.api(kintone.api.url('/k/v1/app/form/fields', true), 'GET', {"app": kintone.app.getId()}).then( (resp) => {
    console.log(resp);
    kintoneConfig = resp.properties
  }), (err) => {
    console.log(err)
  }   

  // kintoneのテーブルレコードを生成
  const kintoneTableRows = filterFields.map(field => {
    return {
      "num": {"type": "NUMBER", "value": record[field].value},
      "category": {"type": "SINGLE_LINE_TEXT", "value": kintoneConfig[field].label},
    }
  })
  const kintoneTable = Common.createTableRows(kintoneTableRows, 'Table')

  return Swal.fire({
    title: 'info',
    text: "レコード保存の際に値をテーブルにセットします",
    type: 'info',
    showCancelButton: true,
    confirmButtonText: 'OK'
  }).then((result) => {
    if (result.value) { // OKボタン押下
      return Swal.fire({
        title: '保存',
        text: '値をテーブルにセットしました',
        type: 'success',
        confirmButtonText: 'OK'
      }).then(() => {
        record.Table = kintoneTable.Table
        return event  
      })
    } else { // 処理をキャンセルした
      console.log('do cancel')
      return Swal.fire('処理をキャンセルします').then(() => {
        console.log('cancel done')
        return false
      })
    }
  }).catch((error) => {
    event.error = 'レコード保存時にエラーが発生しました'
    return Swal.fire('エラーが発生しました' + error)
  })
});

環境設定

  • Babel7 + Webpack4 でコンパイル、バンドルしています。
  • Vue.js のテンプレートを利用してプロジェクトフォルダを生成しています。
  • テストは Mocha + power-assert を利用しました。

$ vue init webpack-simple プロジェクト名

package.json
{
  "name": "app285",
  "description": "A Vue.js project",
  "version": "1.0.0",
  "author": "Kazuhiro Yoshida <sy250f@gmail.com>",
  "license": "MIT",
  "private": true,
  "scripts": {
    "dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot",
    "build": "cross-env NODE_ENV=production webpack --progress --hide-modules",
    "test": "mocha --require intelli-espower-loader -w"
  },
  "dependencies": {
    "vue": "^2.5.11"
  },
  "browserslist": [
    "> 1%",
    "last 2 versions",
    "not ie <= 8"
  ],
  "devDependencies": {
    "@kintone/dts-gen": "^1.0.3",
    "babel-core": "^6.26.0",
    "babel-loader": "^7.1.2",
    "babel-polyfill": "^6.26.0",
    "babel-preset-env": "^1.6.0",
    "babel-preset-stage-3": "^6.24.1",
    "cross-env": "^5.0.5",
    "css-loader": "^0.28.7",
    "file-loader": "^1.1.4",
    "intelli-espower-loader": "^1.0.1",
    "mocha": "^6.1.4",
    "node-sass": "^4.5.3",
    "power-assert": "^1.6.1",
    "sass-loader": "^6.0.6",
    "typescript": "^3.5.2",
    "vue-loader": "^13.0.5",
    "vue-template-compiler": "^2.4.4",
    "webpack": "^3.6.0",
    "webpack-dev-server": "^2.9.1"
  }
}
webpack.config.js
var path = require('path')
var webpack = require('webpack')

module.exports = {
  entry: ['babel-polyfill','./src/main.js'],
  output: {
    path: path.resolve(__dirname, './dist'),
    publicPath: '/dist/',
    filename: 'build.js'
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'vue-style-loader',
          'css-loader'
        ],
      },
      {
        test: /\.scss$/,
        use: [
          'vue-style-loader',
          'css-loader',
          'sass-loader'
        ],
      },
      {
        test: /\.sass$/,
        use: [
          'vue-style-loader',
          'css-loader',
          'sass-loader?indentedSyntax'
        ],
      },
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: {
          loaders: {
            // Since sass-loader (weirdly) has SCSS as its default parse mode, we map
            // the "scss" and "sass" values for the lang attribute to the right configs here.
            // other preprocessors should work out of the box, no loader config like this necessary.
            'scss': [
              'vue-style-loader',
              'css-loader',
              'sass-loader'
            ],
            'sass': [
              'vue-style-loader',
              'css-loader',
              'sass-loader?indentedSyntax'
            ]
          }
          // other vue-loader options go here
        }
      },
      {
        test: /\.js$/,
        loader: 'babel-loader',
        exclude: /node_modules/
      },
      {
        test: /\.(png|jpg|gif|svg)$/,
        loader: 'file-loader',
        options: {
          name: '[name].[ext]?[hash]'
        }
      }
    ]
  },
  resolve: {
    alias: {
      'vue$': 'vue/dist/vue.esm.js'
    },
    extensions: ['*', '.js', '.vue', '.json']
  },
  devServer: {
    historyApiFallback: true,
    noInfo: true,
    overlay: true
  },
  performance: {
    hints: false
  },
  devtool: '#eval-source-map'
}

if (process.env.NODE_ENV === 'production') {
  module.exports.devtool = '#source-map'
  // http://vue-loader.vuejs.org/en/workflow/production.html
  module.exports.plugins = (module.exports.plugins || []).concat([
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: '"production"'
      }
    }),
    new webpack.optimize.UglifyJsPlugin({
      sourceMap: true,
      compress: {
        warnings: false
      }
    }),
    new webpack.LoaderOptionsPlugin({
      minimize: true
    })
  ])
}

まとめ

  • フォーム未入力時の値判別がなかなかに難しかったです。
  • JSのテストを初めて書いてみました。 power-assert はエラー内容が分かりやすくて助かった。テストは書くべきですね。
  • Vue.js の テンプレートが環境構築に便利でした。

参考サイト

0
1
0

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
0
1