4.Dappsのフロントエンドを作る
ここではDappsをコントロールするフロントエンドをVue.jsを使い作成します。
4.1 必要なディレクトリやファイルの作成
/#を表紙とし、/#/demoに作成したDappsを表示させるフロントエンドにしました。
/scr以下のディレクトリ構成、ファイル構成を以下のとおりに実装してください。なお黒字で記載したファイルやディレクトリの名前は変更しない方がよいかと思われます。一方、赤字で記載したファイル名は任意に変更してもかまいません。
/scr/store以下は新規に追加する必要があります。また、/scr/components以下も配置したいページ数に応じてファイルの追加が必要となります。
4.2 各ファイルのソースコード
各ファイルのソースコードを下記に記載しましたので4.1で作成したそれぞれのファイルにコピペしてください。
/scr/components/Top.vue
<template>
<div>
<p>DEMOに移動する</p>
<input type="button" value="移動" @click="goNewTask()">
</div>
</template>
<script>
// eslint-disable-next-line to ignore the next line.
/* eslint-disable */
export default {
name: 'top',
methods: {
goNewTask: function () {
this.$router.push('demo') //New.vueに移動する
}
},
}
</script>
/scr/components/Demo.vue
<template>
<div>
<table border="1" align="center">
<tr>
<th>myaccount</th>
<th>{{ myaccount }}</th>
</tr>
<tr>
<td>Data in BlockChain</td>
<td>{{ update_bcdata[0] }}</td>
</tr>
</table>
<font color="red"> {{update_bcdata[1]}} </font>
<br><br>
<input type="text" v-model="msg">
<button v-on:click="okButtonClick">update</button>
<br><br><br>
<button v-on:click="returnButtonClick">TOP PAGEへ戻る</button>
</div>
</template>
<script>
// eslint-disable-next-line to ignore the next line.
/* eslint-disable */
import * as types from '../store/mutation-types'
import { mapGetters, mapActions } from 'vuex'
export default {
name: 'demo',
data(){
return {
msg: ''
}
},
methods:{
...mapActions([
'update_BCDATA',
'initial',
]),
okButtonClick: function () {
console.log('ok button click')
this.update_BCDATA(this.msg)
//console.log(this.$store.getters.newTodo)
//this.test_dat()
},
returnButtonClick: function () {
this.$router.go(-1) // 1つ戻る
}
},
computed: {
...mapGetters({
myaccount:'datACCOUNT',
get_bcdata:'BCDATA',
updating_done: 'UPDATING_DONE'
}),
update_bcdata: {
get () {
// return [this.$store.getters.BCDATA, this.$store.getters.UPDATING_DONE]
return [this.get_bcdata,this.updating_done]
},
set (value) {
this.$store.commit(types.UPDATING_BCDAT, value)
},
}
},
created () {
this.initial()
},
}
</script>
<style scoped>
</style>
/scr/rounter/index.js
// eslint-disable-next-line
/* eslint-disable */
import Vue from 'vue'
import Router from 'vue-router'
import Top from '@/components/Top'
import Demo from '@/components/Demo'
Vue.use(Router)
export default new Router({
routes:[
{
path: '/',
name: 'top',
component: Top
},
{
path: '/demo',
name: 'demo',
component: Demo
},
]
})
/scr/store/index.js
// eslint-disable-next-line
/* eslint-disable */
import Vue from 'vue'
import Vuex from 'vuex'
import * as actions from './actions'
import * as getters from './getters'
import demo from './modules/demo'
Vue.use(Vuex)
export default new Vuex.Store({
actions,
getters,
modules: {
demo
}
})
/scr/store/mutaion-types.js
// eslint-disable-next-line to ignore the next line.
/* eslint-disable */
export const GET_ACCOUNT = 'GET_ACCOUNT'
export const GET_BCDAT = 'GET_BCDAT'
export const UPDATING_BCDAT = 'UPDATING_BCDAT'
export const UPDATING_DONE = 'UPDATING_DONE'
/scr/store/actions.js
/scr/store/getters.js
下記のdemo.jsでは、smartContractAddressを指定します。コントラクトをdeployしたときに
表示されたコントラクトアドレスに書き換えてください。
/scr/store/modules/demo.js
// eslint-disable-next-line to ignore the next line.
/* eslint-disable */
import * as types from '../mutation-types'
import Vue from 'vue'
import Vuex from 'vuex'
import Web3 from "web3";
let myAccount; //EOA(External Owned Address)
var contractInstance; //コントラクトインスタンス
var getweb3; // web3オブジェクトを返す関数
var bcdat; //ブロックチェーンより取得したデータ
var smartContractAddress = "0x9bCb05dfA4c55A2Ca057B5615BC9044B9A5f17b0"; //Ropstenテストネット上でのコントラクトアドレス(deploy時に表示されるアドレスに書き換えてください)
//var smartContractAddress = "0xdea0c4131c14f566E22D441F3a0e5D30b9f729C2"; //Localテストネット上でのコントラクトアドレス(deploy時に表示されるアドレスに書き換えてください)
// ABI(Application Binary Interface) はブロックチェーンの外からコントラクトを利用するための
// インターフェースの定義です。
const abi = [
{
"constant": true,
"inputs": [],
"name": "message",
"outputs": [
{
"name": "",
"type": "string"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"name": "initMessage",
"type": "string"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"constant": false,
"inputs": [
{
"name": "newMessage",
"type": "string"
}
],
"name": "update",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
}
];
Vue.use(Vuex)
//initial state
const state = {
account: '',
bc_dat: '',
updating_dat:'',
updating_done:''
}
const getters = {
datACCOUNT: state => state.account, //Account
BCDATA : state => state.bc_dat, //Block chainに保存されているdata
UPDATING_DAT: state => state.updating_dat, //Block chainに書き込みを行うdata
UPDATING_DONE: state => state.updating_done //Block chainに書き込み中と書き込み完了を知らせる。
}
// actions
const actions = {
async initApp() {
//console.log("web3")
//console.log(web3)
myAccount = (await web3.eth.getAccounts())[0];
//console.log("myAccount")
//console.log(myAccount)
return myAccount
},
async getdata(){
try{
const result = await contractInstance.methods.message().call();
console.log('Fetched msg value from blockchain:', result);
return result
} catch (err) {
console.log(err);
}
},
async initial({dispatch,commit}){
window.addEventListener('load', getweb3 = async function() {
// web3 がブラウザのアドオンなどから提供されているかチェックします。(MetaMask)
if (typeof window.ethereum !== 'undefined' || (typeof window.web3 !== 'undefined')) {
// MetaMask の provider を使う
let provider = window['ethereum'] || window.web3.currentProvider;
// MetaMask の provider の利用を可能にします。
// MetaMask にはプライバシーモードがあり、これが有効になっている場合には、この enable() を使っ
// てこのサイトでMetaMaskを使う許可をユーザから得る必要があります。
await provider.enable();
web3 = new Web3(provider);
return web3
} else {
// ユーザが web3 を持っていないケースのハンドリング。 おそらく、あなたのアプリを利用するために
// MetaMask をインストールするように伝えるメッセージを表示する処理を書く必要があります。
// もしくは、Ethereum ノードがローカルで動いている場合には、
// web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
// また、 infura.io の RPC エンドポイントを利用する場合には、
// var web3 = new Web3(new Web3.providers.HttpProvider('https://ropsten.infura.io/your_project_id'));
// のようにできます。
console.log('METAMASK NOT DETECTED');
}
// これで web3.js を自由に使えるようになりました。
// アプリを初期化して起動しましょう!
//await this.initApp(this.abi,this.smartContractAddress);
});
web3 = await getweb3()
//console.log("web3")
//console.log(web3)
contractInstance = new web3.eth.Contract(abi, smartContractAddress);
myAccount = await dispatch('initApp')
bcdat = await dispatch('getdata')
commit(types.GET_ACCOUNT, myAccount)
commit(types.GET_BCDAT, bcdat)
},
async update_BCDATA ({dispatch,commit},value) {
commit(types.UPDATING_BCDAT,value)
//console.log("state.updating_dat")
//console.log(state.updating_dat)
const msgString =state.updating_dat;
commit(types.UPDATING_DONE,'処理中')
if(!msgString){
return window.alert("MESSAGE VALUE IS EMPTY");
}
try {
let option = {
from: myAccount,
gasPrice: "20000000000", // このトランザクションで支払う1ガス当たりの価格。単位は wei。
gas: "41000", // ガスリミット。このトランザクションで消費するガスの最大量。
};
let result = await contractInstance.methods.update(msgString).send(option);
console.log('MESSAGE UPDATED IN BLOCKCHIAN SUCCESSFULLY')
commit(types.UPDATING_DONE,'Block Chainへの書き込みが完了しました')
console.log(result);
bcdat = await dispatch('getdata')
commit(types.GET_BCDAT, bcdat)
} catch (err) {
console.log(err);
}
},
}
// mutations
const mutations = {
[types.GET_ACCOUNT](state, data){
state.account = data
},
[types.GET_BCDAT](state, data){
state.bc_dat = data
},
[types.UPDATING_BCDAT](state, data){
state.updating_dat = data
},
[types.UPDATING_DONE](state, flag){
state.updating_done = flag
}
}
export default {
state,
getters,
actions,
mutations
}
/scr/App.vue
<template>
<div id="app">
<img src="./assets/logo.png">
<h1>Hello Dapps with Vue-cli</h1>
<router-view/>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
/scr/main.js
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
// eslint-disable-next-line to ignore the next line.
/* eslint-disable */
import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
store,
router,
components: { App },
template: '<App/>'
})
4.3 次記事
4.4 参考
Vuexの説明、考え方等はこちらに書きました