フリーランス 技術調査ブログ

フリーランス/エンジニア Ruby Python Nodejs Vuejs React Dockerなどの調査技術調査の備忘録

nuxtjsにvue-smooth-dndを適用する

はじめに

  • 下記のドラック&ドロップのライブラリをnuxtjsに適用してみる github.com

インストール

npm install vue-dnd-zone

コード

  • pages/test.vueファイルを作成する
<template>
  <div>
    <div class="simple-page">
      <Container @drop="onDrop">
        <Draggable v-for="item in items" :key="item.id">
          <div class="draggable-item">
            {{ item.data }}
          </div>
        </Draggable>
      </Container>
    </div>
  </div>
</template>

<script>
import { Container, Draggable } from 'vue-smooth-dnd'
import { applyDrag, generateItems } from '../utils/helpers'
export default {
  name: 'Simple',
  components: { Container, Draggable },
  data() {
    return {
      items: generateItems(50, (i) => ({ id: i, data: 'Draggable ' + i })),
    }
  },
  methods: {
    onDrop(dropResult) {
      this.items = applyDrag(this.items, dropResult)
    },
  },
}
</script>
  • utils/helpers.jsファイルを作成する
export const applyDrag = (arr, dragResult) => {
    const { removedIndex, addedIndex, payload } = dragResult
    if (removedIndex === null && addedIndex === null) return arr
  
    const result = [...arr]
    let itemToAdd = payload
  
    if (removedIndex !== null) {
      itemToAdd = result.splice(removedIndex, 1)[0]
    }
  
    if (addedIndex !== null) {
      result.splice(addedIndex, 0, itemToAdd)
    }
  
    return result
  }
  
  export const generateItems = (count, creator) => {
    const result = []
    for (let i = 0; i < count; i++) {
      result.push(creator(i))
    }
    return result
  }

実行結果

f:id:PX-WING:20210325232401p:plain

簡易版タスク管理機能をRedux Tool Kitで作成してみる②

はじめに

  • 前回、Redux Tool Kitでstoreを作成したので、作成したstoreを利用したコンポーネントを作成してみる

コード

import React from 'react'
import { useSelector } from 'react-redux'
import { selectTasks } from './taskSlice'
import { TaskItem } from './TaskItem'

export const TaskList = () => {
    // useSelectorでstoreに定義しているtasksを取得することができる
    const tasks = useSelector(selectTasks)
    return (
        <div>
            {
                tasks.map((task) => <TaskItem key={task.id} task={task} />)
            }
        </div>
    )
}
import React from 'react'
import { useDispatch } from 'react-redux'
import { doneTask, deleteTask } from './taskSlice'

export const TaskItem = ({ task }) => {
    const dispatch = useDispatch();
    return (
        <div>
            <input type="checkbox" 
                    onClick={()=>dispatch(doneTask(task))}
                    defaultChecked={task.done} />
            <span>{task.title}</span>
            <button onClick={()=>dispatch(deleteTask(task))}>削除</button>
        </div>
    )
}
import React, { useState } from 'react'
import { useDispatch } from "react-redux"
import { createTask } from "./taskSlice"

export const TaskInput = () => {
    const dispatch = useDispatch()
    const [newTitle, setNewTitle] = useState("")
    const handleTitleChange = (e) => {
        setNewTitle(e.target.value)
    }
    const handleSubmit = (e) => {
        e.preventDefault()
        dispatch(createTask(newTitle))
        setNewTitle("");
    }
    return (
        <div>
            <form onSubmit={handleSubmit}>
                <input type="text" 
                  value={newTitle}
                  onChange={handleTitleChange}
                  placeholder="新しいタスクを入力してください"
                />
                <button>追加</button>
            </form>
        </div>
    )
}

実行結果

f:id:PX-WING:20210108084202p:plain

簡易版タスク管理機能をRedux Tool Kitで作成してみる①

はじめに

  • 前回、ReduxToolkitの件を簡単に調べてので今度は実際に実装してみる px-wing.hatenablog.com

コード

  • app/features/task/taskSlice.jsファイルを作成する
import { createSlice } from '@reduxjs/toolkit';

export const taskSlice = createSlice({
  name: 'task',
  initialState: {
      currentId: 3,
      tasks: [
          { id: 1, title: 'タスク1', done: false },
          { id: 2, title: 'タスク2', done: false },
          { id: 3, title: 'タスク3', done: false }          
      ]
  },
  reducers: {
    createTask: (state, action) => {
      // タスクの新規作成
      state.currentId += 1;
      const newTask = {
          id: state.currentId,
          title: action.payload,
          done: false
      }
      state.tasks = [newTask, ...state.tasks]

    },
    doneTask: (state, action) => {
      // 取得したIDのタスクのdoneステータスを変更する
      const task = state.tasks.find((t)=> t.id === action.payload.id)
      if (task){
          task.done = !task.done;
      }
    },
    deleteTask: (state, action) => {
      // 取得したID以外のタスクを取得してstate.tasksを更新する
      state.tasks = state.tasks.filter((t)=> t.id !== action.payload.id)
    }
  },
});

export const { createTask, doneTask, deleteTask } = taskSlice.actions;
export const selectTasks = state => state.task.tasks;
export default taskSlice.reducer;
  • app/store.jsファイルに上記で作成したtaskSliceを追加する
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from '../features/counter/counterSlice';
import taskReducer from '../features/task/taskSlice';

export default configureStore({
  reducer: {
    counter: counterReducer,
    task: taskReducer,
  },
});

確認

  • 上記のファイルを作成した状態で動作確認するとstoreにデータが格納されていることが確認できる f:id:PX-WING:20210107085812p:plain

express adminを動かしてみる

はじめに

  • express版のlaravel admin またはrails adminのようなソースを見つけたので動かしてみる github.com

docker-compose.yaml

  • nodeのイメージをpullするだけのdocker-composeファイルを作成する
version: '3'
services:
  app:
    image: node:14.16.0-alpine
    container_name: app
    ports:
      - "8080:8080"
    volumes:
      - ./express:/srv/backend/app
    environment:
      TZ: Asia/Tokyo
    tty: true
  • node環境を起動する
docker-comose up -d

express adminの雛形をダウンロード及び設定

github.com

  • ローカルに作成されたexpressフォルダ内でgit cloneしてexpress adminのテンプレートをダウンロードする
git clone https://github.com/simov/express-admin-examples.git .
  • 下記のコマンドを実行してDB接続先とexpress adminにログインするアカウント情報を設定する
 ./bin/admin
  • 上記のコマンドだけではhost名など指定できないので、直接express/config.jsonファイルを開きDB接続の指定を行う。
{
    "mysql": {
        "host": "hoge",
        "database": "fuga",
        "user": "hoge",
        "password": "fuga",
        "port": 3306
    },
    "server": {
        "port": 8080
    },
    "app": {
        "layouts": true,
        "themes": true,
        "languages": true
    }
}

express admin起動

  • docker内部にログインする
docker exec -it app ash
  • 下記のコマンドを実行すると起動する
 ./bin/admin

動作確認

  • http://localhost:8080/にアクセスする f:id:PX-WING:20210322234218p:plain

android/kotlinで位置情報を取得する

はじめに

AndrodManifest.xml

ACCESS_COARSE_LOCATION 

  • 位置情報の精度に、街区を指定します。

ACCESS_FINE_LOCATION

  • ACCESS_COARSE_LOCATION をリクエストしたときよりも正確な位置情報を指定します。
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    <uses-permission android:name="android.permission.INTERNET"/>

google maps keyの指定

    <application・・・>
            <meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="@string/google_maps_key" />
    </activity>

layout

  • layout/activity_maps.xml
<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:map="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/map"
    android:name="com.google.android.gms.maps.SupportMapFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MapsActivity" />

Activity

package biz.hogehoge.currentlocationonmap

import android.content.pm.PackageManager
import android.location.Geocoder
import android.location.Location
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.core.app.ActivityCompat
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.location.LocationServices

import com.google.android.gms.maps.CameraUpdateFactory
import com.google.android.gms.maps.GoogleMap
import com.google.android.gms.maps.OnMapReadyCallback
import com.google.android.gms.maps.SupportMapFragment
import com.google.android.gms.maps.model.LatLng
import com.google.android.gms.maps.model.Marker
import com.google.android.gms.maps.model.MarkerOptions
import java.io.IOException
import java.util.*

class MapsActivity : AppCompatActivity(), OnMapReadyCallback {
    var currentMarker: Marker? = null
    private lateinit var mMap: GoogleMap
    var fusedLocationProviderClient: FusedLocationProviderClient? = null
    var currentLocation: Location? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_maps)
        // SupportMapFragmentを取得し、マップが使えるようになったら通知を受ける。       
        fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this)
        fetchLocation()
    }

    private fun fetchLocation() {
        // GPSのパーミッションの確認
        if(ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION)
                    != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION)
            != PackageManager.PERMISSION_GRANTED) {

            ActivityCompat.requestPermissions(this, arrayOf(android.Manifest.permission.ACCESS_FINE_LOCATION), 1000)
            return
        }

        val task = fusedLocationProviderClient?.lastLocation
        task?.addOnSuccessListener { location->
            if(location != null ) {
                this.currentLocation = location
                val mapFragment = supportFragmentManager
                    .findFragmentById(R.id.map) as SupportMapFragment
                mapFragment.getMapAsync(this)
            }

        }

    }

    // GPSの許可ダイアログの承認結果を受け取る
    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
       when(requestCode) {
           1000 -> if(grantResults.size > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
               fetchLocation()
           }
       }
    }

    // 利用可能になったマップを操作します。
    // このコールバックは、マップが使用可能な状態になったときにトリガーされます。
    override fun onMapReady(googleMap: GoogleMap) {
        mMap = googleMap

        // シドニーにマーカーを追加し、カメラを動かす
        val sydney = LatLng(-34.0, 151.0)
        mMap.addMarker(MarkerOptions().position(sydney).title("Marker in Sydney"))
        mMap.moveCamera(CameraUpdateFactory.newLatLng(sydney))*/


        val latlong = LatLng(currentLocation?.latitude!!, currentLocation?.longitude!!)
        drawMarker(latlong)

        mMap.setOnMarkerDragListener(object: GoogleMap.OnMarkerDragListener {
            override fun onMarkerDrag(p0: Marker?) {

            }

            override fun onMarkerDragEnd(p0: Marker?) {
                if(currentMarker != null)
                    currentMarker?.remove()

                val newLatLng = LatLng(p0?.position!!.latitude, p0?.position.longitude)
                drawMarker(newLatLng)
            }

            override fun onMarkerDragStart(p0: Marker?) {

            }
        })
    }

    // マーカー表示hyouji
    private fun drawMarker(latlong : LatLng) {
        val markerOption = MarkerOptions().position(latlong).title("現在地")
            .snippet(getTheAddress(latlong.latitude, latlong.longitude)).draggable(true)

        mMap.animateCamera(CameraUpdateFactory.newLatLng(latlong))
        mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(latlong, 15f))
        currentMarker = mMap.addMarker(markerOption)
        currentMarker?.showInfoWindow()
    }

    // 現在地取得
    private fun getTheAddress(latitude: Double, longitude: Double): String? {
        val geocoder = Geocoder(this, Locale.getDefault())
        val addresses = geocoder.getFromLocation(latitude, longitude, 1)
        if(addresses.size > 0 )
            return addresses[0].getAddressLine(0)
        return ""
    }
}

実行結果

https://cdn-ak.f.st-hatena.com/images/fotolife/P/PX-WING/20210319/20210319215131.jpg

jsonからドキュメントを自動生成する

はじめに

  • jsonファイルを仕様書を動的に作成できないか色々と調べてみた。

CASE1: maticを使ってみる

  • maticを使ってjsonからドキュメントを自動生成する
npm install -g matic
npm install -g pug

www.npmjs.com

  • ドキュメントを生成するために必要なテンプレートをダンロードする
git clone https://github.com/mattyod/matic-draft4-example.git

実行結果

  • サンプルは動いたが、実際に利用しているJSONではうまく行かなかった。使えたら便利そう f:id:PX-WING:20210321002442p:plain

CASE2: json2xlsを使ってみる

  • 下記のインストールする
npm install json2xls

www.npmjs.com

jsonからエクセルファイルを出力するコード

  • test.jsファイルを作成して下記のように記述する
const jsonData = require('./hogehoge.json');
console.log(jsonData)
var json2xls = require('json2xls');
const fs = require('fs');
const filename = 'sample.xlsx';
var convert = function () {
  var xls = json2xls(jsonData);
  fs.writeFileSync(filename, xls, 'binary', (err) => {
     if (err) {
           console.log("writeFileSync :", err);
      }
    console.log( filename+" file is saved!");
 });
}

convert()

実行

node  test.js

実行結果

  • 散々な結果になってしまった。 f:id:PX-WING:20210321002958p:plain

Case3 GoogleスプレッドシートのGASを利用したサンプル

  • これを試す時間がなかったので今度試してみたいと思います。

gist.github.com

medium.com

vercelでデプロイする方法

CLIから本番環境へデプロイする

インストール

  • 下記のパッケージをインストールする
npm i -g vercel

www.npmjs.com

cliでログイン

  • 下記のコマンドを実行するとメールアドレスを聞かれるので、入力すると入力したメールアドレスに対してメールが送信されます。
vercel login
  • 受信したメールのVERIFYボタンをクリックすることでログインすることができる。 f:id:PX-WING:20210320001248p:plain

デプロイ

  • 下記のコマンドを実行するとデプロイすることができますが、本番環境にはすぐに反映されません。
vercel deploy

GUIから本番環境へデプロイする

  • 変更したPromote to Productionを選択する

f:id:PX-WING:20210319235845p:plain

  • ダイアログが表示されて再度、Promote to Productionを選択すると本番環境にデプロイされる f:id:PX-WING:20210320000916p:plain