本文中完整程式可以在GitHub找到。
本篇接續EOS智能合約範例-TODO工作事項管理合約,還沒完成合約的建議先去看看。
不得不說,雖然完成了,但是雷區還不少。
我原本用npm
安裝Scatter.js需要的相關套件,但是卻在一個地方卡住,後來使用 CDN 提供的檔案,就又正常了。只能說...使用當下,最新版的可能存在Bug。
接下來使用 Parcel 建置網頁頁面,不知道是什麼的可以參考Parcel入門使用。不過這次避免太多看不懂,不會使用Pug模板。(不過你還需要有一點Vue的概念...)
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdn.scattercdn.com/file/scatter-cdn/js/latest/scatterjs-core.min.js"></script>
<!-- <script src="https://cdn.scattercdn.com/file/scatter-cdn/js/latest/scatterjs-plugin-eosjs.min.js"></script> -->
<script src="https://cdn.scattercdn.com/file/scatter-cdn/js/latest/scatterjs-plugin-eosjs2.min.js"></script
這些套件,因為我編寫的時候,在js裡import
會有一些錯誤,像是ScatterJS
如果這麼用:
import ScatterJS from '@scatterjs/core';
import ScatterEOS from '@scatterjs/eosjs';
import {JsonRpc, Api} from 'eosjs';
import config from './config.js';
ScatterJS.plugins( new ScatterEOS() );
const network = ScatterJS.Network.fromJson(config.config.network);
const rpc = new JsonRpc(network.fullhost());
const api = new Api({ rpc, signatureProvider:ScatterJS.scatter.eos, textDecoder: new TextDecoder(), textEncoder: new TextEncoder() });
const eos = ScatterJS.scatter.eos(network, Api, {rpc});
會在最後ScatterJS.scatter.eos
莫名報錯,貌似Api
型態不對,但應該是Bug。不過你還是可以參考該項目說明試試看。至於...Vue,單純是我試了之後,資料渲染全給我白的,可能要放到$(document).ready()
裡使用。不清楚,我可能還要問問熟悉Vue.js的朋友。
些著看看顯示資料的地方。Vue的部份我會跳過(Bootstrap、jQuery也是),所以只要知道之後邏輯版定在id
為connect
、login
、logout
、refresh
和類別為btn_complete
、btn_delete
。頁面不含操作邏輯,所以基本上我會跳過。不過可以先知道Scatter的流程。會先連結網路、登入驗證身份,最後才去交易送資料。不過其實單純取資料不需要登入,可以直接用get_table_rows()
。(後面會看到)
<div class="container" id="todo_app">
<div class="btn btn-primary" id="connect">連線</div>
<div class="btn btn-primary" id="login" v-show="!account">登入</div>
<div class="btn btn-danger" id="logout" v-show="account">登出</div>
<div v-if="account">登入狀態:{{account.name}}@{{account.authority}}</div>
<div v-else>登入狀態:(未登入)</div>
<h1>代辦清單</h1>
<div id="refresh_setting">
<label for="refresh_index">從:</label>
<input id="refresh_index" type="number" value="1" v-model="task_index"/>
<label for="refresh_limit">數量:</label>
<input id="refresh_limit" type="number" value="50" v-model="task_show_limit"/>
<div class="btn btn-primary" id="refresh">刷新任務</div>
</div>
<ul class="list-group" v-for="task in tasks">
<li class="list-group-item"> {{ task.id }}. [{{ task.status }}] {{ task.task }}
<div class="btn btn-success btn_complete" v-bind:data-id="task.id">完成</div>
<div class="btn btn-danger btn_delete" v-bind:data-id="task.id">刪除</div>
</li>
</ul>
<div v-if="task_more">還有其他任務</div>
<div id="create_task_setting">
<h2>新增任務</h2>
<label for="new_task_content">任務內容:</label>
<input id="new_task_content" v-model="new_task" placeholder="任務內容" type="text" value=""/>
<input class="btn btn-primary" id="btn_create_task" type="button" value="新增"></input>
</div>
需要設定部份獨立出來。host
、port
、protocol
、chainId
應該不陌生,dapp
則是部署之前那份合約的合約帳號。
let config = {
network:{
blockchain:'eos',
chainId:'cf057bbfb72640471fd910bcb67639c22df9f92470936cddc1ade0e2f2e7dc4f',
host:'localhost',
port:8888,
protocol:'http'
},
dapp:'dapp1',
};
首先要先引入而外需要使用的東西,eosjs
是EOS.IO官方提供的套件,要注意版本有1.16以前的,以及2.0之後的差別;config.js
就是上面的設定檔案:
import {JsonRpc, Api} from 'eosjs';
import config from './config.js';
ScatterJS.plugins( new ScatterEOS() );
const network = ScatterJS.Network.fromJson(config.config.network);
const rpc = new JsonRpc(network.fullhost());
const api = new Api({ rpc, signatureProvider:ScatterJS.scatter.eos, textDecoder: new TextDecoder(), textEncoder: new TextEncoder() });
const eos = ScatterJS.scatter.eos(network, Api, {rpc});
ScatterJS
需要使用eosio.js 2.0+
。你還可以多重設定,Scatter是支援的。eosio.js
本身的東西。rpc
和api
。(該項目連結)eos
。首先要先透過ScatterJS.connect()
連線網路,然後會有ScatterJS.login()
方法來登入帳號。登入執行時,Scatter會跳出來要求確認。然後有ScatterJS.logout()
方法登出帳號。登入後取得的結果,可以透過result.accounts[0]
取得帳號,會包含帳號名稱和權限等級。
async function connect(){
await ScatterJS.connect('todoapp', {network}).then(connected => {
if(!connected)
return false;
});
}
function login(){
let login = ScatterJS.login();
login.then((success)=>{
console.log("Login Success");
app.account = success.accounts[0];
},(fail)=>{
console.log("Login Fail");
});
}
讀取資料無須登入,比較單純,先來看看。
function getTasks(){
// https://eosio.github.io/eosjs/latest/classes/json_rpc.jsonrpc/#get_table_rows
rpc.get_table_rows({
code: config.config.dapp,
lower_bound: app.task_index,
limit: app.task_show_limit,
table: 'tasks',
scope: config.config.dapp,
}).then(result=>{
console.group("Tasks");
console.debug("The Resulte:");
console.table(result);
console.debug("tasks:");
console.table(result.rows);
app.tasks = result.rows;
app.task_more = result.more,
console.groupEnd();
});
}
rpc.get_table_rows
的用法可以看看這個頁面。其給定的參數表示:
code
: 合約帳號table
: 資料表scope
: 範圍。在撰寫合約時,直接將範圍設定為合約名稱。如果你有改寫合約,建立多的不同的範圍,讀取時就是在這裡設定,會讀到不同的資料。lower_bound
: 從哪開始讀取任務。預設是從頭1
limit
: 讀取數量。預設是10
之後結果可以透過result.rows
讀取。
建立任務也是一筆交易,會使用到transact
。雖然用ScatterJS包裝起來了,但用法一樣,可以參考這個頁面。一個交易(transaction)是可以包含數個活動(action)的,每個活動的參數,機本要設定合約帳號(account
)、執行活動(name
)、執行權限(authorization
),以及對應活動需要的而外參數(data
),如果知道順序,參數也可以用list
。
// https://eosio.github.io/eosjs/latest/#sending-a-transaction
async function createTask(task_content){
let account = await ScatterJS.login();
let permission = account.accounts[0].authority;
account = account.accounts[0].name;
let result = await eos.transact({
actions: [{
account: config.config.dapp,
name: 'create',
authorization: [{
actor: account,
permission: permission,
}],
data: {
owner: account,
task: task_content
},
}]
}, {
blocksBehind: 3,
expireSeconds: 30,
});
}
新版似乎一定要加上{blocksBehind: 3,expireSeconds: 30}
。之前使用1.16
版本類似下面這樣:
// NOTE: this is an example for eosio.js v1.16
eos.transaction({
actions: [
{
account: dapp, //has to be the smart contract name of the token you want to transfer - eosio for EOS or eosjackscoin for JKR for example
name: "deleteact",
authorization: [{
actor: scatter.identity.accounts[0].name,
permission: scatter.identity.accounts[0].authority
}],data: {
organizer: organizer,
activity_id: activity_id
}
}]
}).then(res => {
console.log(res);
})
完成任務和刪除任務也就大同小異了:
async function completeTask(task_id){
let account = await ScatterJS.login();
let permission = account.accounts[0].authority;
account = account.accounts[0].name;
let result = await eos.transact({
actions: [{
account: config.config.dapp,
name: 'update',
authorization: [{
actor: account,
permission: permission,
}],
data: {
id: task_id,
new_status: "DONE",
},
}]
}, {
blocksBehind: 3,
expireSeconds: 30,
});
}
async function deleteTask(task_id){
let account = await ScatterJS.login();
let permission = account.accounts[0].authority;
account = account.accounts[0].name;
let result = await eos.transact({
actions: [{
account: config.config.dapp,
name: 'remove',
authorization: [{
actor: account,
permission: permission,
}],
data: {
id: task_id,
},
}]
}, {
blocksBehind: 3,
expireSeconds: 30,
});
}
最後綁定事件完工:
$('#login').click(login);
$('#connect').click(connect);
$('#refresh').click(getTasks);
$('.btn_complete').click(async e=>{
console.group("Complete Task");
let id = e.target.getAttribute('data-id');
console.debug("complete task id: #",id);
await completeTask(id);
console.groupEnd();
getTasks();
});
$('.btn_delete').click(async e=>{
console.group("Delete Task");
let id = e.target.getAttribute('data-id');
console.debug("Delete task id: #",id);
await deleteTask(id);
console.groupEnd();
getTasks();
});
$('#btn_create_task').click(async e=>{
console.group("Create Task");
let task_content = app.new_task;
//console.debug("Create task id: #",id);
await createTask(task_content);
console.groupEnd();
getTasks();
});
parcel build -d public src/index.html --public-url ./
本文中完整程式可以在GitHub找到。