Cree una aplicación web de lista de tareas con tecnología Blockchain

Aquí, vamos a crear una aplicación de lista de tareas que guardará los datos en la string de bloques. La parte blockchain de esta aplicación también puede entenderse como una base de datos. Primero, crearemos un contrato inteligente y, posteriormente, la aplicación web en sí. Usaremos Bloc como el nombre de la aplicación, pero primero, veamos los componentes.

Componentes en aplicación de bloque

  • Ganache : una string de bloques de Ethereum local.
  • Web3 JS : para que la aplicación pueda comunicarse con la string de bloques.
  • Bootstrap : para el front-end de la aplicación.
  • Solidez : para compilación de contratos inteligentes.
  • JQuery- Para la manipulación de DOM.

¿Qué es un contrato inteligente?

Para poder comunicarnos con blockchain necesitaremos escribir un contrato inteligente. Un contrato inteligente también puede usarse entendido como un script de back-end que contacta la string de bloques. El contrato inteligente nos permitirá almacenar nuestras tareas de la lista de tareas pendientes en la string de bloques.

Para escribir y desarrollar el contrato inteligente, utilizaremos el IDE de REMIX

Nota: asegúrese de estar utilizando el sitio Http en lugar de Https. El sitio Http nos permitirá implementar nuestro contrato inteligente en nuestra string de bloques local.

Haga clic en el ícono más para crear un archivo bloc.sol.

Create .sol file

Crear Bloque Sol

La primera línea del contrato inteligente debe declarar la versión de solidez para compilar nuestro contrato inteligente para hacer eso, escribiremos lo siguiente:

pragma solidity ^0.5.1;

Para informarle al compilador sobre nuestro contrato inteligente, definiremos un bloque de contrato. Un contrato como una clase en OOP que contiene todos los campos y métodos:

pragma solidity ^0.5.1;
contract Bloc{
}  

Para almacenar la tarea necesitamos hacer lo siguiente:

1. Cree una estructura para su tarea: Struct permite crear un tipo de datos definido por el usuario. Tendrá una string como tarea y un valor booleano para indicar si la tarea se completó o no.

pragma solidity ^0.5.1;
contract Bloc{
 struct Task{
    string task;
       bool isDone;
   } 
}  

2. Cree una asignación para almacenar nuestra array de tareas con una dirección de usuario asociada: la asignación es como una tabla hash aquí estamos creando una dirección como clave y el valor será una array de estructura de tareas. Establezca esta asignación como privada para el modificador de acceso. La dirección es un tipo de dato en solidez que destaca la dirección de la cuenta.

pragma solidity ^0.5.1;
 contract Bloc{ 
   struct Task{
    string task;
       bool isDone;
   }
   mapping (address => Task[]) private Users;
}

Métodos para manipular nuestra tarea en el contrato:

1. Crear tarea: este método creará una tarea.

  • El método addTask toma una string como argumento.
  • calldata establece la ubicación de datos para el argumento de string.
  • external hace que el método esté disponible cuando se llama a través de web3js.
  • msg.sender nos da la dirección del usuario que llama al método.
  • El método push para agregar la tarea a la asignación.
 pragma solidity ^0.5.1;
 
contract Bloc{ 
   struct Task{
    string task;
       bool isDone;
   }
   
   mapping (address => Task[]) private Users;
 
  function addTask(string calldata _task) external{
      Users[msg.sender].push(Task({
          task:_task,
          isDone:false
      }));
   } 
}

2. Tarea de lectura: este método ayuda a leer el valor de la tarea.

  • Para devolver la estructura de tareas del método getTask, debemos agregar la línea 2.
  • El método getTask toma el índice de la tarea y proporciona la tarea.
  • La memoria es la ubicación de datos para que se devuelva la tarea.
  • view dice que la función no modifica el estado de la string de bloques.
pragma solidity ^0.5.1;
 
contract Bloc{ 
   struct Task{
    string task;
       bool isDone;
   }
   
   mapping (address => Task[]) private Users;
 
  function addTask(string calldata _task) external{
      Users[msg.sender].push(Task({
          task:_task,
          isDone:false
      }));
   }
   
   function getTask(uint _taskIndex) external view returns (Task memory){
       Task storage task = Users[msg.sender][_taskIndex];
       return task;
   } 
}

3. Actualizar tarea: este método actualizará los valores en la tarea.

  • Este método establece que la tarea esté marcada o desmarcada.
  • El método updateStatus tomará el índice de tareas y el estado para actualizar.
  • Mediante taskIndex , podremos acceder a la estructura de la tarea, por lo que estableceremos isDone en el estado aprobado.
 pragma solidity ^0.5.1;
 
contract Bloc{ 
   struct Task{
    string task;
       bool isDone;
   }
   
   mapping (address => Task[]) private Users;
 
  function addTask(string calldata _task) external{
      Users[msg.sender].push(Task({
          task:_task,
          isDone:false
      }));
   }
   
   function getTask(uint _taskIndex) external view returns (Task memory){
       Task storage task = Users[msg.sender][_taskIndex];
       return task;
   }
 
  function updateStatus(uint256 _taskIndex,bool _status) external{
        Users[msg.sender][_taskIndex].isDone = _status;
   } 
}

4. Eliminar tarea: el método deleteTask tomará el índice de la tarea y luego eliminará el elemento de la array como en C.

pragma solidity ^0.5.1;
 
contract Bloc{ 
   struct Task{
    string task;
       bool isDone;
   }
   
   mapping (address => Task[]) private Users;
 
  function addTask(string calldata _task) external{
      Users[msg.sender].push(Task({
          task:_task,
          isDone:false
      }));
   }
   
   function getTask(uint _taskIndex) external view returns (Task memory){
       Task storage task = Users[msg.sender][_taskIndex];
       return task;
   }
 
  function updateStatus(uint256 _taskIndex,bool _status) external{
        Users[msg.sender][_taskIndex].isDone = _status;
   }
 
    function deleteTask(uint256 _taskIndex) external{
       delete Users[msg.sender][_taskIndex];
   } 
}

5. Obtener recuento de tareas: el recuento de tareas se puede recuperar como la longitud de la array de tareas.

Y el programa de solidity completo después de agregar todos los métodos anteriores para hacer frente a una tarea se ve así:

Solidity

pragma solidity ^0.5.1;
 
// Creating a contract
contract Bloc{
    
// Defining a structure to
// store a task
struct Task
{
  string task;
  bool isDone;
}
 
mapping (address => Task[]) private Users;
      
// Defining function to add  a task
function addTask(string calldata _task) external
{
  Users[msg.sender].push(Task({
    task:_task,
    isDone:false
    }));
}
 
// Defining a function to get details of a task 
function getTask(uint _taskIndex) external view returns (Task memory)
{
  Task storage task = Users[msg.sender][_taskIndex];
  return task;
}
   
// Defining a function to update status of a task
function updateStatus(uint256 _taskIndex,bool _status) external
{
  Users[msg.sender][_taskIndex].isDone = _status;
}
   
// Defining a function to delete a task
function deleteTask(uint256 _taskIndex) external
{
  delete Users[msg.sender][_taskIndex];
}
   
// Defining a function to get task count.
function getTaskCount() external view returns (uint256)
{
  return Users[msg.sender].length;
} 
}

Haga clic en el botón Compilar debajo de la opción de la barra de navegación izquierda de Solidez.

Compile smart contract

Compilar bloque.sol

Haga clic en Implementar y Ejecutar transacción en su implementación y dentro del contrato implementado puede encontrar todos los métodos.

Deploy contract

Con esto, hemos terminado de crear un contrato inteligente básico. Este contrato inteligente lo usaremos en el próximo artículo para comunicarnos con una aplicación web. Ahora, crearemos una aplicación web que pueda comunicarse con el contrato inteligente que hemos creado anteriormente.

Antes de comenzar a construir la aplicación web, necesitaremos la string de bloques Ethereum que tendrá el contrato inteligente y los datos de nuestra aplicación. Vamos a usar Ganache para continuar con la descarga e instalación.

Después de la instalación, abra Ganache y haga clic en Inicio rápido de Ethereum. Luego, observe la dirección etiquetada en Servidor RPC (debería ser algo como esto http://127.0.0.1:7545).

Ahora construyendo la aplicación web

HTML

<!doctype html>
<html lang="en">
 
<head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"
        integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
    <!-- App CSS-->
    <style>
        .card-container {
            width: 300px;
            height: 500px;
            background: #2699FB;
            border: 1px solid #2699FB;
            border-radius: 10px;
            opacity: 1;
            margin-right: auto;
            margin-left: auto;
            border-left: 0;
        }
 
        .icon-circle {
            background: white;
            border-radius: 200px;
            height: 70px;
            font-weight: bold;
            width: 70px;
            display: table;
            margin-top: 12px;
            margin-left: 31px;
        }
 
        .bloc {
            margin-top: 12px;
            margin-left: 28px;
        }
 
        .task-count {
            margin-top: 7px;
            margin-left: 31px;
        }
 
        .task-card {
            width: 300px;
            height: 325px;
            border: 1px solid #FFFFFF;
            background: #FFFFFF;
            border-radius: 25px 25px 10px 10px;
            opacity: 1;
            position: relative;
        }
 
        .fab {
            background: #2699FB;
            border-radius: 200px;
            height: 40px;
            width: 40px;
            font-weight: bold;
            border: 0;
            position: absolute;
            right: 0;
            bottom: 0;
            margin-right: 27px;
            margin-bottom: 31px;
        }
 
        .add-task-container {
            top: 150px;
            width: 300px;
            height: 187px;
            background: #FFFFFF;
            border-radius: 25px 25px 0px 0px;
            opacity: 1;
            position: absolute;
        }
 
        .task-done {
            color: gray;
            text-decoration: line-through;
        }
    </style>
    <title>Bloc</title>
</head>
 
<body style="background: #BCE0FD">
    <div class="card-container my-5 border-left-0">
        <div class="icon-circle">
            <img class="px-2 py-2" src="https://github.com/gupta-shrinath/Bloc/raw/gupta-shrinath/images/add-list.png"
                alt="icon">
        </div>
        <h2 class="bloc text-white"><strong>BLOC</strong></h1>
            <p id="taskCount" class="task-count text-white">0 Task</p>
 
 
 
 
            <div class="task-card">
                <!-- Floating Action Button -->
                <button id="fab" class="fab float-right" data-toggle="modal" data-target="#add-task-container">
                    <img clas="m-auto" src="https://github.com/gupta-shrinath/Bloc/raw/gupta-shrinath/images/plus.png"
                        alt="" height="16" width="16">
                </button>
                <!-- #Floating Action Button -->
 
                <!-- Task List-->
                <ul id="list" class="list-group mt-3">
                </ul>
                <!-- #Task List-->
 
                <!-- Add Task Modal -->
                <div id="add-task-container" class="modal add-task-container" data-backdrop="static">
                    <div class="container">
                        <div class="col mx-2">
                            <h5 class="text-primary text-center mt-4">New Task</h5>
                            <input id="new-task" class="mt-3" type="text">
                            <button type="button" data-dismiss="modal" class="btn btn-primary btn-block mt-3"
                                onclick="addTask(document.getElementById('new-task').value);">Add
                                Task</button>
                        </div>
                    </div>
                </div>
                <!-- #Add Task Modal -->
 
            </div>
    </div>
 
    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"
        integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj"
        crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"
        integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN"
        crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"
        integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV"
        crossorigin="anonymous"></script>
    <!-- App and related files JS-->
    <script src="https://cdn.jsdelivr.net/npm/web3@latest/dist/web3.min.js"></script>
    <script src="js/config.js"></script>
    <script src="js/getAccount.js"></script>
    <script src="js/app.js"></script>
</body>
 
</html>

La página web se verá así:

bloque político

getCuenta.js

Javascript

// Connect to Ganache Make sure you enter the address you noted earlier here //
web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:7545'));
// getAccount() will get the first account from  ganache and will set it as defaultAccount for our contract operations ////
async function getAccount() {
    let accounts = await web3.eth.getAccounts();
    web3.eth.defaultAccount = accounts[0];
    console.log(web3.eth.defaultAccount + ' account detected');
    return web3.eth.defaultAccount;
}

config.js

  • Vaya a Remix IDE y asegúrese de tener el bloque.sol del tutorial anterior (asegúrese de asegurarse de que el sitio HTTP, no https).
  • Vaya al compilador de solidez ubicado en el panel izquierdo y haga clic en compilar block.sol. Abajo encontrará un botón con el icono de copiar y texto como ABI, haga clic en él. Péguelo en js/config.js línea 1.
let contractABI = COPIED TEXT;
The copied text will be enclosed in [].
  • Vaya a implementar y ejecutar transacciones en el entorno, seleccione Proveedor Web3.
  • Ingrese la dirección que copió de Ganache y péguela, haga clic en Aceptar.
  • Ahora se verá un botón de implementación, haga clic en él.
  • Abajo encontrará la etiqueta Contratos implementados ahora habrá un botón de icono de copia, haga clic en él.
  • Y pegue en js/config.js línea 2.
let contractAddress = 'COPIED text';
The copied text might look like this 0xF3017acEDd45526aC6153FBBCfcA8096173D245a.
The contractABI helps the web3js with our smart contract
The contractAddress tells the web3js about where on blockchain is our smart contract

aplicación.js

Javascript

$(document).ready(createTaskList());
 
// Auto focus on input of add task modal //
$('#add-task-container').on('shown.bs.modal', function () {
    $('#new-task').trigger('focus');
});
 
/**
 * createTaskList() set the contract object and gets the number
 * of tasks of the user and then calls addTaskToList() to add
 * them to HTML one after the other after all task are added to
 * HTML then calls updateTaskCount()
 * @author Gupta Shrinath <https://github.com/gupta-shrinath>
*/
async function createTaskList() {
    // Get account from the Ganache EVM //
    try {
        await getAccount();
        // Set contract and set gas //
        contract = new web3.eth.Contract(contractABI, contractAddress);
        try {
            numberOfTask = await contract.methods.getTaskCount().call({ from: web3.eth.defaultAccount });
            /*  The actual number of task may differ because
                when an task is removed the task element is
                removed and the index value now has nothing.
            */
            console.log('Number of Tasks are ' + numberOfTask);
            // If there are task present //
            if (numberOfTask != 0) {
                // Fetch one task after the other until no task remain //
                console.log('Start fetching task ...');
                let taskIterator = 0;
                while (taskIterator < numberOfTask) {
                    try {
                        let task = await contract.methods.getTask(taskIterator).call({ from: web3.eth.defaultAccount });
                        if (task[0] != '') {
                            // addTaskToList add this task as children to the ul tag //
                            addTaskToList(taskIterator, task[0], task[1]);
                        }
                        else {
                            console.log('The index ' + taskIterator + ' is empty');
                        }
                    } catch {
                        console.log('Failed to get Task ' + taskIterator);
                    }
                    taskIterator++;
                }
                // Update the task count in HTML //
                updateTasksCount();
            }
        } catch {
            console.log('Failed to get task count from blockchain');
        }
    } catch {
        console.log('Failed to get the account');
    }
 
}
 
/**
 * addTaskToList() takes the task attributes and adds them to
 * the HTML
 * @author Gupta Shrinath <https://github.com/gupta-shrinath>
 * @param {number} id
 * @param {string} name
 * @param {boolean} status
 */
function addTaskToList(id, name, status) {
    console.log('addTaskToList(): Add Task ' + (id) + ' ' + [name, status]);
    /*  Get the id of ul element so to be able to
        add children to it
    */
    let list = document.getElementById('list');
    /*  Create a li element and add the class
        required to make look good  and
        set the id of it
    */
    let item = document.createElement('li');
    item.classList.add('list-group-item', 'border-0', 'd-flex', 'justify-content-between', 'align-items-center');
    item.id = 'item-' + id;
    // Create a text to add it to the li element//
    let task = document.createTextNode(name);
    /*  Create a checkbox and set its id and checked
        value to add it to the li element
    */
    var checkbox = document.createElement("INPUT");
    checkbox.setAttribute("type", "checkbox");
    checkbox.setAttribute("id", "item-" + id + "-checkbox");
    checkbox.checked = status;
    /*  if status is true then add task-done class to li
        element so that the text gets a linethrough
    */
    if (status) {
        item.classList.add("task-done");
    }
    // Add the li element to ul element //
    list.appendChild(item);
    /* Set a ondblclick event to able to remove the
       item when double clicked on it */
    item.ondblclick = function () {
        removeTask(item.id);
    }
    // Append the text of task //
    item.appendChild(task);
    // Append the checkbox for task //
    item.appendChild(checkbox);
    // Add onclick to the checkbox //
    checkbox.onclick = function () { changeTaskStatus(checkbox.id, id); };
}
 
/**
 * removeTask() remove the task from blockchain and then from
 * the HTML using JQuery
 * Note: The taskIndex is the li element id {item-taskIndex}
 * @author Gupta Shrinath <https://github.com/gupta-shrinath>
 * @param {string} taskIndex
 */
async function removeTask(taskIndex) {
    console.log("removeTask(): Remove Task " + taskIndex);
    // Create the selector for the Task //
    let taskSelector = '#' + taskIndex;
    // Make taskIndex to have only task index number
    taskIndex = taskIndex.replace('item-', '');
    try {
        await contract.methods.deleteTask(taskIndex).send({ from: web3.eth.defaultAccount });
        console.log('Remove Task ' + taskIndex + ' from the blockchain');
        // Remove the task from the HTML //
        $(taskSelector).remove();
        // Update the task count in HTML//
        updateTasksCount();
    } catch {
        console.log('Issue occured while removing task item-' + taskIndex);
    }
}
 
/**
 * changeTaskStatus() change the status of task in blockchain and
 * then in the HTML
 * Note: The id is the checkbox id {item-taskIndex-checkbox}
 * @author Gupta Shrinath <https://github.com/gupta-shrinath>
 * @param {string} id
 * @param {number} taskIndex
 */
async function changeTaskStatus(id, taskIndex) {
    // Get checkbox element //
    let checkbox = document.getElementById(id);
    // Get the id of the li element //
    let textId = id.replace('-checkbox', '');
    // Get the li element //
    let text = document.getElementById(textId);
    try {
        await contract.methods.updateStatus(taskIndex, checkbox.checked).send({ from: web3.eth.defaultAccount });
        console.log('changeTaskStatus(): Change status of task ' + textId + ' to ' + checkbox.checked);
        if (checkbox.checked) {
            text.classList.add("task-done");
        } else {
            text.classList.remove("task-done");
        }
    } catch (error) {
        console.log('Failed to change Status of task ' + textId + ' in blockchain');
    }
}
 
/**
 * updateTaskCount() update the number of task in HTML by counting
 * the number of item in the ul element
 * @author Gupta Shrinath <https://github.com/gupta-shrinath>
 */
function updateTasksCount() {
    // Get the element of ul tag //
    let list = document.getElementById('list');
    // Get the count of the ul element //
    let taskCount = list.childElementCount;
    console.log('updateTaskCount(): The number of task are ' + taskCount);
    // Set the count to the taskCount id element //
    let count = document.getElementById('taskCount');
    count.innerText = taskCount + " Task";
}
 
/**
 * addTask() add the task to the HTML via adddTasktoList() and then
 * add it to blockchain and update the count via updateTaskCount()
 * @author Gupta Shrinath <https://github.com/gupta-shrinath>
 * @param {string} name
 */
async function addTask(name) {
    // Get the form element containing the new task //
    let form = document.getElementById('add-task-form');
    // Check if the input is valid and then add it//
    if (form.checkValidity()) {
        console.log('Get the number of task from blockchain');
        // Set blank value for text in the addtask modal //
        document.getElementById('new-task').value = '';
        // Remove the mentioned class because it might be
        // present if a task was added before
        form.classList.remove('was-validated');
        // Get the number of task from blockchain //
        contract.methods.getTaskCount().call({ from: web3.eth.defaultAccount }).then(numberOfTask => {
            // Add the task to the HTML //
            addTaskToList(numberOfTask, name, false);
            // Update the task count in HTML//
            updateTasksCount();
        }, err => {
            console.log('Failed to get the number of task in blockchain ' + err);
        });
        try {
            await contract.methods.addTask(name).send({ from: web3.eth.defaultAccount });
            console.log('Add task ' + name + ' to blockchain');
        } catch {
            console.log('Failed to add task to EVM');
        }
 
    } else {
        form.addEventListener('submit', function (event) {
            // Stop all events //
            event.preventDefault();
            event.stopPropagation();
            // Add the mentioned class to able to display
            // error to user
            form.classList.add('was-validated');
            // Set blank value for text in the addtask modal //
            document.getElementById('new-task').value = '';
        }, false);
 
    }
 
}

Ahora la aplicación está funcionando completamente.

Publicación traducida automáticamente

Artículo escrito por gupta_shrinath y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *