以太坊构建DApps系列教程(七):为DAO合约构建Web3 UI
以太坊构建DApps系列教程(七):为DAO合约构建Web3 UI
在本系列关于使用以太坊构建DApps教程的第6部分中,我们通过添加投票,黑名单,股息分配和撤销来完成DAO合约,同时投入一些额外的辅助函数以实现良好的标准。在本教程中,我们将构建一个用于与我们的故事Story交互的Web界面,否则我们无法统计用户如何参与。所以这是我们故事Story发布之前的最后一部分。
由于这不是一个Web应用程序教程,我们将保持非常简单。下面的代码不是生产就绪的,只是作为如何将JavaScript连接到区块链的概念证明。但首先,让我们添加一个新的迁移。
自动转移
现在,当我们部署代币和DAO时,它们位于区块链上但不进行交互。为了测试我们构建的内容,我们需要手动将代币所有权和余额转移到DAO,这在测试期间可能很乏味。
让我们写一个新的迁移,为我们做这件事。创建文件4_configure_relationship.js
并将以下内容放在其中:
var Migrations = artifacts.require("./Migrations.sol");
var StoryDao = artifacts.require("./StoryDao.sol");
var TNSToken = artifacts.require("./TNSToken.sol");
var storyInstance, tokenInstance;
module.exports = function (deployer, network, accounts) {
deployer.then(function () {
return TNSToken.deployed();
}).then(function (tIns) {
tokenInstance = tIns;
return StoryDao.deployed();
}).then(function (sIns) {
storyInstance = sIns;
return balance = tokenInstance.totalSupply();
}).then(function (bal) {
return tokenInstance.transfer(storyInstance.address, bal);
})
.then(function (something) {
return tokenInstance.transferOwnership(storyInstance.address);
});
}
这是这段代码的作用。首先,你会注意到它是基于promise的。它充满了各种调用。这是因为我们在调用下一个数据之前依赖于返回一些数据的函数。所有合约调用都是基于promise的,这意味着它们不会立即返回数据,因为Truffle需要向节点请求信息,因此promise在将来返回数据。我们强制代码等待这些数据,使用then
关键词并提供所有then
调用函数,这些函数在最终给出时将使用此结果调用。
所以,按顺序:
首先,向节点询问已部署代币的地址并将其返回。
然后,接受此数据,将其保存到全局变量中,并询问已部署的DAO的地址并将其返回。
然后,接受这些数据,将其保存到全局变量中,并询问代币合约的所有者将在其帐户中具有的余额,这在技术上是总供应量,并返回此数据。
然后,一旦你得到这个余额,用它来调用这个代币的transfer函数,并将令牌发送到DAO的地址并返回结果。
然后,忽略返回的结果——我们只想知道它何时完成——最后将代币的所有权转移到DAO的地址,返回数据但不丢弃它。
运行truffle migrate --reset
现在应该产生这样的输出:
前端
前端是一个常规的静态HTML页面,其中包含一些JavaScript用于与区块链和一些CSS进行通信以使页面变得不那么难看。
让我们在子文件夹public
创建一个文件index.html
,并为其提供以下内容:
The Neverending Story
Chapter 0
It's a rainy night in central London.
This is an example submission. A proposal for its deletion has been submitted.
0xbE2B28F870336B4eAA0aCc73cE02757fcC428dC9
This is a long submission. It has over 244 characters, just we can see what it looks like when rendered in the UI. We need to make sure it doesn't break anything and the layout also needs to be maintained, not clashing with actions/buttons etc.
0xbE2B28F870336B4eAA0aCc73cE02757fcC428dC9
This is an example submission. A proposal for its deletion has been submitted but is looking like it'll be rejected.
0xbE2B28F870336B4eAA0aCc73cE02757fcC428dC9
注意:这是一个非常基本的框架,仅用于演示集成。请不要把这当成最终产品!
你可能缺少web3
文件夹中的dist
文件夹。该软件仍处于测试阶段,因此仍有可能出现轻微漏洞。要解决此问题并使用dist
文件夹,请运行npm install ethereum/web3.js --save
。
对于CSS,让我们在public/assets/css/main.css
一些基本内容:
@supports (grid-area: auto) {
.grid-container{
display: grid;
grid-template-columns: 6fr 5fr 4fr;
grid-template-rows: 10rem ;
grid-column-gap: 0.5rem;
grid-row-gap: 0.5rem;
justify-items: stretch;
align-items: stretch;
grid-template-areas:
"header header information"
"content events information";
height: 100vh;
}
.events {
grid-area: events;
}
.content {
grid-area: content;
}
.information {
grid-area: information;
}
.header {
grid-area: header;
text-align: center;
}
.container {
border: 1px solid black;
padding: 15px;
overflow-y: scroll;
}
p {
margin: 0;
}
}
body {
padding: 0;
margin: 0;
font-family: sans-serif;
}
然后作为JS,我们将在public/assets/js/app.js
:
var Web3 = require('web3');
var web3 = new Web3(web3.currentProvider);
console.log(web3);
这里发生了什么?
既然我们假设所有用户都安装了MetaMask,并且MetaMask将自己的Web3实例注入到任何访问过的网页的DOM中,我们基本上可以访问我们网站上MetaMask的wallet provider
。实际上,如果我们在页面打开时登录MetaMask,我们将在控制台中看到:
注意MetamaskInpageProvider
是如何激活的。实际上,如果我们在控制台中键入web3.eth.accounts
,我们将通过MetaMask访问的所有帐户都将打印出来:
但是,这个特殊帐户默认添加到我自己的个人Metamask中,因此余额为0eth。它不是我们运行的Ganache或PoA链的一部分:
请注意,如果要求我们的MetaMask活动帐户的余额产生0,同时要求我们的一个私有区块链帐户的余额产生100以太(在我的情况下它是Ganache,所以所有帐户都用100以太初始化)。
关于语法
你会注意到这些调用的语法看起来有点奇怪:
web3.eth.getBalance("0x35d4dCDdB728CeBF80F748be65bf84C776B0Fbaf", function(err, res){console.log(JSON.stringify(res));});
为了读取区块链数据,大多数MetaMask用户不会在本地运行节点,而是从Infura或其他远程节点请求它。因此,我们实际上可以依靠回调。因此,通常不支持同步方法。相反,一切都是通过promise或回调来完成的——就像本文开头的部署步骤一样。这是否意味着你需要非常熟悉为以太坊开发JS的promise?不,这意味着以下内容。在DOM中进行JS调用时......
总是提供一个回调函数作为你正在调用的函数的最后一个参数。
假设它的返回值是双重的:第一个error
,然后是result
。
所以,基本上,只需考虑延迟响应就可以了。当节点响应数据时,你定义为回调函数的函数将由JavaScript调用。是的,这意味着你不能指望你的代码在编写时逐行执行!
有关promises,回调和所有async jazz
的更多信息,请参阅此文章。
帐户信息
如果我们打开上面提到的网站骨架,我们得到这样的东西:
让我们用真实数据填充关于帐户信息的最右侧列。
session
当用户未登录其MetaMask扩展名时,帐户列表将为空。如果甚至没有安装MetaMask,则提供程序将为空(未定义)。当他们登录MetaMask时,接口将可用并提供帐户信息以及与连接的以太坊节点(live或Ganache或其他)的交互。
提示:要进行测试,你可以通过单击右上角的头像图标然后选择注销来注销MetaMask。如果用户界面看起来不像下面的屏幕截图,你可能需要通过打开菜单并单击“试用Beta”来激活Beta用户界面。
首先,如果用户已注销,请将该状态列的所有内容替换为用户的消息:
处理它的JS看起来像这样(在public/assets/js/main.js
):
var loggedIn;
(function () {
loggedIn = setLoggedIn(web3.currentProvider !== undefined && web3.eth.accounts.length > 0);
})();
function setLoggedIn(isLoggedIn) {
let loggedInEl = document.querySelector('div.logged.in');
let loggedOutEl = document.querySelector('div.logged.out');
if (isLoggedIn) {
loggedInEl.style.display = "block";
loggedOutEl.style.display = "none";
} else {
loggedInEl.style.display = "none";
loggedOutEl.style.display = "block";
}
return isLoggedIn;
}
第一部分——(function () { -
包含一旦网站加载就要执行的逻辑。因此,当页面准备就绪时,内部的任何内容都会立即执行。调用单个函数setLoggedIn
并将条件传递给它条件是:
设置web3对象的currentProvider(即网站中存在web3客户端)。
可用的帐户数量非零,即可通过此Web3提供商使用帐户。 换句话说,我们已登录至少一个帐户。
如果这些条件一起评估为true
,则setLoggedIn
函数使“Logged out”消息不可见,并且“Logged In”消息可见。
所有这些都具有能够使用任何其他web3提供商的额外优势。如果最终出现MetaMask替代方案,它将立即与此代码兼容,因为我们并未明确期望任何地方的MetaMask。
帐户头像
因为以太坊钱包的每个私钥都是唯一的,所以它可用于生成独特的图像。这是你在MetaMask的右上角或使用MyEtherWallet时看到的彩色化身,尽管Mist,MyEtherWallet和MetaMask都使用不同的方法。让我们为登录用户生成一个并显示它。
Mist中的图标是使用Blockies库生成的——是自定义的,因为原始文件具有损坏的随机数生成器,并且可以为不同的键生成相同的图像。因此,要安装此文件,请将此文件下载到assets/js
文件夹中。然后,在index.html
我们在main.js
之前包含它:
我们还应该升级logged.in
容器:
在main.js,我们启动该功能。
if (isLoggedIn) {
loggedInEl.style.display = "block";
loggedOutEl.style.display = "none";
var icon = blockies.create({ // All options are optional
seed: web3.eth.accounts[0], // seed used to generate icon data, default: random
size: 20, // width/height of the icon in blocks, default: 8
scale: 8, // width/height of each block in pixels, default: 4
});
document.querySelector("div.avatar").appendChild(icon);
因此,我们升级JS代码的登录部分以生成图标并将其粘贴到头像部分。我们应该在渲染之前将它与CSS稍微对齐:
div.avatar { width: 100%; text-align: center; margin: 10px 0; }
现在,如果我们在登录MetaMask时刷新页面,我们应该会看到生成的头像图标。
帐户余额
现在让我们输出一些帐户余额信息。
我们拥有一系列只读功能,我们专门为此目的而开发。所以让我们查询区块链并询问一些信息。为此,我们需要通过以下步骤调用智能合约功能。
1.ABI
获取我们正在调用的函数的合约的ABI。ABI包含函数签名,因此我们的JS代码知道如何调用它们。在此处了解有关ABI的更多信息。
你可以通过在编译后打开项目文件夹中的build/TNSToken.json
和build/StoryDao.json
文件并仅选择abi
部分来获取TNS代币和StoryDAO的ABI([
和]
方括号之间的部分):
我们将这个ABI放在我们的JavaScript代码的顶部,进入main.js
如下所示:
请注意,上面的屏幕截图显示了我的代码编辑器(Microsoft Visual Code)折叠的缩写插入。如果你查看行号,你会注意到令牌的ABI是400行代码,而DAO的ABI是另外1000行,所以将它粘贴到本文中是没有意义的。
2.实例化代币
if (loggedIn) {
var token = TNSToken.at('0x3134bcded93e810e1025ee814e87eff252cff422');
var story = StoryDao.at('0x729400828808bc907f68d9ffdeb317c23d2034d5');
token.balanceOf(web3.eth.accounts[0], function(error, result) {console.log(JSON.stringify(result))});
story.getSubmissionCount(function(error, result) {console.log(JSON.stringify(result))});
//...
我们使用Truffle给我们的地址调用每个合约,并分别为每个token
和story
创建一个实例。然后,我们简单地调用函数(与以前一样异步)。控制台给我们两个零,因为MetaMask中的帐户有0个代币,因为现在故事story中有0个提交。
3.读取和输出数据
最后,我们可以使用我们提供的信息填充用户的个人资料数据。
让我们更新我们的JavaScript:
var loggedIn;
(function () {
loggedIn = setLoggedIn(web3.currentProvider !== undefined && web3.eth.accounts.length > 0);
if (loggedIn) {
var token = TNSToken.at('0x3134bcded93e810e1025ee814e87eff252cff422');
var story = StoryDao.at('0x729400828808bc907f68d9ffdeb317c23d2034d5');
token.balanceOf(web3.eth.accounts[0], function(error, result) {console.log(JSON.stringify(result))});
story.getSubmissionCount(function(error, result) {console.log(JSON.stringify(result))});
readUserStats().then(User => renderUserInfo(User));
}
})();
async function readUserStats(address) {
if (address === undefined) {
address = web3.eth.accounts[0];
}
var User = {
numberOfSubmissions: await getSubmissionsCountForUser(address),
numberOfDeletions: await getDeletionsCountForUser(address),
isWhitelisted: await isWhitelisted(address),
isBlacklisted: await isBlacklisted(address),
numberOfProposals: await getProposalCountForUser(address),
numberOfVotes: await getVotesCountForUser(address)
}
return User;
}
function renderUserInfo(User) {
console.log(User);
document.querySelector('#user_submissions').innerHTML = User.numberOfSubmissions;
document.querySelector('#user_deletions').innerHTML = User.numberOfDeletions;
document.querySelector('#user_proposals').innerHTML = User.numberOfProposals;
document.querySelector('#user_votes').innerHTML = User.numberOfVotes;
document.querySelector('dd.user_blacklisted').style.display = User.isBlacklisted ? 'inline-block' : 'none';
document.querySelector('dt.user_blacklisted').style.display = User.isBlacklisted ? 'inline-block' : 'none';
document.querySelector('dt.user_whitelisted').style.display = User.isWhitelisted ? 'inline-block' : 'none';
document.querySelector('dd.user_whitelisted').style.display = User.isWhitelisted ? 'inline-block' : 'none';
}
async function getSubmissionsCountForUser(address) {
if (address === undefined) {
address = web3.eth.accounts[0];
}
return new Promise(function (resolve, reject) {
resolve(0);
});
}
async function getDeletionsCountForUser(address) {
if (address === undefined) {
address = web3.eth.accounts[0];
}
return new Promise(function (resolve, reject) {
resolve(0);
});
}
async function getProposalCountForUser(address) {
if (address === undefined) {
address = web3.eth.accounts[0];
}
return new Promise(function (resolve, reject) {
resolve(0);
});
}
async function getVotesCountForUser(address) {
if (address === undefined) {
address = web3.eth.accounts[0];
}
return new Promise(function (resolve, reject) {
resolve(0);
});
}
async function isWhitelisted(address) {
if (address === undefined) {
address = web3.eth.accounts[0];
}
return new Promise(function (resolve, reject) {
resolve(false);
});
}
async function isBlacklisted(address) {
if (address === undefined) {
address = web3.eth.accounts[0];
}
return new Promise(function (resolve, reject) {
resolve(false);
});
}
让我们更改个人资料信息部分:
You are logged in!
Submissions
Proposals
Votes
Deletions
Whitelisted
Yes
Blacklisted
Yes
你会注意到我们在获取数据时使用了promises,即使我们的函数当前只是模拟函数:它们会立即返回平面数据。这是因为每个函数都需要不同的时间来获取我们要求它获取的数据,因此我们将在填充User
对象之前等待它们完成,然后将其传递给render
函数,该函数更新了屏幕。
如果您对JS承诺不熟悉并希望了解更多信息,请参阅此帖子。
现在,我们所有的功能都是嘲笑; 我们需要先做一些写操作才能阅读。 但首先我们需要准备好注意那些写作的发生!
监听事件
为了能够跟踪合约发出的事件,我们需要监听它们——否则我们将所有这些emit
语句都放入代码中。我们构建的模拟UI的中间部分用于保存这些事件。
以下是我们如何监听区块链发出的事件:
// Events
var WhitelistedEvent = story.Whitelisted(function(error, result) {
if (!error) {
console.log(result);
}
})
这里我们在StoryDao合约的story
实例上调用Whitelisted
函数,并将回调传递给它。每当触发此给定事件时,将自动调用此回调。因此,当用户被列入白名单时,代码将自动将该事件的输出记录到控制台。
但是,这只会获取网络挖掘的最后一个块的最后一个事件。因此,如果从第1块到第10块触发了几个白名单事件,它只会向我们展示第10块中的那些事件,如果有的话。更好的方法是使用这种方法:
story.Whitelisted({}, { fromBlock: 0, toBlock: 'latest' }).get((error, eventResult) => {
if (error) {
console.log('Error in myEvent event handler: ' + error);
} else {
// eventResult contains list of events!
console.log('Event: ' + JSON.stringify(eventResult[0].args));
}
});
注意:将上面的内容放在JS文件底部的一个单独的部分,一个专门用于事件。
在这里,我们使用get
函数,它允许我们定义从中获取事件的块范围。我们使用0到最新,这意味着我们可以获取此类型的所有事件。但是这增加了与上述监听方法发生冲突的可能。监听方法输出最后一个块的事件,get
方法输出所有这些事件。我们需要一种方法来使JS忽略双重事件。不要写那些你已经从历史中获取的东西。我们会进一步做到这一点,但就目前而言,让我们来处理白名单。
帐户白名单
最后,让我们进行一些写操作。
第一个也是最简单的一个是白名单。请记住,要获得白名单,帐户需要向DAO的地址发送至少0.01以太。你将在部署时获得此地址。如果你的Ganache/PoA链在本课程的各个部分之间重新启动,那没关系,只需使用truffle migrate --reset
重新运行,你就可以获得代币和DAO的新地址。在我的例子中,DAO的地址是0x729400828808bc907f68d9ffdeb317c23d2034d5
,我的代币是0x3134bcded93e810e1025ee814e87eff252cff422
。
设置完所有内容后,让我们尝试向DAO地址发送一定数量的以太。让我们尝试0.05以太只是为了好玩,所以我们可以看看DAO是否为我们提供额外的计算代币,以支付超额费用。
注意:不要忘记自定义gas量——只需在21000限制之上再拍一个零——使用标记为红色的图标。为什么这有必要?因为由简单的ether发送(回调函数)触发的函数执行超过21000的额外逻辑,这对于简单发送就足够了。所以我们需要达到极限。不要担心:超出此限制的任何内容都会立即退款。有关gas如何工作的入门读物,请参见[https://www.sitepoint.com/ethereum-transaction-costs]。
在交易确认后(你将在MetaMask中将其视为“已确认”),我们可以在MetaMask帐户中检查代币金额。我们首先需要将自定义代币添加到MetaMask中,以便跟踪它们。根据下面的动画,过程如下:选择MetaMask菜单,向下滚动到Add Tokens
,选择Custom Token
,粘贴Truffle
在迁移时给你的代币地址,点击Next
,查看余额是否为ok,然后选择添加Add Tokens
。
对于0.05 eth,我们应该有400k令牌,我们这样做。
但是这个事件怎么样?我们收到了这个白名单的通知吗?我们来看看控制台吧。
实际上,完整的数据集就在那里——发出事件的地址,块数和挖掘它的hash,等等。其中包括args
对象,它告诉我们事件数据:addr
是被列入白名单的地址,状态是它是添加到白名单还是从中删除。成功!
如果我们现在刷新页面,则事件再次出现在控制台中。但是怎么样?我们没有将任何新人列入白名单。为什么事件会发生警告?EVM中的事件是它们不像JavaScript那样是一次性的事情。当然,它们包含任意数据并仅用作输出,但它们的输出永远在区块链中注册,因为导致它们的交易也永久地在区块链中注册。因此事件将在发出之后保留,这使我们不必将它们存储在某处并在页面刷新时调用它们!
现在让我们将其添加到UI中的事件屏幕!编辑JavaScript文件的Events
部分,如下所示:
// Events
var highestBlock = 0;
var WhitelistedEvent = story.Whitelisted({}, { fromBlock: 0, toBlock: "latest" });
WhitelistedEvent.get((error, eventResult) => {
if (error) {
console.log('Error in Whitelisted event handler: ' + error);
} else {
console.log(eventResult);
let len = eventResult.length;
for (let i = 0; i < len; i++) {
console.log(eventResult[i]);
highestBlock = highestBlock < eventResult[i].blockNumber ? eventResult[i].blockNumber : highestBlock;
printEvent("Whitelisted", eventResult[i]);
}
}
});
WhitelistedEvent.watch(function(error, result) {
if (!error && result.blockNumber > highestBlock) {
printEvent("Whitelisted", result);
}
});
function printEvent(type, object) {
switch (type) {
case "Whitelisted":
let el;
if (object.args.status === true) {
el = "Whitelisted address "+ object.args.addr +" ";
} else {
el = "Removed address "+ object.args.addr +" from whitelist! ";
}
document.querySelector("ul.eventlist").innerHTML += el;
break;
default:
break;
}
}
哇,变得很快,是吧?不用担心,我们会澄清。
highestBlock
变量将记住从历史记录中获取的最新块。我们创建了一个事件的实例,并为它附加了两个监听器。一个是get
,它从历史记录中获取所有事件并记住最新的块。另一个是watch
,watch
事件“实时”并在最近一个块中出现新事件时触发。只有当刚刚进入的块大于我们记忆中最高的块时,观察者才会触发,确保只有新事件被附加到事件列表中。
我们还添加了一个printEvent
函数执行打印事件的操作; 我们也可以将它重复用于其他类型的事件!
如果我们现在测试它,确实,我们可以很好地打印出来。
现在尝试自己做这个,我们的故事Story可以发出的所有其他事件!看看你是否可以弄清楚如何一次处理它们,而不必为每个都写出这个逻辑。(提示:在数组中定义它们的名称,然后遍历这些名称并动态注册事件!)
手动检查
你还可以通过在MyEtherWallet中打开并调用其whitelist
函数来手动检查StoryBAO的白名单和所有其他公共参数。
你会注意到,如果我们检查刚刚发送白名单金额的帐户,我们将获得true
回复,表明此帐户确实存在于whitelist
映射中。
在将其添加到Web UI之前,使用此相同的功能菜单来试验其他功能。
提交参赛作品
最后,让我们从UI进行正确的写函数调用。这一次,我们将在故事Story中提交一个条目。首先,我们需要清除我们在开始时放在那里的示例条目。编辑HTML到这个:
Chapter 0
It's a rainy night in central London.
Submit
...
还有一些基本的CSS:
.submission_input textarea {
width: 100%;
}
我们添加了一个非常简单的textarea,用户可以通过它提交新条目。
我们现在来做JS部分吧。
首先,让我们准备通过添加一个新事件并修改我们的printEvent
函数来接受这个事件。我们还可以对整个事件部分进行一些重构,以使其更具可重用性。
// Events
var highestBlock = 0;
var WhitelistedEvent = story.Whitelisted({}, { fromBlock: 0, toBlock: "latest" });
var SubmissionCreatedEvent = story.SubmissionCreated({}, { fromBlock: 0, toBlock: "latest" });
var events = [WhitelistedEvent, SubmissionCreatedEvent];
for (let i = 0; i < events.length; i++) {
events[i].get(historyCallback);
events[i].watch(watchCallback);
}
function watchCallback(error, result) {
if (!error && result.blockNumber > highestBlock) {
printEvent(result.event, result);
}
}
function historyCallback(error, eventResult) {
if (error) {
console.log('Error in event handler: ' + error);
} else {
console.log(eventResult);
let len = eventResult.length;
for (let i = 0; i < len; i++) {
console.log(eventResult[i]);
highestBlock = highestBlock < eventResult[i].blockNumber ? eventResult[i].blockNumber : highestBlock;
printEvent(eventResult[i].event, eventResult[i]);
}
}
}
function printEvent(type, object) {
let el;
switch (type) {
case "Whitelisted":
if (object.args.status === true) {
el = "Whitelisted address "+ object.args.addr +" ";
} else {
el = "Removed address "+ object.args.addr +" from whitelist! ";
}
document.querySelector("ul.eventlist").innerHTML += el;
break;
case "SubmissionCreated":
el = "User " + object.args.submitter + " created a"+ ((object.args.image) ? "n image" : " text") +" entry: #" + object.args.index + " of content " + object.args.content+" ";
document.querySelector("ul.eventlist").innerHTML += el;
break;
default:
break;
}
}
现在我们需要做的就是添加一个全新的事件来实例化它,然后为它定义一个case。
接下来,让我们提交。
document.getElementById("submission-body-btn").addEventListener("click", function(e) {
if (!loggedIn) {
return false;
}
var text = document.getElementById("submission-body-input").value;
text = web3.toHex(text);
story.createSubmission(text, false, {value: 0, gas: 400000}, function(error, result) {
refreshSubmissions();
});
});
function refreshSubmissions() {
story.getAllSubmissionHashes(function(error, result){
console.log(result);
});
}
在这里,我们向提交表单添加一个事件监听器,一旦提交,首先拒绝所有用户未登录的内容,然后抓取内容并将其转换为十六进制格式(这是我们需要将值存储为bytes
)。
最后,它通过调用createSubmission
函数并提供两个参数来创建交易:条目的文本和false标记(意思即不是图像)。第三个参数是交易设置:值表示要发送多少以太,而gas表示你想要默认的gas限制量。这可以在客户端(MetaMask)中手动更改,但这是一个很好的起点,以确保我们不会遇到限制。最后一个参数是我们现在已经习惯的回调函数,这个回调函数将调用一个刷新函数来加载故事Story的所有提交。目前,此刷新功能仅加载故事story哈希并将它们放入控制台,以便我们检查一切是否正常。
注意:以太量为0,因为第一个条目是免费的。进一步的条目将需要添加以太币。我们将动态计算留给你当作业。提示:为此目的,我们的DAO中有一个calculateSubmissionFee
函数。
此时,我们需要在JS的顶部更改一些在页面加载时自动执行的函数:
if (loggedIn) {
token.balanceOf(web3.eth.accounts[0], function(error, result) {console.log(JSON.stringify(result))});
story.getSubmissionCount(function(error, result) {console.log(JSON.stringify(result))});
web3.eth.defaultAccount = web3.eth.accounts[0]; // CHANGE
readUserStats().then(User => renderUserInfo(User));
refreshSubmissions(); // CHANGE
} else {
document.getElementById("submission-body-btn").disabled = "disabled";
}
更改标记为//CHANGE
:第一个允许我们设置执行交易的默认帐户。这可能会在未来的Web3
版本中默认使用。第二个刷新页面加载时提交的内容,因此我们在网站打开时获得一个完整的故事story。
如果你现在尝试提交条目,MetaMask应在你单击“提交”后立即打开,并要求你确认提交。
你还应该在事件部分中看到事件打印出来。
控制台应该回显这个新条目的哈希值。
注意:MetaMask目前在私有网络和nonce
方面存在问题。它在这里描述并将很快修复,但如果nonce
在提交条目时在JavaScript控制台中收到错误,那么目前的权宜之计解决方案是重新安装MetaMask(禁用和启用将不起作用)。请记住首先备份你的种子SEED :你需要它来重新导入你的MetaMask帐户!
最后,让我们获取这些条目并显示它们。让我们从一些CSS开始:
.content-submissions .submission-submitter { font-size: small; }
现在让我们更新一下这个refreshSubmissions
功能:
我们浏览所有提交内容,获取它们的哈希值,获取每个哈希值,然后在屏幕上输出。如果提交者与登录用户相同,则打印“你”而不是地址。
让我们添加另一个条目进行测试。
结论
在这一部分中,我们为DApp开发了基本前端的开端。
由于开发完整的前端应用程序也可以成为它自己的一个过程,我们将作为家庭作业留给你进一步的发展。只需调用所演示的函数,将它们绑定到常规JavaScript流程中(通过像VueJS这样的框架或普通的旧jQuery或像我们上面所做的原生JS)并将它们绑定在一起。它实际上就像与标准服务器API交谈。如果你遇到困难,请查看代码的项目仓库!
可以执行的其他升级:
检测web3提供程序何时更改或可用帐户数何时更改,指示登录或注销事件并自动重新加载页面。
除非用户已登录,否则将阻止呈现提交表单。
防止呈现投票和删除按钮,除非用户至少有1个代币等。
让人们提交并呈现Markdown!
按时间(块号)订购事件,而不是按类型订购!
使事件更漂亮,更可读:不是显示十六进制内容,而是将其翻译为ASCII并截断为30个左右的字符。
使用像VueJS这样的适当的JS框架来从项目中获得一些可重用性并获得更好的结构化代码。
在下一部分和最后一部分中,我们将专注于将我们的项目部署到实时互联网。敬请关注!
======================================================================
分享一些以太坊、EOS、比特币等区块链相关的交互式在线编程实战教程:
java以太坊开发教程,主要是针对java和android程序员进行区块链以太坊开发的web3j详解。
python以太坊,主要是针对python工程师使用web3.py进行区块链以太坊开发的详解。
php以太坊,主要是介绍使用php进行智能合约开发交互,进行账号创建、交易、转账、代币开发以及过滤器和交易等内容。
以太坊入门教程,主要介绍智能合约与dapp应用开发,适合入门。
以太坊开发进阶教程,主要是介绍使用node.js、mongodb、区块链、ipfs实现去中心化电商DApp实战,适合进阶。
C#以太坊,主要讲解如何使用C#开发基于.Net的以太坊应用,包括账户管理、状态与交易、智能合约开发与交互、过滤器和交易等。
EOS教程,本课程帮助你快速入门EOS区块链去中心化应用的开发,内容涵盖EOS工具链、账户与钱包、发行代币、智能合约开发与部署、使用代码与智能合约交互等核心知识点,最后综合运用各知识点完成一个便签DApp的开发。
java比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Java代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Java工程师不可多得的比特币开发学习课程。
php比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Php代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Php工程师不可多得的比特币开发学习课程。
tendermint区块链开发详解,本课程适合希望使用tendermint进行区块链开发的工程师,课程内容即包括tendermint应用开发模型中的核心概念,例如ABCI接口、默克尔树、多版本状态库等,也包括代币发行等丰富的实操代码,是go语言工程师快速入门区块链开发的最佳选择。
汇智网原创翻译,转载请标明出处。这里是原文以太坊构建DApps系列教程(七):为DAO合约构建Web3 UI
你可能感兴趣的:(以太坊构建DApps系列教程(七):为DAO合约构建Web3 UI)
OC语言多界面传值五大方式
Magnetic_h
ios ui 学习 objective-c 开发语言
前言在完成暑假仿写项目时,遇到了许多需要用到多界面传值的地方,这篇博客来总结一下比较常用的五种多界面传值的方式。属性传值属性传值一般用前一个界面向后一个界面传值,简单地说就是通过访问后一个视图控制器的属性来为它赋值,通过这个属性来做到从前一个界面向后一个界面传值。首先在后一个界面中定义属性@interfaceBViewController:UIViewController@propertyNSSt
UI学习——cell的复用和自定义cell
Magnetic_h
ui 学习
目录cell的复用手动(非注册)自动(注册)自定义cellcell的复用在iOS开发中,单元格复用是一种提高表格(UITableView)和集合视图(UICollectionView)滚动性能的技术。当一个UITableViewCell或UICollectionViewCell首次需要显示时,如果没有可复用的单元格,则视图会创建一个新的单元格。一旦这个单元格滚动出屏幕,它就不会被销毁。相反,它被添
element实现动态路由+面包屑
软件技术NINI
vue案例 vue.js 前端
el-breadcrumb是ElementUI组件库中的一个面包屑导航组件,它用于显示当前页面的路径,帮助用户快速理解和导航到应用的各个部分。在Vue.js项目中,如果你已经安装了ElementUI,就可以很方便地使用el-breadcrumb组件。以下是一个基本的使用示例:安装ElementUI(如果你还没有安装的话):你可以通过npm或yarn来安装ElementUI。bash复制代码npmi
理解Gunicorn:Python WSGI服务器的基石
范范0825
ipython linux 运维
理解Gunicorn:PythonWSGI服务器的基石介绍Gunicorn,全称GreenUnicorn,是一个为PythonWSGI(WebServerGatewayInterface)应用设计的高效、轻量级HTTP服务器。作为PythonWeb应用部署的常用工具,Gunicorn以其高性能和易用性著称。本文将介绍Gunicorn的基本概念、安装和配置,帮助初学者快速上手。1.什么是Gunico
瑶池防线
谜影梦蝶
冥华虽然逃过了影梦的军队,但他是一个忠臣,他选择上报战况。败给影梦后成逃兵,高层亡尔还活着,七重天失守......随便一条,即可处死冥华。冥华自然是知道以仙界高层的习性此信一发自己必死无疑,但他还选择上报实情,因为责任。同样此信送到仙宫后,知道此事的人,大多数人都认定冥华要完了,所以上到仙界高层,下到扫大街的,包括冥华自己,全都准备好迎接冥华之死。如果仙界现在还属于两方之争的话,冥华必死无疑。然而
《投行人生》读书笔记
小蘑菇的树洞
《投行人生》----作者詹姆斯-A-朗德摩根斯坦利副主席40年的职业洞见-很短小精悍的篇幅,比较适合初入职场的新人。第一部分成功的职业生涯需要规划1.情商归为适应能力分享与协作同理心适应能力,更多的是自我意识,你有能力识别自己的情并分辨这些情绪如何影响你的思想和行为。2.对于初入职场的人的建议,细节,截止日期和数据很重要截止日期,一种有效的方法是请老板为你所有的任务进行优先级排序。和老板喝咖啡的好
Long类型前后端数据不一致
igotyback
前端
响应给前端的数据浏览器控制台中response中看到的Long类型的数据是正常的到前端数据不一致前后端数据类型不匹配是一个常见问题,尤其是当后端使用Java的Long类型(64位)与前端JavaScript的Number类型(最大安全整数为2^53-1,即16位)进行数据交互时,很容易出现精度丢失的问题。这是因为JavaScript中的Number类型无法安全地表示超过16位的整数。为了解决这个问
swagger访问路径
igotyback
swagger
Swagger2.x版本访问地址:http://{ip}:{port}/{context-path}/swagger-ui.html{ip}是你的服务器IP地址。{port}是你的应用服务端口,通常为8080。{context-path}是你的应用上下文路径,如果应用部署在根路径下,则为空。Swagger3.x版本对于Swagger3.x版本(也称为OpenAPI3)访问地址:http://{ip
Linux下QT开发的动态库界面弹出操作(SDL2)
13jjyao
QT类 qt 开发语言 sdl2 linux
需求:操作系统为linux,开发框架为qt,做成需带界面的qt动态库,调用方为java等非qt程序难点:调用方为java等非qt程序,也就是说调用方肯定不带QApplication::exec(),缺少了这个,QTimer等事件和QT创建的窗口将不能弹出(包括opencv也是不能弹出);这与qt调用本身qt库是有本质的区别的思路:1.调用方缺QApplication::exec(),那么我们在接口
绘本讲师训练营【24期】8/21阅读原创《独生小孩》
1784e22615e0
24016-孟娟《独生小孩》图片发自App今天我想分享一个蛮特别的绘本,讲的是一个特殊的群体,我也是属于这个群体,80后的独生小孩。这是一本中国绘本,作者郭婧,也是一个80厚。全书一百多页,均为铅笔绘制,虽然为黑白色调,但并不显得沉闷。全书没有文字,犹如“默片”,但并不影响读者对该作品的理解,反而显得神秘,梦幻,給读者留下想象的空间。作者在前蝴蝶页这样写到:“我更希望父母和孩子一起分享这本书,使他
我校举行新老教师师徒结对仪式暨名师专业工作室工作交流活动
李蕾1229
为促进我校教师专业发展,发挥骨干教师的引领带头作用,11月6日下午,我校举行新老教师师徒结对仪式暨名师专业工作室工作交流活动。图片发自App会议由教师发展处李蕾主任主持,首先,由范校长宣读新老教师结对名单及双方承担职责。随后,两位新调入教师陈玉萍、莫正杰分别和他们的师傅鲍元美、刘召彬老师签订了师徒结对协议书。图片发自App图片发自App师徒拥抱、握手。有了师傅就有了目标有了方向,相信两位新教师在师
ArcGIS栅格计算器常见公式(赋值、0和空值的转换、补充栅格空值)
研学随笔
arcgis 经验分享
我们在使用ArcGIS时通常经常用到栅格计算器,今天主要给大家介绍我日常中经常用到的几个公式,供大家参考学习。将特定值(-9999)赋值为0,例如-9999.Con("raster"==-9999,0,"raster")2.给空值赋予特定的值(如0)Con(IsNull("raster"),0,"raster")3.将特定的栅格值(如1)赋值为空值,其他保留原值SetNull("raster"==
《大清方方案》| 第二话
谁佐清欢
和珅究竟说了些什么?竟能令堂堂九五之尊龙颜失色!此处暂且按下不表;单说这位乾隆皇帝,果真不愧是康熙从小带过的,一旦决定了要做的事,便杀伐决断毫不含糊。他当即亲自拟旨,着令和珅为钦差大臣,全权负责处理方方事件,并钦赐尚方宝剑,遇急则三品以下官员可先斩后奏。和珅身负皇上重托,岂敢有半点怠慢,当夜即率领相关人等,马不停蹄杀奔江汉。这一路上,和珅的几位幕僚一直在商讨方方事件的处置方案。有位年轻幕僚建议快刀
【一起学Rust | 设计模式】习惯语法——使用借用类型作为参数、格式化拼接字符串、构造函数
广龙宇
一起学Rust # Rust设计模式 rust 设计模式 开发语言
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档文章目录前言一、使用借用类型作为参数二、格式化拼接字符串三、使用构造函数总结前言Rust不是传统的面向对象编程语言,它的所有特性,使其独一无二。因此,学习特定于Rust的设计模式是必要的。本系列文章为作者学习《Rust设计模式》的学习笔记以及自己的见解。因此,本系列文章的结构也与此书的结构相同(后续可能会调成结构),基本上分为三个部分
放下是一段成长的修行
小莳玥
人来到这个世界上,只有两件事:生和死。一件事已经做完了,另一件你还急什么呢?是人,都有七情六欲。是心,都有喜怒哀乐,这些再正常不过了。别总抱怨自己活得累,过得辛苦。永远记住:舒坦是留给死人的。苦,才是生活;累,才是工作;变,才是命运;忍,才是历练;容,才是智慧;静,才是修养;舍,才会得到;做,才会拥有。人生,活得太清楚,才是最大的不明白。有些事,看得很清,却说不清;有些人,了解很深,却猜不透;有些
每日一题——第八十九题
互联网打工人no1
C语言程序设计每日一练 c语言
题目:在字符串中找到提取数字,并统计一共找到多少整数,a123xxyu23&8889,那么找到的整数为123,23,8889//思想:#include#include#includeintmain(){charstr[]="a123xxyu23&8889";intcount=0;intnum=0;//用于临时存放当前正在构建的整数。boolinNum=false;//用于标记当前是否正在读取一个整
《庄子.达生9》
钱江潮369
【原文】孔子观于吕梁,县水三十仞,流沫四十里,鼋鼍鱼鳖之所不能游也。见一丈夫游之,以为有苦而欲死也,使弟子并流而拯之。数百步而出,被发行歌而游于塘下。孔子从而问焉,曰:“吾以子为鬼,察子则人也。请问,‘蹈水有道乎’”曰:“亡,吾无道。吾始乎故,长乎性,成乎命。与齐俱入,与汩偕出,从水之道而不为私焉。此吾所以蹈之也。”孔子曰:“何谓始乎故,长乎性,成乎命?”曰:“吾生于陵而安于陵,故也;长于水而安于
腾讯云技术深度探索:构建高效云原生微服务架构
我的运维人生
云原生 架构 腾讯云 运维开发 技术共享
腾讯云技术深度探索:构建高效云原生微服务架构在当今快速发展的技术环境中,云原生技术已成为企业数字化转型的关键驱动力。腾讯云作为行业领先的云服务提供商,不断推出创新的产品和技术,助力企业构建高效、可扩展的云原生微服务架构。本文将深入探讨腾讯云在微服务领域的最新进展,并通过一个实际案例展示如何在腾讯云平台上构建云原生应用。腾讯云微服务架构概览腾讯云微服务架构基于云原生理念,旨在帮助企业快速实现应用的容
Pyecharts数据可视化大屏:打造沉浸式数据分析体验
我的运维人生
信息可视化 数据分析 数据挖掘 运维开发 技术共享
Pyecharts数据可视化大屏:打造沉浸式数据分析体验在当今这个数据驱动的时代,如何将海量数据以直观、生动的方式展现出来,成为了数据分析师和企业决策者关注的焦点。Pyecharts,作为一款基于Python的开源数据可视化库,凭借其丰富的图表类型、灵活的配置选项以及高度的定制化能力,成为了构建数据可视化大屏的理想选择。本文将深入探讨如何利用Pyecharts打造数据可视化大屏,并通过实际代码案例
linux sdl windows.h,Windows下的SDL安装
奔跑吧linux内核
linux sdl windows.h
首先你要下载并安装SDL开发包。如果装在C盘下,路径为C:\SDL1.2.5如果在WINDOWS下。你可以按以下步骤:1.打开VC++,点击"Tools",Options2,点击directories选项3.选择"Includefiles"增加一个新的路径。"C:\SDL1.2.5\include"4,现在选择"Libaryfiles“增加"C:\SDL1.2.5\lib"现在你可以开始编写你的第
直抒《紫罗兰永恒花园外传》
雷姆的黑色童话
没看过《紫罗兰永恒花园》的我莫名的看完了《紫罗兰永恒花园外传》,又莫名的被故事中的姐妹之情狠狠地感动了的一把。感动何在:困苦中相依为命的姐妹二人被迫分离,用一个人的自由换取另一个人的幸福。之后,虽相隔不知几许依旧心心念念彼此牵挂。这种深深的姐妹情谊就是令我为之动容的所在。贝拉和泰勒分别影片开始,海天之间一个孩童凭栏眺望,手中拿着折旧的信纸。镜头一转,挑灯伏案的薇尔莉特正在打字机前奋笔疾书。这些片段
Google earth studio 简介
陟彼高冈yu
旅游
GoogleEarthStudio是一个基于Web的动画工具,专为创作使用GoogleEarth数据的动画和视频而设计。它利用了GoogleEarth强大的三维地图和卫星影像数据库,使用户能够轻松地创建逼真的地球动画、航拍视频和动态地图可视化。网址为https://www.google.com/earth/studio/。GoogleEarthStudio是一个基于Web的动画工具,专为创作使用G
从鸡肉高汤到记忆的魔法再到有效提示的艺术
步子哥
人工智能
还记得小时候那些天马行空的白日梦吗?也许只要按下键盘上的某个神奇组合,电脑就会发出滴滴的声响,一个隐藏的世界突然在你眼前展开,让你获得超凡的能力,摆脱平凡的生活。这听起来像是玩过太多电子游戏的幻想,但实际上,间隔重复系统给人的感觉惊人地相似。在最佳状态下,这些系统就像魔法一样神奇。本文将以一个看似平凡的鸡肉高汤食谱为例,深入浅出地探讨如何编写有效的间隔重复提示,让你像掌握烹饪技巧一样轻松地掌握记忆
下载github patch到本地
小米人er
我的博客 git patch
以下是几种从GitHub上下载以.patch结尾的补丁文件的方法:通过浏览器直接下载打开包含该.patch文件的GitHub仓库。在仓库的文件列表中找到对应的.patch文件。点击该文件,浏览器会显示文件的内容,在页面的右上角通常会有一个“Raw”按钮,点击它可以获取原始文件内容。然后在浏览器中使用快捷键(如Ctrl+S或者Command+S)将原始文件保存到本地,选择保存的文件名并确保后缀为.p
关于提高复杂业务逻辑代码可读性的思考
编程经验分享
开发经验 java 数据库 开发语言
目录前言需求场景常规写法拆分方法领域对象总结前言实际工作中大部分时间都是在写业务逻辑,一般都是三层架构,表示层(Controller)接收客户端请求,并对入参做检验,业务逻辑层(Service)负责处理业务逻辑,一般开发都是在这一层中写具体的业务逻辑。数据访问层(Dao)是直接和数据库交互的,用于查数据给业务逻辑层,或者是将业务逻辑层处理后的数据写入数据库。简单的增删改查接口不用多说,基本上写好一
如果做到轻松在股市赚钱?只要坚持这三个原则。
履霜之人
大A股里向来就有七亏二平一赚的说法,能赚钱的都是少数人。否则股市就成了慈善机构,人人都有钱赚,谁还要上班?所以说亏钱是正常的,或者说是应该的。那么那些赚钱的人又是如何做到的呢?普通人能不能找到捷径去分一杯羹呢?方法是有的,但要做到需要你有极高的自律。第一,控制仓位,散户最大的问题是追涨杀跌,只要涨起来,就把钱往股票上砸,然后被套,隔天跌的受不了,又一刀切,全部割肉。来来回回间,遍体鳞伤。所以散户首
DIV+CSS+JavaScript技术制作网页(旅游主题网页设计与制作)云南大理
STU学生网页设计
网页设计 期末网页作业 html静态网页 html5期末大作业 网页设计 web大作业
️精彩专栏推荐作者主页:【进入主页—获取更多源码】web前端期末大作业:【HTML5网页期末作业(1000套)】程序员有趣的告白方式:【HTML七夕情人节表白网页制作(110套)】文章目录二、网站介绍三、网站效果▶️1.视频演示2.图片演示四、网站代码HTML结构代码CSS样式代码五、更多源码二、网站介绍网站布局方面:计划采用目前主流的、能兼容各大主流浏览器、显示效果稳定的浮动网页布局结构。网站程
398顺境,逆境
戴骁勇
2018.11.27周二雾霾最近儿子进入了一段顺境期,今天表现尤其不错。今天的数学测试成绩喜人,没有出现以往的计算错误,整个卷面书写工整,附加题也在规定时间内完成且做对。为迎接体育测试的锻炼有了质的飞跃。坐位体前屈成绩突飞猛进,估测成绩能达到12cm,这和上次测试的零分来比,简直是逆袭。儿子还在不断锻炼和提升,唯恐到时候掉链子。跑步姿势在我的调教下,逐渐正规起来,速度随之也有了提升。今晚测试的50
【加密社】Solidity 中的事件机制及其应用
加密社
闲侃 区块链 智能合约 区块链
加密社引言在Solidity合约开发过程中,事件(Events)是一种非常重要的机制。它们不仅能够让开发者记录智能合约的重要状态变更,还能够让外部系统(如前端应用)监听这些状态的变化。本文将详细介绍Solidity中的事件机制以及如何利用不同的手段来触发、监听和获取这些事件。事件存储的地方当我们在Solidity合约中使用emit关键字触发事件时,该事件会被记录在区块链的交易收据中。具体而言,事件
python是什么意思中文-在python中%是什么意思
编程大乐趣
Python中%有两种:1、数值运算:%代表取模,返回除法的余数。如:>>>7%212、%操作符(字符串格式化,stringformatting),说明如下:%[(name)][flags][width].[precision]typecode(name)为命名flags可以有+,-,''或0。+表示右对齐。-表示左对齐。''为一个空格,表示在正数的左侧填充一个空格,从而与负数对齐。0表示使用0填
关于旗正规则引擎下载页面需要弹窗保存到本地目录的问题
何必如此
jsp 超链接 文件下载 窗口
生成下载页面是需要选择“录入提交页面”,生成之后默认的下载页面<a>标签超链接为:<a href="<%=root_stimage%>stimage/image.jsp?filename=<%=strfile234%>&attachname=<%=java.net.URLEncoder.encode(file234filesourc
【Spark九十八】Standalone Cluster Mode下的资源调度源代码分析
bit1129
cluster
在分析源代码之前,首先对Standalone Cluster Mode的资源调度有一个基本的认识:
首先,运行一个Application需要Driver进程和一组Executor进程。在Standalone Cluster Mode下,Driver和Executor都是在Master的监护下给Worker发消息创建(Driver进程和Executor进程都需要分配内存和CPU,这就需要Maste
linux上独立安装部署spark
daizj
linux 安装 spark 1.4 部署
下面讲一下linux上安装spark,以 Standalone Mode 安装
1)首先安装JDK
下载JDK:jdk-7u79-linux-x64.tar.gz ,版本是1.7以上都行,解压 tar -zxvf jdk-7u79-linux-x64.tar.gz
然后配置 ~/.bashrc&nb
Java 字节码之解析一
周凡杨
java 字节码 javap
一: Java 字节代码的组织形式
类文件 {
OxCAFEBABE ,小版本号,大版本号,常量池大小,常量池数组,访问控制标记,当前类信息,父类信息,实现的接口个数,实现的接口信息数组,域个数,域信息数组,方法个数,方法信息数组,属性个数,属性信息数组
}
&nbs
java各种小工具代码
g21121
java
1.数组转换成List
import java.util.Arrays;
Arrays.asList(Object[] obj); 2.判断一个String型是否有值
import org.springframework.util.StringUtils;
if (StringUtils.hasText(str)) 3.判断一个List是否有值
import org.spring
加快FineReport报表设计的几个心得体会
老A不折腾
finereport
一、从远程服务器大批量取数进行表样设计时,最好按“列顺序”取一个“空的SQL语句”,这样可提高设计速度。否则每次设计时模板均要从远程读取数据,速度相当慢!!
二、找一个富文本编辑软件(如NOTEPAD+)编辑SQL语句,这样会很好地检查语法。有时候带参数较多检查语法复杂时,结合FineReport中生成的日志,再找一个第三方数据库访问软件(如PL/SQL)进行数据检索,可以很快定位语法错误。
mysql linux启动与停止
墙头上一根草
如何启动/停止/重启MySQL一、启动方式1、使用 service 启动:service mysqld start2、使用 mysqld 脚本启动:/etc/inint.d/mysqld start3、使用 safe_mysqld 启动:safe_mysqld&二、停止1、使用 service 启动:service mysqld stop2、使用 mysqld 脚本启动:/etc/inin
Spring中事务管理浅谈
aijuans
spring 事务管理
Spring中事务管理浅谈
By Tony Jiang@2012-1-20 Spring中对事务的声明式管理
拿一个XML举例
[html]
view plain
copy
print
?
<?xml version="1.0" encoding="UTF-8"?>&nb
php中隐形字符65279(utf-8的BOM头)问题
alxw4616
php中隐形字符65279(utf-8的BOM头)问题
今天遇到一个问题. php输出JSON 前端在解析时发生问题:parsererror.
调试:
1.仔细对比字符串发现字符串拼写正确.怀疑是 非打印字符的问题.
2.逐一将字符串还原为unicode编码. 发现在字符串头的位置出现了一个 65279的非打印字符.
 
调用对象是否需要传递对象(初学者一定要注意这个问题)
百合不是茶
对象的传递与调用技巧
类和对象的简单的复习,在做项目的过程中有时候不知道怎样来调用类创建的对象,简单的几个类可以看清楚,一般在项目中创建十几个类往往就不知道怎么来看
为了以后能够看清楚,现在来回顾一下类和对象的创建,对象的调用和传递(前面写过一篇)
类和对象的基础概念:
JAVA中万事万物都是类 类有字段(属性),方法,嵌套类和嵌套接
JDK1.5 AtomicLong实例
bijian1013
java thread java多线程 AtomicLong
JDK1.5 AtomicLong实例
类 AtomicLong
可以用原子方式更新的 long 值。有关原子变量属性的描述,请参阅 java.util.concurrent.atomic 包规范。AtomicLong 可用在应用程序中(如以原子方式增加的序列号),并且不能用于替换 Long。但是,此类确实扩展了 Number,允许那些处理基于数字类的工具和实用工具进行统一访问。
 
自定义的RPC的Java实现
bijian1013
java rpc
网上看到纯java实现的RPC,很不错。
RPC的全名Remote Process Call,即远程过程调用。使用RPC,可以像使用本地的程序一样使用远程服务器上的程序。下面是一个简单的RPC 调用实例,从中可以看到RPC如何
【RPC框架Hessian一】Hessian RPC Hello World
bit1129
Hello world
什么是Hessian
The Hessian binary web service protocol makes web services usable without requiring a large framework, and without learning yet another alphabet soup of protocols. Because it is a binary p
【Spark九十五】Spark Shell操作Spark SQL
bit1129
shell
在Spark Shell上,通过创建HiveContext可以直接进行Hive操作
1. 操作Hive中已存在的表
[hadoop@hadoop bin]$ ./spark-shell
Spark assembly has been built with Hive, including Datanucleus jars on classpath
Welcom
F5 往header加入客户端的ip
ronin47
when HTTP_RESPONSE {if {[HTTP::is_redirect]}{ HTTP::header replace Location [string map {:port/ /} [HTTP::header value Location]]HTTP::header replace Lo
java-61-在数组中,数字减去它右边(注意是右边)的数字得到一个数对之差. 求所有数对之差的最大值。例如在数组{2, 4, 1, 16, 7, 5,
bylijinnan
java
思路来自:
http://zhedahht.blog.163.com/blog/static/2541117420116135376632/
写了个java版的
public class GreatestLeftRightDiff {
/**
* Q61.在数组中,数字减去它右边(注意是右边)的数字得到一个数对之差。
* 求所有数对之差的最大值。例如在数组
mongoDB 索引
开窍的石头
mongoDB索引
在这一节中我们讲讲在mongo中如何创建索引
得到当前查询的索引信息
db.user.find(_id:12).explain();
cursor: basicCoursor 指的是没有索引
&
[硬件和系统]迎峰度夏
comsci
系统
从这几天的气温来看,今年夏天的高温天气可能会维持在一个比较长的时间内
所以,从现在开始准备渡过炎热的夏天。。。。
每间房屋要有一个落地电风扇,一个空调(空调的功率和房间的面积有密切的关系)
坐的,躺的地方要有凉垫,床上要有凉席
电脑的机箱
基于ThinkPHP开发的公司官网
cuiyadll
行业系统
后端基于ThinkPHP,前端基于jQuery和BootstrapCo.MZ 企业系统
轻量级企业网站管理系统
运行环境:PHP5.3+, MySQL5.0
系统预览
系统下载:http://www.tecmz.com
预览地址:http://co.tecmz.com
各种设备自适应
响应式的网站设计能够对用户产生友好度,并且对于
Transaction and redelivery in JMS (JMS的事务和失败消息重发机制)
darrenzhu
jms 事务 承认 MQ acknowledge
JMS Message Delivery Reliability and Acknowledgement Patterns
http://wso2.com/library/articles/2013/01/jms-message-delivery-reliability-acknowledgement-patterns/
Transaction and redelivery in
Centos添加硬盘完全教程
dcj3sjt126com
linux centos hardware
Linux的硬盘识别:
sda 表示第1块SCSI硬盘
hda 表示第1块IDE硬盘
scd0 表示第1个USB光驱
一般使用“fdisk -l”命
yii2 restful web服务路由
dcj3sjt126com
PHP yii2
路由
随着资源和控制器类准备,您可以使用URL如 http://localhost/index.php?r=user/create访问资源,类似于你可以用正常的Web应用程序做法。
在实践中,你通常要用美观的URL并采取有优势的HTTP动词。 例如,请求POST /users意味着访问user/create动作。 这可以很容易地通过配置urlManager应用程序组件来完成 如下所示
MongoDB查询(4)——游标和分页[八]
eksliang
mongodb MongoDB游标 MongoDB深分页
转载请出自出处:http://eksliang.iteye.com/blog/2177567 一、游标
数据库使用游标返回find的执行结果。客户端对游标的实现通常能够对最终结果进行有效控制,从shell中定义一个游标非常简单,就是将查询结果分配给一个变量(用var声明的变量就是局部变量),便创建了一个游标,如下所示:
> var
Activity的四种启动模式和onNewIntent()
gundumw100
android
Android中Activity启动模式详解
在Android中每个界面都是一个Activity,切换界面操作其实是多个不同Activity之间的实例化操作。在Android中Activity的启动模式决定了Activity的启动运行方式。
Android总Activity的启动模式分为四种:
Activity启动模式设置:
<acti
攻城狮送女友的CSS3生日蛋糕
ini
html Web html5 css css3
在线预览:http://keleyi.com/keleyi/phtml/html5/29.htm
代码如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>攻城狮送女友的CSS3生日蛋糕-柯乐义<
读源码学Servlet(1)GenericServlet 源码分析
jzinfo
tomcat Web servlet 网络应用 网络协议
Servlet API的核心就是javax.servlet.Servlet接口,所有的Servlet 类(抽象的或者自己写的)都必须实现这个接口。在Servlet接口中定义了5个方法,其中有3个方法是由Servlet 容器在Servlet的生命周期的不同阶段来调用的特定方法。
先看javax.servlet.servlet接口源码:
package
JAVA进阶:VO(DTO)与PO(DAO)之间的转换
snoopy7713
java VO Hibernate po
PO即 Persistence Object VO即 Value Object
VO和PO的主要区别在于: VO是独立的Java Object。 PO是由Hibernate纳入其实体容器(Entity Map)的对象,它代表了与数据库中某条记录对应的Hibernate实体,PO的变化在事务提交时将反应到实际数据库中。
实际上,这个VO被用作Data Transfer
mongodb group by date 聚合查询日期 统计每天数据(信息量)
qiaolevip
每天进步一点点 学习永无止境 mongodb 纵观千象
/* 1 */
{
"_id" : ObjectId("557ac1e2153c43c320393d9d"),
"msgType" : "text",
"sendTime" : ISODate("2015-06-12T11:26:26.000Z")
java之18天 常用的类(一)
Luob.
Math Date System Runtime Rundom
System类
import java.util.Properties;
/**
* System:
* out:标准输出,默认是控制台
* in:标准输入,默认是键盘
*
* 描述系统的一些信息
* 获取系统的属性信息:Properties getProperties();
*
*
*
*/
public class Sy
maven
wuai
maven
1、安装maven:解压缩、添加M2_HOME、添加环境变量path
2、创建maven_home文件夹,创建项目mvn_ch01,在其下面建立src、pom.xml,在src下面简历main、test、main下面建立java文件夹
3、编写类,在java文件夹下面依照类的包逐层创建文件夹,将此类放入最后一级文件夹
4、进入mvn_ch01
4.1、mvn compile ,执行后会在