1657282620

Build

``npm run build``

Test

``npm test``

Author: jenslind
Source Code: https://github.com/jenslind/piglet

1665369120

CellularAutomata.jl: Cellular Automata Simulation toolkit for Julia

Cellular Automata

A cellular automaton is a collection of "colored" cells on a grid of specified shape that evolves through a number of discrete time steps according to a set of rules based on the states of neighboring cells. The rules are then applied iteratively for as many time steps as desired.

mathworld.wolfram.com/CellularAutomaton

Elementary CA

To generate an elementary cellular automaton, use

``````ca = CellularAutomaton(rule, init, gen)
``````

where `rule` is the Wolfram code (integer), `init` is a vector containing the initial starting condition and `gen` is the number of generations to be computed. For a single starting cell in the middle just omit the `init` vector.

To generate 15 generations of elementary cellular automaton of rule 90 use

``````using CellularAutomata

ca90 = CellularAutomaton(90, 16)
``````
``````                            #
# #
#   #
# # # #
#       #
# #     # #
#   #   #   #
# # # # # # # #
#               #
# #             # #
#   #           #   #
# # # #         # # # #
#       #       #       #
# #     # #     # #     # #
#   #   #   #   #   #   #   #
# # # # # # # # # # # # # # # #
``````

Totalistic CA

For a more complex cellular automaton you can change the number of states `k` the cell can be and the radius `r` of neighbors that can influence the states. If `k` is changed to be larger than 2, a totalistic CA is computed where only the average value of all neighbors count. This can be done like this

``````ca = CellularAutomaton(993, 15, k=3)
``````
``````                        X
XXX
X# #X
X     X
XXX   XXX
X# #X X# #X
X     #     X
XXX   ###   XXX
X# #X # X # X# #X
X      # X #      X
XXX    ## X ##    XXX
X# #X  #   X   #  X# #X
X     X### XXX ###X     X
XXX   X XX  # #  XX X   XXX
X# #X XX###X## ##X###XX X# #X
``````

2 dimensional CAs

Two dimensional cellular automaton (like Conway's Game of Life) can be created by

``````ca = CA2d(B, S, init, gen)
``````

where `B` and `S` are vectors that have the numbers of neighboring cells that define when cell is born or survives, `init` (matrix) is the initial starting condition and `gen` is the number of generations the CA is to be computed.

Game of life is then run for 9 generations for e.g. a turbine pattern by typing

``````ca = CA2d([3], [2, 3], init, 9)
``````

1st step

``````   ###### ##
###### ##
##
##     ##
##     ##
##     ##
##
## ######
## ######

``````

2nd

``````    ####
#    # ##
#    #   #
##    #
##    #  #
#  #   #  #
#  #    ##
#    ##
#   #    #
## #    #
####

``````

3rd

``````     ##
####
# ## ## #
##  #
##  ##  ###
#### #  ###
#  #   #  #
###  # ####
###  ##  ##
#  ##
# ## ## #
####
##

``````

4th

``````    #  #
#
##
# ##      #
#  #   #
#   # ###
#           #
### #   #
#   #  #
#      ## #
##
#
#  #

``````

5th

``````        ##
#
###  ##
### #   #
#    # ##
# #
## #    #
#   # ###
##  ###
#
##
``````

6th

``````        ##
#
# #  ##
# # ###  #
#  ######
## ##
######  #
#  ### # #
##  # #
#
##

``````

7th

``````     #  # #
## # ###
#      #
##     #

#     ##
#      #
### # ##
# #  #

``````

8th

``````    ## ## #
##  ## ##
#
##
##     ##
##
#
## ##  ##
# ## ##

``````

9th

``````   ###### ##
###### ##
##
##     ##
##     ##
##     ##
##
## ######
## ######

``````

Running Tests

To run tests, execute the following command from the root folder of the repository:

``julia tests/run_tests.jl``

Author: Natj
Source Code: https://github.com/natj/CellularAutomata.jl

1650391200

Ansible Jupyter Kernel

The Ansible Jupyter Kernel adds a kernel backend for Jupyter to interface directly with Ansible and construct plays and tasks and execute them on the fly.

Installation:

`ansible-kernel` is available to be installed from pypi but you can also install it locally. The setup package itself will register the kernel with `Jupyter` automatically.

From pypi

``````pip install ansible-kernel
python -m ansible_kernel.install
``````

From a local checkout

``````pip install -e .
python -m ansible_kernel.install
``````

For Anaconda/Miniconda

``````pip install ansible-kernel
python -m ansible_kernel.install --sys-prefix
``````

Usage

Local install

``````    jupyter notebook
# In the notebook interface, select Ansible from the 'New' menu
``````

Container

``````docker run -p 8888:8888 benthomasson/ansible-jupyter-kernel

Then copy the URL from the output into your browser:
http://localhost:8888/?token=ABCD1234
``````

Using the Cells

Normally `Ansible` brings together various components in different files and locations to launch a playbook and performs automation tasks. For this `jupyter` interface you need to provide this information in cells by denoting what the cell contains and then finally writing your tasks that will make use of them. There are Examples available to help you, in this section we'll go over the currently supported cell types.

In order to denote what the cell contains you should prefix it with a pound/hash symbol (#) and the type as listed here as the first line as shown in the examples below.

#inventory

``````#inventory
[all]
ahost ansible_connection=local
anotherhost examplevar=val
``````

#play

This represents the opening block of a typical `Ansible` play

``````#play
name: Hello World
hosts: all
gather_facts: false
``````

This is the default cell type if no type is given for the first line

``````#task
debug:
``````
``````#task
shell: cat /tmp/afile
register: output
``````

#host_vars

This takes an argument that represents the hostname. Variables defined in this file will be available in the tasks for that host.

``````#host_vars Host1
hostname: host1
``````

#group_vars

This takes an argument that represents the group name. Variables defined in this file will be available in the tasks for hosts in that group.

``````#group_vars BranchOfficeX
gateway: 192.168.1.254
``````

#vars

This takes an argument that represents the filename for use in later cells

``````#vars example_vars
message: hello vars
``````
``````#play
name: hello world
hosts: localhost
gather_facts: false
vars_files:
- example_vars
``````

#template

This takes an argument in order to create a templated file that can be used in later cells

``````#template hello.j2
{{ message }}
``````
``````#task
template:
src: hello.j2
dest: /tmp/hello
``````

#ansible.cfg

Provides overrides typically found in ansible.cfg

``````#ansible.cfg
[defaults]
host_key_checking=False
``````

Examples

You can find various example notebooks in the repository

Using the development environment

It's possible to use whatever python development process you feel comfortable with. The repository itself includes mechanisms for using pipenv

``````pipenv install
...
pipenv shell``````

Author: ansible
Source Code:  https://github.com/ansible/ansible-jupyter-kernel

1633583160

PythonでのCeleryを使用した非同期タスク

• より多くのリクエストを処理する機能
• I / Oバウンドメソッドの並列実行。
• 応答性が向上しました。

範囲

このチュートリアルでは、新しい要求に応答するWebサーバーの機能を制限する長時間実行タスクを処理するWebアプリケーションを構築するときに発生する一般的な落とし穴の1つを克服する方法を説明します。

このチュートリアルでは、コルーチンを使用してコードを同時に実行できるasyncioPythonの組み込みライブラリについては説明していません。

前提条件

Redis：は、オープンソースの高度なKey-Valueストアであり、高性能でスケーラブルなWebアプリケーションを構築するための適切なソリューションです。それを際立たせる3つの主な特徴があります：

Redisはデータベースを完全にメモリに保持し、永続性のためにのみディスクを使用します。

• Redisは、任意の数のスレーブにデータを複製できます。

Redisのインストールは、このチュートリアルの範囲外です。ただし、Windowsマシンにインストールするには、このクイックガイドに従うことをお勧めします。

LinuxまたはmacOSを使用している場合は、以下のコマンドのいずれかを実行すると、Redisがセットアップされます。

Ubuntu / Debian：

``\$ sudo apt-get install redis-server``

マックOS：

``````\$ brew install redis
\$ brew services start redis``````

セロリ：Pythonの世界で最も人気のあるバックグラウンドジョブマネージャーの1人です。リアルタイム操作に重点を置いていますが、スケジューリングもサポートしています。RedisやRabbitMQなどのいくつかのメッセージブローカーと互換性があり、プロデューサーとコンシューマーの両方として機能できます。

`requirements.txt` ファイルにCeleryをインストールし ます。

• Socket.IO：リアルタイムWebアプリケーション用のJavaScriptライブラリです。これにより、Webクライアントとサーバー間のリアルタイムの双方向通信が可能になります。これには、ブラウザで実行されるクライアント側ライブラリとサーバー側ライブラリの2つの部分があります。

食欲をそそる

このチュートリアルでは、スキャフォールディング手法を採用し、同期通信と非同期通信の違い、および非同期通信のバリエーションを理解するために、一連のさまざまなシナリオについて説明します。

このチュートリアルに興味をそそられ、すぐにコードに飛び込みたいと思う 場合は、この記事で使用されているコードについて、このGithubリポジトリにアクセス してください。

アプリケーションスケルトンの作成

• `app_sync.py` 同期通信を紹介するプログラム 。
• `app_async1.py` クライアントがポーリングメカニズムを使用してサーバー側プロセスのフィードバックを要求する可能性がある非同期サービス呼び出しを示すプログラム 。
• `app_async2.py` クライアントへの自動フィードバックを伴う非同期サービス呼び出しを示すプログラム 。
• `app_async3.py` クライアントへの自動フィードバックを伴う、スケジュール後の非同期サービス呼び出しを示すプログラム 。

セットアップに飛び込みましょう。もちろん 、システムにPython3をインストールする必要があり ます。私は必要なライブラリをインストールする仮想環境を使用します（そしてあなたも間違いなくそうするべきです）：

``````\$ python -m venv async-venv
\$ source async-venv/bin/activate``````

``````Flask==1.1.2
Celery==4.4.7
redis==3.5.3
gevent==21.1.2
gevent-websocket==0.10.1
flower==0.9.7``````

``\$ pip install -r requirements.txt``

このチュートリアルを終了すると、フォルダー構造は次のようになります。

それがクリアされたので、実際のコードを書き始めましょう。

まず、アプリケーションの構成パラメータを次のように定義します `config.py`

``````#config.py
#Application configuration File
################################

#Secret key that will be used by Flask for securely signing the session cookie
# and can be used for other security related needs
SECRET_KEY = 'SECRET_KEY'

#Map to REDIS Server Port
BROKER_URL = 'redis://localhost:6379'

#Minimum interval of wait time for our task
MIN_WAIT_TIME = 1
#Maximum interval of wait time for our task
MAX_WAIT_TIME = 20
``````

``````#init.py

app.secret_key = app.config['SECRET_KEY']
app.config.from_object("config")

#Setup the Flask SocketIO integration (Required only for asynchronous scenarios)
socketio = SocketIO(app,logger=True,engineio_logger=True,message_queue=app.config['BROKER_URL'])``````

シナリオ1：同期通信の紹介

コーディングに入る前に、同期通信について簡単に説明します。

それでは、コーディングに取り掛かりましょう。Flaskがレンダリングできるテンプレート（`index.html`）を作成し、その中に次のHTMLコードを含めます。

`templates/index.html`

``````<!DOCTYPE html>
<html>
<title>Synchronicity versus Asynchronicity</title>
<script src="{{ url_for('static',filename='js/jquery.min.js') }}"></script>
<script src="{{ url_for('static',filename='js/socket.io.js') }}"></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<body class="container">
<div class="row">
<h5>Click to start a post scheduled ansycnhronous task with automatic feedback.</h5>
</div>
<div class="card-panel">
<div>
<input id="duration" name="duration" placeholder="Enter duration in seconds. for example: 30" type="text">
<label for="duration">Duration</label>
</div>
<button style="height:50px;width:600px" type="submit" id="runTask">Run A Post Scheduled Asynchronous Task With Automatic Feedback</button>
</form>
</div>
<div class="row">
<div id="Messages" class="red-text" style="width:800px; height:400px; overflow-y:scroll;"></div>
</div>
<script>
var url = 'http://' + document.domain + ':' + location.port + namespace;
var socket = io.connect(url);
socket.on('connect', function() {
socket.emit('join_room');
});

socket.on('msg' , function(data) {
\$("#Messages").prepend('<li>'+data.msg+'</li>');
});

socket.on('status', function(data) {
if (data.msg == 'End') {
};
});
});
</script>
<script>
\$("#Messages").empty();
\$.ajax({ type: "Post"
, success: function(data) {
\$("#Messages").empty();
\$("#Messages").prepend('<li>The Task ' + data.taskid + ' has been submitted and will execute in ' + data.duration + ' seconds. </li>');
}
});
e.preventDefault();
});
</script>
</body>
</html>``````

このテンプレートには次のものが含まれます。

• `runTask`ルートを使用してサーバーにタスクを送信するボタン`/runSyncTask`
• 結果は`div`idでに配置され`Messages`ます。

• `"/"`Webページをレンダリングします（`index.html`
• `"/runSyncTask"`1〜20秒の乱数を生成し、反復ごとに1秒間スリープするループを実行する、長時間実行中のタスクをシミュレートします。
``````#app_sync.py
from random import randint
from init import app

#Render the predefined template index.html
@app.route("/",methods=['GET'])
def index():
return render_template('index.html')

#Defining the route for running A Synchronous Task
#Generate a random number between MIN_WAIT_TIME and MAX_WAIT_TIME
n = randint(app.config['MIN_WAIT_TIME'],app.config['MAX_WAIT_TIME'])
#Return the random wait time generated
return jsonify({ 'waittime': n })

if __name__ == "__main__":
app.run(debug=True)``````

このチュートリアルで定義されているすべてのタスクのコアロジックは、プログラム内にあります`tasks.py`

``````#tasks.py
import time
from celery import Celery
import config

# Setup the logger (compatible with celery version 4)

# Setup the celery client
celery = Celery(__name__)
# Load celery configurations from celeryconfig.py
celery.config_from_object("celeryconfig")

# Setup and connect the socket instance to Redis Server
socketio = SocketIO(message_queue=config.BROKER_URL)

###############################################################################
print(f"This task will take {n} seconds.")
for i in range(n):
print(f"i = {i}")
time.sleep(1)
###############################################################################
print(f"The task of session {session}  will take {n} seconds.")
for i in range(n):
print(f"i = {i}")
time.sleep(1)
###############################################################################
def send_message(event, namespace, room, message):
print("Message = ", message)
socketio.emit(event, {'msg': message}, namespace=namespace, room=room)

room      = data['sessionid']
namespace = data['namespase']
n         = data['waittime']

#Send messages signaling the lifecycle of the task
send_message('status', namespace, room, 'Begin')
send_message('msg', namespace, room, 'This task will take {} seconds'.format(n))

print(f"This task will take {n} seconds.")
for i in range(n):
msg = f"{i}"
send_message('msg', namespace, room, msg )
time.sleep(1)

send_message('status', namespace, room, 'End')
###############################################################################
room      = data['sessionid']
namespace = data['namespase']
n         = data['waittime']

send_message('status', namespace, room, 'Begin')
send_message('msg'   , namespace, room, 'This task will take {} seconds'.format(n))

print(f"This task will take {n} seconds.")
for i in range(n):
msg = f"{i}"
send_message('msg', namespace, room, msg )
time.sleep(1)

send_message('status', namespace, room, 'End')
###############################################################################
``````

このセクションでは、この`long_sync_task()`関数を同期タスクとしてのみ使用します。

`app_sync.py`プログラムを実行して、同期シナリオをテストしてみましょう。

``\$ python app_sync.py``

`http://localhost:5000`Flaskインスタンスが実行されているリンクにアクセスすると、次の出力が表示されます。

「同期タスクの実行」ボタンを押して、プロセスが完了するまで待ちます。

シナリオ2：ポーリングメカニズムを使用した非同期サービス呼び出しの表示

このセクションでは、クライアントがポーリングメカニズムを使用してサーバー側プロセスのフィードバックを要求する可能性のある非同期サービス呼び出しを示します。

ポーリングは潜在的に高いネットワーク負荷を引き起こすため、お勧めしません。それでも、サービスプロバイダー（サーバー）がクライアントについて知る必要がないという利点があります。

それでは、コーディングに移りましょう。構成ファイルを使用して、セロリの初期化パラメーターを定義します`celeryconfig.py`

``````#celeryconfig.py
#Celery Configuration parameters
#Map to Redis server
broker_url = 'redis://localhost:6379/0'

#Backend used to store the tasks results
result_backend = 'redis://localhost:6379/0'

#A string identifying the default serialization to use Default json
result_serializer = 'json'
accept_content = ['json']

#When set to false the local system timezone is used.
enable_utc = False

#To track the started state of a task, we should explicitly enable it

#Configure Celery to use a specific time zone.
#The timezone value can be any time zone supported by the pytz library
#timezone = 'Asia/Beirut'
#enable_utc = True
``````

Flaskがレンダリングできるテンプレートを作成します（`index1.html`）：

``````<!DOCTYPE html>
<html>
<title>Synchronicity versus Asynchronicity</title>
<script src="{{ url_for('static',filename='js/jquery.min.js') }}"></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<body class="container">
<div class="row">
<h4>Click to start an ansycnhronous task</h4>
</div>
<div class="card-panel">
</form>
</form>
</div>
<div class="row">
<div id="Messages" class="red-text" style="width:800px; height:400px; overflow-y:scroll;"></div>
</div>
<script>
\$("*").css("cursor","wait");
\$("#Messages").empty();
\$.ajax({ type: "Post"
, success: function(data) {
\$("*").css("cursor","");
\$("#Messages").append('The task ' + data.taskid + ' will be executed in asynchronous manner for ' + data.waittime + ' seconds...');
}
});
e.preventDefault();
});
var msg = \$("#Messages").text();
//Get The Task ID from The Messages div and create a Target URL
\$.ajax({ type: "Post"
, url: vurl
, success: function(data) {
\$("*").css("cursor","");
\$("#Messages").append('<p> The Status of the task = ' + data.taskid + ' is ' + data.taskstatus + '</p>');
}
});
e.preventDefault();
});
</script>
</body>
</html>``````

``````#app_async1.py
from flask import render_template, jsonify, session,request
from random import randint
import uuid
from init import app
from celery.result import AsyncResult

@app.route("/",methods=['GET'])
def index():
# create a unique ID to assign for the asynchronous task
if 'uid' not in session:
sid = str(uuid.uuid4())
session['uid'] = sid
print("Session ID stored =", sid)
return render_template('index1.html')

#Generate a random number between MIN_WAIT_TIME and MAX_WAIT_TIME
n = randint(app.config['MIN_WAIT_TIME'],app.config['MAX_WAIT_TIME'])
sid = str(session['uid'])

#Get The Result of The Asynchronous Task
def result():
# grab the AsyncResult
# print the Asynchronous result status

if __name__ == "__main__":
app.run(debug=True)``````

このプログラムには、3つの主要なルートがあります。

• `"/"`：Webページをレンダリングします（`index1.html`）。
• `"/runAsyncTask"`：1〜20秒の乱数を生成する非同期タスクを呼び出してから、反復ごとに1秒間スリープするループを実行します。
• `"/getAsyncTaskResult"`：受信したタスクIDに基づいて、タスクの状態を収集します。

このシナリオをテストして、次の手順に従ってパスを進めましょう。

• Redisサーバーを起動します。Windowsでは、Redisがインストールされているフォルダーを見つけて、をダブルクリックし`redis-server.exe`ます。デフォルトのインストールまたはLinux / MacOSの場合は、RedisインスタンスがTCPポート6379で実行されていることを確認してください。
• Celeryワーカーを起動します。Windowsでは、コマンドプロンプトを開き、プロジェクトフォルダーに移動して、次のコマンドを実行します。
``\$ async-venv\Scripts\celery.exe worker -A tasks --loglevel=DEBUG --concurrency=1 -P solo -f celery.logs``

Linux / MacOSでは、非常によく似ています。

``\$ async-venv/bin/celery worker -A tasks --loglevel=DEBUG --concurrency=1 -P solo -f celery.logs``

これ`async-venv`が仮想環境の名前であることに注意してください。別の名前を付けた場合は、必ず自分の名前に置き換えてください。セロリが始まると、次の出力が表示されます。

プログラムで定義されたタスク`tasks.py`がCeleryに反映されていることを確認してください。

• ターミナルウィンドウを開き、メインプログラムを起動します。
``\$ python app_async1.py​``

[非同期タスクの実行]ボタンを押すと、新しいタスクがキューに入れられ、直接実行されます。「メッセージ」セクションに、タスクのIDとその実行時間を示すメッセージが表示されます。

[非同期タスク結果の取得]ボタンを（継続的に）押すと、その特定の時間におけるタスクの状態が収集されます。

セロリタスクの組み込み状態は次のとおりです。

• `PENDING`：実行を待機しています。
• `STARTED`：タスクが開始されました。
• `SUCCESS`：タスクは正常に実行されました。
• `FAILURE`：タスクの実行により例外が発生しました。
• `RETRY`：タスクは再試行されています。
• `REVOKED`：タスクが取り消されました。

ログファイルに含まれているセロリワーカーのログ`celery.logs`を確認すると、タスクのライフサイクルに気付くでしょう。

シナリオ3：自動フィードバックを使用した非同期サービス呼び出しの表示

これがもたらす主な利点は、ネットワークの負荷を軽減し、膨大な数のクライアントに情報を伝達するためにより効率的になることです。

さらに掘り下げる前に、実行する手順について簡単に説明します。

セロリからWebブラウザーにメッセージを送り返すことができるようにするために、以下を活用します。

• TのWebSocket接続を可能にするための使いやすいJavaScriptライブラリであるSocket.ioの彼の能力。

データ接続を効果的に管理するために、次の区分化戦略を採用します。

• `"/runAsyncTaskF"`このシナリオに名前空間を割り当てます。（名前空間は、単一の共有接続を介してサーバーロジックを分離するために使用されます）。
• W eは、各ユーザーセッションのための部屋を割り当てます。（部屋は名前空間のサブディビジョンまたはサブチャネルです）。

それでは、コーディングに移りましょう。

• Flaskがレンダリングできるテンプレートを作成します（`index2.html`）：
``````<!DOCTYPE html>
<html>
<title>Synchronicity versus Asynchronicity</title>
<script src="{{ url_for('static',filename='js/jquery.min.js') }}"></script>
<script src="{{ url_for('static',filename='js/socket.io.js') }}"></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<body class="container">
<div class="row">
<h5>Click to start an ansycnhronous task with automatic feedback.</h5>
</div>
<div class="card-panel">
</form>
</div>
<div class="row">
<div id="Messages" class="red-text" style="width:800px; height:400px; overflow-y:scroll;"></div>
</div>
<script>
var url = 'http://' + document.domain + ':' + location.port + namespace;
var socket = io.connect(url);
socket.on('connect', function() {
socket.emit('join_room');
});
socket.on('msg', function(data) {
\$("#Messages").prepend('<li>' + data.msg + '</li>');
});
socket.on('status', function(data) {
if (data.msg == 'End') {
};
});
});
</script>
<script>
\$("*").css("cursor", "wait");
\$("#Messages").empty();

\$.ajax({
type: "Post",
success: function(data) {
\$("*").css("cursor", "");
\$("#Messages").empty();
}
});
e.preventDefault();
});
</script>
</body>
</html>``````

`app_async2.py`Flaskアプリケーションを含むと呼ばれるプログラムを作成します。

``````#Gevent is a coroutine based concurrency library for Python
from gevent import monkey
#For dynamic modifications of a class or module
monkey.patch_all()
from flask import render_template, jsonify, session, request
from random import randint
import uuid
from init import app, socketio

@app.route("/",methods=['GET'])
def index():
# create a unique session ID and store it within the Flask session
if 'uid' not in session:
sid = str(uuid.uuid4())
session['uid'] = sid
print("Session ID stored =", sid)
return render_template('index2.html')

#Run an Asynchronous Task With Automatic Feedback
# Generate a random number between MIN_WAIT_TIME and MAX_WAIT_TIME
n = randint(app.config['MIN_WAIT_TIME'], app.config['MAX_WAIT_TIME'])

data = {}
data['sessionid'] = str(session['uid'])
data['waittime']  = n

,'sessionid':data['sessionid']
,'waittime':data['waittime']
,'namespace':data['namespase']
})

def socket_connect():
#Display message upon connecting to the namespace
print('Client Connected To NameSpace /runAsyncTaskF - ',request.sid)

def socket_connect():
# Display message upon disconnecting from the namespace
print('Client disconnected From NameSpace /runAsyncTaskF - ',request.sid)

def on_room():
room = str(session['uid'])
# Display message upon joining a room specific to the session previously stored.
print(f"Socket joining room {room}")
join_room(room)

@socketio.on_error_default
def error_handler(e):
# Display message on error.
print(f"socket error: {e}, {str(request.event)}")

if __name__ == "__main__":
# Run the application with socketio integration.
socketio.run(app,debug=True)``````

このプログラムには、主に2つのルートがあります。

• `"/"`：Webページをレンダリングします（`index2.html`）。
• `"/runAsyncTaskF"`：以下を実行する非同期タスクを呼び出します。
• 1〜20秒の乱数を生成します。
• `long_async_taskf()` プログラム内のそれぞれのタスクを呼び出します`tasks.py`

このシナリオを実行するには：

• Redisサーバーを起動します。
• セロリワーカーを起動します。
• 走る `app_async2.py`

ブラウザを開き、次のリンクにアクセスしてボタンを押すと、次のような出力が徐々に表示されます。

また`celery.logs`、タスクのライフサイクルについてファイルをいつでも確認できます。

シナリオ4：自動フィードバックを使用してポートでスケジュールされた非同期サービスコールを表示する

このシナリオはシナリオ3に似ています。唯一の違いは、非同期タスクを直接実行する代わりに、このタスクがクライアントによって指定された特定の期間の後に実行されるようにスケジュールされることです。

コーディングに進みましょう。非同期タスクを実行する前に待機する時間を秒単位で表す`index3.html`新しいフィールドを使用してテンプレートを作成し`"Duration"`ます。

``````<!DOCTYPE html>
<html>
<title>Synchronicity versus Asynchronicity</title>
<script src="{{ url_for('static',filename='js/jquery.min.js') }}"></script>
<script src="{{ url_for('static',filename='js/socket.io.js') }}"></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<body class="container">
<div class="row">
<h5>Click to start a post scheduled ansycnhronous task with automatic feedback.</h5>
</div>
<div class="card-panel">
<div>
<input id="duration" name="duration" placeholder="Enter duration in seconds. for example: 30" type="text">
<label for="duration">Duration</label>
</div>
<button style="height:50px;width:600px" type="submit" id="runTask">Run A Post Scheduled Asynchronous Task With Automatic Feedback</button>
</form>
</div>
<div class="row">
<div id="Messages" class="red-text" style="width:800px; height:400px; overflow-y:scroll;"></div>
</div>
<script>
var url = 'http://' + document.domain + ':' + location.port + namespace;
var socket = io.connect(url);
socket.on('connect', function() {
socket.emit('join_room');
});
socket.on('msg', function(data) {
\$("#Messages").prepend('<li>' + data.msg + '</li>');
});
socket.on('status', function(data) {
if (data.msg == 'End') {
};
});
});
</script>
<script>
\$("#Messages").empty();
\$.ajax({
type: "Post",
success: function(data) {
\$("#Messages").empty();
\$("#Messages").prepend('<li>The Task ' + data.taskid + ' has been submitted and will execute in ' + data.duration + ' seconds. </li>');
}
});
e.preventDefault();
});
</script>
</body>
</html>``````

``````#app_async3.py
from gevent import monkey
monkey.patch_all()

from flask import render_template, jsonify, session, request
from random import randint
import uuid
from init import app, socketio

@app.route("/",methods=['GET'])
def index():
# create a unique session ID
if 'uid' not in session:
sid = str(uuid.uuid4())
session['uid'] = sid
print("Session ID stored =", sid)
return render_template('index3.html')

#Run a Post Scheduled Asynchronous Task With Automatic Feedback
# Generate a random number between MIN_WAIT_TIME and MAX_WAIT_TIME
n = randint(app.config['MIN_WAIT_TIME'], app.config['MAX_WAIT_TIME'])
data = {}
data['sessionid'] = str(session['uid'])
data['waittime']  = n
data['duration']  = int(request.form['duration'])
#Countdown represents the duration to wait in seconds before running the task
,'sessionid':data['sessionid']
,'waittime': data['waittime']
,'namespace':data['namespase']
,'duration':data['duration']
})

def socket_connect():
print('Client Connected To NameSpace /runPSATask - ',request.sid)

def socket_connect():
print('Client disconnected From NameSpace /runPSATask - ',request.sid)

def on_room():
room = str(session['uid'])
print(f"Socket joining room {room}")
join_room(room)

@socketio.on_error_default
def error_handler(e):
print(f"socket error: {e}, {str(request.event)}")

if __name__ == "__main__":
socketio.run(app,debug=True)``````

`app_async3.py`以前と同じように実行し、ブラウザを開きます。

また、`celery.logs`ログファイルに含まれているセロリワーカーのログを復元すると、タスクのライフサイクルに気付くでしょう。

付録：セロリの監視に花を使用する

セロリタスクをより適切に監視するために、セロリ クラスターを監視および管理するためのWebベースのツールであるFlowerをインストールできます。

• 以前と同じようにRedisサーバーを起動します。
• 以前と同じようにCeleryワーカーを起動します。
• Windowsでは、コマンドを使用してflowerを開始します。
``\$ async-venv\Scripts\flower.exe worker -A tasks --port=5555``

Linux / MacOSの場合：

``\$ async-venv/bin/flower worker -A tasks --port=5555``

コンソールに次の情報が表示されます。

アプリに戻ってタスクを実行し、ブラウザを開いて`http://localhost:5555`[タスク]タブに移動します。

タスクが完了すると、フラワーダッシュボードに次のように表示されます。

結論

この記事が、Celeryの助けを借りて同期および非同期リクエストの概念的な基礎を得るのに役立つことを願っています。同期要求は遅くなる可能性があり、非同期要求は迅速に実行されますが、あらゆるシナリオに適切な方法を認識することが重要です。時には、彼らは一緒に働くことさえあります。

1650009780

Learn CRUD Operations in JavaScript by Building TODO APP

Today we're gonna learn how to do CRUD Operations in JavaScript by making a Todo App. Let's get started 🔥

This is the app we're making today:

What is CRUD?

CRUD stands for -

• C: Create
• U: Update
• D: Delete

CRUD is a type of mechanism that allows you to create data, read data, edit it, and delete those data. In our case, we're gonna make a Todo app, so we will have 4 options to create tasks, read tasks, update tasks, or delete tasks.

Understanding CRUD Principles

Before starting the tutorial, first, let's understand the CRUD principles. For that, let's create a very very simple Social Media Application.

Setup

For this project, we will be following these steps below:

• Create 3 files named index.html, style.css, and main.js
• Link the JavaScript and CSS file to index.html

HTML

Inside the body tag, create a div with a class name `.container`. There, we will have 2 sections, `.left` and `.right` 👇

``````<body>
<h1>Social Media App</h1>
<div class="container">

<div class="left"></div>
<div class="right"></div>

</div>
</body>
``````

On the left side, we will create our posts. On the right side, we can see, update, and delete our posts. Now, create a form inside the .left div tag 👇

``````<div class="left">
<form id="form">
<label for="post"> Write your post here</label>
<br><br>
<textarea name="post" id="input" cols="30" rows="10"></textarea>
<br> <br>
<div id="msg"></div>
<button type="submit">Post</button>
</form>
</div>
``````

Write this code inside the HTML so that we can display our post on the right side 👇

``````<div class="right">
<div id="posts"></div>
</div>
``````

Next, we'll insert the font-awesome CDN inside the head tag to use its fonts in our project 👇

``````<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css" />
``````

Now, we're gonna make some sample posts with delete and edit icons. Write this code inside the div with the id #posts: 👇

``````<div id="posts">
<div>
<p>Hello world post 1</p>
<span class="options">
<i class="fas fa-edit"></i>
<i class="fas fa-trash-alt"></i>
</span>
</div>

<div >
<p>Hello world post 2</p>
<span class="options">
<i class="fas fa-edit"></i>
<i class="fas fa-trash-alt"></i>
</span>
</div>
</div>
``````

The result so far looks like this:

CSS

Let's keep it simple. Write these styles inside your stylesheet: 👇

``````body {
font-family: sans-serif;
margin: 0 50px;
}

.container {
display: flex;
gap: 50px;
}

#posts {
width: 400px;
}

i {
cursor: pointer;
}
``````

Now, write these styles for the post div and option icons: 👇

``````#posts div {
display: flex;
align-items: center;
justify-content: space-between;
}

.options {
display: flex;
gap: 25px;
}

#msg {
color: red;
}
``````

The results so far look like this:👇

JavaScript Part

According to this flow chart, we will go forward with the project. Don't worry, I'll explain everything along the way. 👇

Form Validation

First, let's target all the ID selectors from the HTML in JavaScript. Like this: 👇

``````let form = document.getElementById("form");
let input = document.getElementById("input");
let msg = document.getElementById("msg");
let posts = document.getElementById("posts");
``````

Then, build a submit event listener for the form so that it can prevent the default behaviour of our App. At the same time, we will create a function named `formValidation`. 👇

``````form.addEventListener("submit", (e) => {
e.preventDefault();
console.log("button clicked");

formValidation();
});

let formValidation = () => {};
``````

Now, we're gonna make an if else statement inside our `formValidation` function. This will help us prevent users from submitting blank input fields. 👇

``````let formValidation = () => {
if (input.value === "") {
msg.innerHTML = "Post cannot be blank";
console.log("failure");
} else {
console.log("successs");
msg.innerHTML = "";
}
};
``````

Here's the result so far: 👇

As you can see, a message will also show up if the user tries to submit the form blank.

How to accept data from input fields

Whatever data we get from the input fields, we will store them in an object. Let's create an object named `data`. And, create a function named `acceptData`: 👇

``````let data = {};

let acceptData = () => {};
``````

The main idea is that, using the function, we collect data from the inputs and store them in our object named `data`. Now let's finish building our `acceptData` function.

``````let acceptData = () => {
data["text"] = input.value;
console.log(data);
};
``````

Also, we need the `acceptData` function to work when the user clicks the submit button. For that, we will fire this function in the else statement of our `formValidation` function. 👇

``````let formValidation = () => {
if (input.value === "") {
// Other codes are here
} else {
// Other codes are here
acceptData();
}
};
``````

When we input data and submit the form, on the console we can see an object holding our user's input values. Like this: 👇

How to create posts using JavaScript template literals

In order to post our input data on the right side, we need to create a div element and append it to the posts div. First, let's create a function and write these lines: 👇

``````let createPost = () => {
posts.innerHTML += ``;
};
``````

The backticks are template literals. It will work as a template for us. Here, we need 3 things: a parent div, the input itself, and the options div which carries the edit and delete icons. Let's finish our function 👇

``````let createPost = () => {
posts.innerHTML += `
<div>
<p>\${data.text}</p>
<span class="options">
<i onClick="editPost(this)" class="fas fa-edit"></i>
<i onClick="deletePost(this)" class="fas fa-trash-alt"></i>
</span>
</div>
`;
input.value = "";
};
``````

In our `acceptdata` function, we will fire our `createPost` function. Like this: 👇

``````let acceptData = () => {
// Other codes are here

createPost();
};
``````

The result so far: 👇

So far so good guys, we're almost done with project 1.

How to delete a post

In order to delete a post, first of all, let's create a function inside our javascript file:

``````let deletePost = (e) => {};
``````

Next up, we fire this `deletePost` function inside all of our delete icons using an onClick attribute. You'll write these lines in HTML and on the template literal. 👇

``````<i onClick="deletePost(this)" class="fas fa-trash-alt"></i>
``````

The `this` keyword will refer to the element that fired the event. in our case, the `this` refers to the delete button.

Look carefully, the parent of the delete button is the span with class name options. The parent of the span is the div. So, we write `parentElement` twice so that we can jump from the delete icon to the div and target it directly to remove it.

Let's finish our function. 👇

``````let deletePost = (e) => {
e.parentElement.parentElement.remove();
};
``````

The result so far: 👇

How to edit a post

In order to edit a post, first of all, let's create a function inside our JavaScript file:

``````let editPost = (e) => {};
``````

Next up, we fire this `editPost` function inside all of our edit icons using an onClick attribute. You'll write these lines in HTML and on the template literal. 👇

``````<i onClick="editPost(this)" class="fas fa-edit"></i>
``````

The `this` keyword will refer to the element that fired the event. In our case, the `this` refers to the edit button.

Look carefully, the parent of the edit button is the span with class name options. The parent of the span is the div. So, we write `parentElement` twice so that we can jump from the edit icon to the div and target it directly to remove it.

Then, whatever data is inside the post, we bring it back on the input field to edit it.

Let's finish our function. 👇

``````let editPost = (e) => {
input.value = e.parentElement.previousElementSibling.innerHTML;
e.parentElement.parentElement.remove();
};
``````

The result so far: 👇

Take a Break!

Congratulations everyone for completing project 1. Now, take a small break!

How to Make a To-Do App using CRUD Operations

Let's start making project 2, which is a To-Do App.

Project Setup

For this project, we will be following these steps below:

• Create 3 files named index.html, style.css, and main.js
• Link the JavaScript and CSS files to index.html
• Start our live server

HTML

Write this starter code inside the HTML file: 👇

``````<div class="app">
<h4 class="mb-3">TODO App</h4>

<i class="fas fa-plus"></i>
</div>
</div>
``````

The div with an id `addNew` is the button that will open the modal. The span will be displayed on the button. The `i` is the icon from font-awesome.

We're going to use bootstrap to make our modal. We'll use the modal to add new tasks. For that, add the bootstrap CDN link inside the head tag. 👇

``````<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"
crossorigin="anonymous"
/>

<script
src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"
integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p"
crossorigin="anonymous"
></script>
``````

To see the created tasks, we'll use a div with an id tasks, inside the div with the classname app. 👇

``````<h5 class="text-center my-3">Tasks</h5>

``````

Insert the font-awesome CDN inside the head tag to use fonts in our project 👇

``````<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css" />
``````

Copy and paste the code below which are from the bootstrap modal. It carries a form with 3 input fields and a submit button. If you want then you can search Bootstrap's website by writing 'modal' in the search bar.

``````<!-- Modal -->
<form
id="form"
tabindex="-1"
aria-labelledby="exampleModalLabel"
aria-hidden="true"
>
<div class="modal-dialog">
<div class="modal-content">
<button
type="button"
class="btn-close"
data-bs-dismiss="modal"
aria-label="Close"
></button>
</div>
<div class="modal-body">
<input type="text" class="form-control" name="" id="textInput" />
<div id="msg"></div>
<br />
<p>Due Date</p>
<input type="date" class="form-control" name="" id="dateInput" />
<br />
<p>Description</p>
<textarea
name=""
class="form-control"
id="textarea"
cols="30"
rows="5"
></textarea>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
Close
</button>
</div>
</div>
</div>
</form>
``````

The result so far: 👇

We're done with the HTML file setup. Let's start the CSS.

CSS

Add these styles in the body so that we can keep our app at the exact center of the screen.

``````body {
font-family: sans-serif;
margin: 0 50px;
background-color: #e5e5e5;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
``````

Let's style the div with the classname app. 👇

``````.app {
background-color: #fff;
width: 300px;
height: 500px;
border: 5px solid #abcea1;
}
``````

The result so far: 👇

Now, let's style the button with the id `addNew`. 👇

``````#addNew {
display: flex;
justify-content: space-between;
align-items: center;
background-color: rgba(171, 206, 161, 0.35);
cursor: pointer;
}
.fa-plus {
background-color: #abcea1;
}
``````

The result so far: 👇

If you click on the button, the modal pops up like this: 👇

In the JavaScript file, first of all, select all the selectors from the HTML that we need to use. 👇

``````let form = document.getElementById("form");
let textInput = document.getElementById("textInput");
let dateInput = document.getElementById("dateInput");
let textarea = document.getElementById("textarea");
let msg = document.getElementById("msg");
``````

Form Validations

We cannot let a user submit blank input fields. So, we need to validate the input fields. 👇

``````form.addEventListener("submit", (e) => {
e.preventDefault();
formValidation();
});

let formValidation = () => {
if (textInput.value === "") {
console.log("failure");
msg.innerHTML = "Task cannot be blank";
} else {
console.log("success");
msg.innerHTML = "";
}
};
``````

Also, add this line inside the CSS:

``````#msg {
color: red;
}
``````

The result so far: 👇

As you can see, the validation is working. The JavaScript code doesn't let the user submit blank input fields, otherwise you're gonna see an error message.

How to collect data and use local storage

Whatever inputs the user writes, we need to collect them and store them in local storage.

First, we collect the data from the input fields, using the function named `acceptData` and an array named `data`. Then we push them inside the local storage like this: 👇

``````let data = [];

let acceptData = () => {
data.push({
text: textInput.value,
date: dateInput.value,
description: textarea.value,
});

localStorage.setItem("data", JSON.stringify(data));

console.log(data);
};
``````

Also note that this will never work unless you invoke the function `acceptData` inside the else statement of the form validation. Follow along here: 👇

``````let formValidation = () => {

// Other codes are here
else {

// Other codes are here

acceptData();
}
};
``````

You may have noticed that the modal doesn't close automatically. To solve this, write this small function inside the else statement of the form validation: 👇

``````let formValidation = () => {

// Other codes are here
else {

// Other codes are here

acceptData();

(() => {
})();
}
};
``````

If you open Chrome dev tools, go to the application and open the local storage. You can see this result: 👇

In order to create a new task, we need to create a function, use template literals to create the HTML elements, and use a map to push the data collected from the user inside the template. Follow along here: 👇

``````let createTasks = () => {
data.map((x, y) => {
<div id=\${y}>
<span class="fw-bold">\${x.text}</span>
<span class="small text-secondary">\${x.date}</span>
<p>\${x.description}</p>

<span class="options">
<i onClick= "editTask(this)" data-bs-toggle="modal" data-bs-target="#form" class="fas fa-edit"></i>
</span>
</div>
`);
});

resetForm();
};
``````

Also note that the function will never run unless you invoke it inside the `acceptData` function, like this: 👇

``````let acceptData = () => {
// Other codes are here

};
``````

Once we're done collecting and accepting data from the user, we need to clear the input fields. For that we create a function called `resetForm`. Follow along: 👇

``````let resetForm = () => {
textInput.value = "";
dateInput.value = "";
textarea.value = "";
};
``````

The result so far: 👇

As you can see, there's no styles with the card. Let's add some styles: 👇

``````#tasks {
display: grid;
grid-template-columns: 1fr;
gap: 14px;
}

border: 3px solid #abcea1;
background-color: #e2eede;
display: grid;
gap: 4px;
}
``````

Style the edit and delete buttons with this code: 👇

``````#tasks div .options {
justify-self: center;
display: flex;
gap: 20px;
}

cursor: pointer;
}
``````

The result so far: 👇

Look here carefully, I added 3 lines of code inside the function.

• The first line will delete the HTML element from the screen,
• the second line will remove the targetted Task from the data array,
• and the third line will update the local storage with the new data.
``````let deleteTask = (e) => {
e.parentElement.parentElement.remove();

data.splice(e.parentElement.parentElement.id, 1);

localStorage.setItem("data", JSON.stringify(data));

console.log(data);
};
``````

Now create a dummy task and try to delete it. The result so far looks like this: 👇

Look here carefully, I added 5 lines of code inside the function.

• Line 1 is targetting the task that we selected to edit
• Lines 2, 3, and 4, are targetting the values [task, date, description] of the task that we selected to edit
• line 5 is running the delete function to remove the selected data both from the local storage, HTML element, and data array.
``````let editTask = (e) => {

};
``````

Now, try to create a dummy task and edit it. The result so far: 👇

How to get data from local storage

If you refresh the page, you'll note that all of your data is gone. In order to solve that issue, we run a IIFE (Immediately invoked function expression) to retrieve the data from local storage. Follow along: 👇

``````(() => {
data = JSON.parse(localStorage.getItem("data")) || [];
console.log(data);
})();
``````

Now the data will show up even if you refresh the page.

Conclusion

Congratulations for successfully completing this tutorial. You've learned how to create a todo list application using CRUD operations. Now, you can create your own CRUD application using this tutorial.

1650865080

CRUD Trong JavaScript Bằng Cách Xây Dựng Ứng Dụng TODO

Hôm nay chúng ta sẽ học cách thực hiện các Thao tác CRUD trong JavaScript bằng cách tạo Ứng dụng Todo. Bắt đầu thôi 🔥

Đây là ứng dụng chúng tôi tạo ra hôm nay:

CRUD là gì?

CRUD là viết tắt của -

• C: Create
• U: Update
• D: Delete

CRUD là một loại cơ chế cho phép bạn tạo dữ liệu, đọc dữ liệu, chỉnh sửa và xóa các dữ liệu đó. Trong trường hợp của chúng tôi, chúng tôi sẽ tạo một ứng dụng Todo, vì vậy chúng tôi sẽ có 4 tùy chọn để tạo tác vụ, đọc tác vụ, cập nhật tác vụ hoặc xóa tác vụ.

Hiểu các nguyên tắc CRUD

Trước khi bắt đầu hướng dẫn, trước tiên, chúng ta hãy hiểu các nguyên tắc CRUD. Để làm được điều đó, hãy tạo một Ứng dụng Truyền thông Xã hội rất đơn giản.

Cài đặt

Đối với dự án này, chúng tôi sẽ thực hiện theo các bước sau:

• Tạo 3 tệp có tên index.html, style.css và main.js
• Liên kết tệp JavaScript và CSS với index.html
• Khởi động máy chủ trực tiếp của bạn

HTML

Bên trong thẻ body, hãy tạo một div có tên lớp `.container`. Ở đó, chúng ta sẽ có 2 phần, `.left``.right`👇

``````<body>
<h1>Social Media App</h1>
<div class="container">

<div class="left"></div>
<div class="right"></div>

</div>
</body>
``````

Ở phía bên trái, chúng tôi sẽ tạo các bài đăng của chúng tôi. Ở phía bên phải, chúng ta có thể xem, cập nhật và xóa các bài đăng của mình. Bây giờ, hãy tạo một biểu mẫu bên trong thẻ div .left 👇

``````<div class="left">
<form id="form">
<label for="post"> Write your post here</label>
<br><br>
<textarea name="post" id="input" cols="30" rows="10"></textarea>
<br> <br>
<div id="msg"></div>
<button type="submit">Post</button>
</form>
</div>
``````

Viết mã này bên trong HTML để chúng tôi có thể hiển thị bài đăng của mình ở phía bên phải 👇

``````<div class="right">
<div id="posts"></div>
</div>
``````

Tiếp theo, chúng tôi sẽ chèn CDN phông chữ tuyệt vời bên trong thẻ head để sử dụng phông chữ của nó trong dự án của chúng tôi 👇

``````<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css" />
``````

Bây giờ, chúng tôi sẽ tạo một số bài đăng mẫu với các biểu tượng xóa và chỉnh sửa. Viết mã này bên trong div với id #posts: 👇

``````<div id="posts">
<div>
<p>Hello world post 1</p>
<span class="options">
<i class="fas fa-edit"></i>
<i class="fas fa-trash-alt"></i>
</span>
</div>

<div >
<p>Hello world post 2</p>
<span class="options">
<i class="fas fa-edit"></i>
<i class="fas fa-trash-alt"></i>
</span>
</div>
</div>
``````

Kết quả cho đến nay trông như thế này:

CSS

Hãy giữ nó đơn giản. Viết các kiểu này bên trong bảng định kiểu của bạn: 👇

``````body {
font-family: sans-serif;
margin: 0 50px;
}

.container {
display: flex;
gap: 50px;
}

#posts {
width: 400px;
}

i {
cursor: pointer;
}
``````

Bây giờ, hãy viết các kiểu này cho các biểu tượng div và tùy chọn của bài đăng: 👇

``````#posts div {
display: flex;
align-items: center;
justify-content: space-between;
}

.options {
display: flex;
gap: 25px;
}

#msg {
color: red;
}
``````

Kết quả cho đến nay trông như thế này: 👇

Phần JavaScript

Theo biểu đồ này, chúng tôi sẽ tiếp tục với dự án. Đừng lo lắng, tôi sẽ giải thích mọi thứ trên đường đi. 👇

Xác thực biểu mẫu

Đầu tiên, hãy nhắm mục tiêu tất cả các bộ chọn ID từ HTML trong JavaScript. Như thế này: 👇

``````let form = document.getElementById("form");
let input = document.getElementById("input");
let msg = document.getElementById("msg");
let posts = document.getElementById("posts");
``````

Sau đó, xây dựng trình xử lý sự kiện gửi cho biểu mẫu để biểu mẫu có thể ngăn chặn hành vi mặc định của Ứng dụng của chúng tôi. Đồng thời, chúng ta sẽ tạo một hàm có tên `formValidation`. 👇

``````form.addEventListener("submit", (e) => {
e.preventDefault();
console.log("button clicked");

formValidation();
});

let formValidation = () => {};
``````

Bây giờ, chúng ta sẽ tạo một câu lệnh if else bên trong `formValidation`hàm của chúng ta. Điều này sẽ giúp chúng tôi ngăn người dùng gửi các trường nhập trống. 👇

``````let formValidation = () => {
if (input.value === "") {
msg.innerHTML = "Post cannot be blank";
console.log("failure");
} else {
console.log("successs");
msg.innerHTML = "";
}
};
``````

Đây là kết quả cho đến nay: 👇

Như bạn có thể thấy, một thông báo cũng sẽ hiển thị nếu người dùng cố gắng gửi biểu mẫu trống.

Cách chấp nhận dữ liệu từ các trường đầu vào

Bất kỳ dữ liệu nào chúng tôi nhận được từ các trường đầu vào, chúng tôi sẽ lưu trữ chúng trong một đối tượng. Hãy tạo một đối tượng có tên `data`. Và, tạo một hàm có tên `acceptData`: 👇

``````let data = {};

let acceptData = () => {};
``````

Ý tưởng chính là, bằng cách sử dụng hàm, chúng tôi thu thập dữ liệu từ các đầu vào và lưu trữ chúng trong đối tượng được đặt tên của chúng tôi `data`. Bây giờ chúng ta hãy hoàn thành việc xây dựng `acceptData`chức năng của chúng ta.

``````let acceptData = () => {
data["text"] = input.value;
console.log(data);
};
``````

Ngoài ra, chúng ta cần `acceptData`chức năng hoạt động khi người dùng nhấp vào nút gửi. Vì vậy, chúng ta sẽ kích hoạt hàm này trong câu lệnh else của `formValidation`hàm của chúng ta. 👇

``````let formValidation = () => {
if (input.value === "") {
// Other codes are here
} else {
// Other codes are here
acceptData();
}
};
``````

Khi chúng tôi nhập dữ liệu và gửi biểu mẫu, trên bảng điều khiển, chúng tôi có thể thấy một đối tượng chứa các giá trị đầu vào của người dùng của chúng tôi. Như thế này: 👇

Cách tạo bài đăng bằng cách sử dụng các ký tự mẫu JavaScript

Để đăng dữ liệu đầu vào của chúng tôi ở phía bên phải, chúng tôi cần tạo một phần tử div và nối nó vào div bài viết. Đầu tiên, hãy tạo một hàm và viết những dòng sau: 👇

``````let createPost = () => {
posts.innerHTML += ``;
};
``````

Các dấu gạch ngược là các ký tự mẫu. Nó sẽ hoạt động như một mẫu cho chúng tôi. Ở đây, chúng ta cần 3 thứ: div cha, chính đầu vào và div tùy chọn mang các biểu tượng chỉnh sửa và xóa. Hãy hoàn thành chức năng của chúng ta 👇

``````let createPost = () => {
posts.innerHTML += `
<div>
<p>\${data.text}</p>
<span class="options">
<i onClick="editPost(this)" class="fas fa-edit"></i>
<i onClick="deletePost(this)" class="fas fa-trash-alt"></i>
</span>
</div>
`;
input.value = "";
};
``````

Trong `acceptdata`chức năng của chúng tôi, chúng tôi sẽ kích `createPost`hoạt chức năng của mình. Như thế này: 👇

``````let acceptData = () => {
// Other codes are here

createPost();
};
``````

Kết quả cho đến nay: 👇

Cho đến nay, các bạn tốt, chúng ta gần như đã hoàn thành xong dự án 1.

Cách xóa một bài đăng

Để xóa một bài đăng, trước hết, hãy tạo một hàm bên trong tệp javascript của chúng tôi:

``````let deletePost = (e) => {};
``````

Tiếp theo, chúng tôi kích `deletePost`hoạt chức năng này bên trong tất cả các biểu tượng xóa của chúng tôi bằng cách sử dụng thuộc tính onClick. Bạn sẽ viết những dòng này bằng HTML và trên mẫu. 👇

``````<i onClick="deletePost(this)" class="fas fa-trash-alt"></i>
``````

Từ `this`khóa sẽ tham chiếu đến phần tử đã kích hoạt sự kiện. trong trường hợp của chúng tôi, `this`đề cập đến nút xóa.

Hãy xem xét kỹ, cha của nút xóa là khoảng cách với các tùy chọn tên lớp. Cha của span là div. Vì vậy, chúng tôi viết `parentElement`hai lần để chúng tôi có thể chuyển từ biểu tượng xóa sang div và nhắm mục tiêu trực tiếp để xóa nó.

Hãy hoàn thành chức năng của chúng ta. 👇

``````let deletePost = (e) => {
e.parentElement.parentElement.remove();
};
``````

Kết quả cho đến nay: 👇

Cách chỉnh sửa một bài đăng

Để chỉnh sửa một bài đăng, trước hết, hãy tạo một hàm bên trong tệp JavaScript của chúng tôi:

``````let editPost = (e) => {};
``````

Tiếp theo, chúng tôi kích `editPost`hoạt chức năng này bên trong tất cả các biểu tượng chỉnh sửa của chúng tôi bằng cách sử dụng thuộc tính onClick. Bạn sẽ viết những dòng này bằng HTML và trên mẫu. 👇

``````<i onClick="editPost(this)" class="fas fa-edit"></i>
``````

Từ `this`khóa sẽ tham chiếu đến phần tử đã kích hoạt sự kiện. Trong trường hợp của chúng tôi, tham chiếu `this`đến nút chỉnh sửa.

Xem kỹ, cha của nút chỉnh sửa là khoảng cách với các tùy chọn tên lớp. Cha của span là div. Vì vậy, chúng tôi viết `parentElement`hai lần để chúng tôi có thể chuyển từ biểu tượng chỉnh sửa sang div và nhắm mục tiêu trực tiếp để xóa nó.

Sau đó, bất kỳ dữ liệu nào có trong bài đăng, chúng tôi đưa dữ liệu đó trở lại trường đầu vào để chỉnh sửa.

Hãy hoàn thành chức năng của chúng ta. 👇

``````let editPost = (e) => {
input.value = e.parentElement.previousElementSibling.innerHTML;
e.parentElement.parentElement.remove();
};
``````

Kết quả cho đến nay: 👇

Nghỉ ngơi một lát!

Xin chúc mừng mọi người đã hoàn thành dự án 1. Bây giờ, hãy nghỉ ngơi một chút!

Cách tạo ứng dụng việc cần làm bằng Thao tác CRUD

Hãy bắt đầu làm dự án 2, đó là Ứng dụng việc cần làm.

Thiết lập dự án

Đối với dự án này, chúng tôi sẽ thực hiện theo các bước sau:

• Tạo 3 tệp có tên index.html, style.css và main.js
• Liên kết các tệp JavaScript và CSS với index.html
• Khởi động máy chủ trực tiếp của chúng tôi

HTML

Viết mã khởi động này bên trong tệp HTML: 👇

``````<div class="app">
<h4 class="mb-3">TODO App</h4>

<i class="fas fa-plus"></i>
</div>
</div>
``````

Div có id `addNew`là nút sẽ mở phương thức. Khoảng cách sẽ được hiển thị trên nút. Đây `i`là biểu tượng từ phông chữ tuyệt vời.

Chúng tôi sẽ sử dụng bootstrap để làm phương thức của chúng tôi. Chúng tôi sẽ sử dụng phương thức để thêm các nhiệm vụ mới. Đối với điều đó, hãy thêm liên kết bootstrap CDN bên trong thẻ head. 👇

``````<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"
crossorigin="anonymous"
/>

<script
src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"
integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p"
crossorigin="anonymous"
></script>
``````

Để xem các tác vụ đã tạo, chúng tôi sẽ sử dụng một div với một nhiệm vụ id, bên trong div có ứng dụng tên lớp. 👇

``````<h5 class="text-center my-3">Tasks</h5>

``````

Chèn CDN phông chữ tuyệt vời bên trong thẻ head để sử dụng phông chữ trong dự án của chúng tôi 👇

``````<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css" />
``````

Sao chép và dán mã bên dưới từ phương thức bootstrap. Nó mang một biểu mẫu với 3 trường đầu vào và một nút gửi. Nếu muốn, bạn có thể tìm kiếm trang web của Bootstrap bằng cách viết 'modal' vào thanh tìm kiếm.

``````<!-- Modal -->
<form
id="form"
tabindex="-1"
aria-labelledby="exampleModalLabel"
aria-hidden="true"
>
<div class="modal-dialog">
<div class="modal-content">
<button
type="button"
class="btn-close"
data-bs-dismiss="modal"
aria-label="Close"
></button>
</div>
<div class="modal-body">
<input type="text" class="form-control" name="" id="textInput" />
<div id="msg"></div>
<br />
<p>Due Date</p>
<input type="date" class="form-control" name="" id="dateInput" />
<br />
<p>Description</p>
<textarea
name=""
class="form-control"
id="textarea"
cols="30"
rows="5"
></textarea>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
Close
</button>
</div>
</div>
</div>
</form>
``````

Kết quả cho đến nay: 👇

Chúng tôi đã hoàn tất việc thiết lập tệp HTML. Hãy bắt đầu CSS.

CSS

Thêm các kiểu này vào phần nội dung để chúng tôi có thể giữ ứng dụng của mình ở chính giữa màn hình.

``````body {
font-family: sans-serif;
margin: 0 50px;
background-color: #e5e5e5;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
``````

Hãy tạo kiểu div với ứng dụng classname. 👇

``````.app {
background-color: #fff;
width: 300px;
height: 500px;
border: 5px solid #abcea1;
}
``````

Kết quả cho đến nay: 👇

Bây giờ, hãy tạo kiểu cho nút với id `addNew`. 👇

``````#addNew {
display: flex;
justify-content: space-between;
align-items: center;
background-color: rgba(171, 206, 161, 0.35);
cursor: pointer;
}
.fa-plus {
background-color: #abcea1;
}
``````

Kết quả cho đến nay: 👇

Nếu bạn nhấp vào nút, phương thức sẽ bật lên như thế này: 👇

Thêm JS

Trong tệp JavaScript, trước hết, hãy chọn tất cả các bộ chọn từ HTML mà chúng ta cần sử dụng. 👇

``````let form = document.getElementById("form");
let textInput = document.getElementById("textInput");
let dateInput = document.getElementById("dateInput");
let textarea = document.getElementById("textarea");
let msg = document.getElementById("msg");
``````

Xác thực biểu mẫu

Chúng tôi không thể để người dùng gửi các trường đầu vào trống. Vì vậy, chúng ta cần xác thực các trường đầu vào. 👇

``````form.addEventListener("submit", (e) => {
e.preventDefault();
formValidation();
});

let formValidation = () => {
if (textInput.value === "") {
console.log("failure");
msg.innerHTML = "Task cannot be blank";
} else {
console.log("success");
msg.innerHTML = "";
}
};
``````

Ngoài ra, hãy thêm dòng này vào bên trong CSS:

``````#msg {
color: red;
}
``````

Kết quả cho đến nay: 👇

Như bạn có thể thấy, xác thực đang hoạt động. Mã JavaScript không cho phép người dùng gửi các trường nhập trống, nếu không bạn sẽ thấy thông báo lỗi.

Cách thu thập dữ liệu và sử dụng bộ nhớ cục bộ

Bất kỳ đầu vào nào mà người dùng viết, chúng tôi cần thu thập chúng và lưu trữ chúng trong bộ nhớ cục bộ.

Đầu tiên, chúng tôi thu thập dữ liệu từ các trường đầu vào, sử dụng hàm được đặt tên `acceptData`và một mảng được đặt tên `data`. Sau đó, chúng tôi đẩy chúng vào bên trong bộ nhớ cục bộ như thế này: 👇

``````let data = [];

let acceptData = () => {
data.push({
text: textInput.value,
date: dateInput.value,
description: textarea.value,
});

localStorage.setItem("data", JSON.stringify(data));

console.log(data);
};
``````

Cũng lưu ý rằng điều này sẽ không bao giờ hoạt động trừ khi bạn gọi hàm `acceptData`bên trong câu lệnh else của xác thực biểu mẫu. Cùng theo dõi tại đây: 👇

``````let formValidation = () => {

// Other codes are here
else {

// Other codes are here

acceptData();
}
};
``````

Bạn có thể nhận thấy rằng phương thức không tự động đóng. Để giải quyết vấn đề này, hãy viết hàm nhỏ này bên trong câu lệnh else của xác thực biểu mẫu: 👇

``````let formValidation = () => {

// Other codes are here
else {

// Other codes are here

acceptData();

(() => {
})();
}
};
``````

Nếu bạn mở các công cụ dành cho nhà phát triển Chrome, hãy chuyển đến ứng dụng và mở bộ nhớ cục bộ. Bạn có thể xem kết quả này: 👇

Cách tạo nhiệm vụ mới

Để tạo một tác vụ mới, chúng ta cần tạo một hàm, sử dụng các ký tự mẫu để tạo các phần tử HTML và sử dụng bản đồ để đẩy dữ liệu được thu thập từ người dùng vào bên trong mẫu. Cùng theo dõi tại đây: 👇

``````let createTasks = () => {
data.map((x, y) => {
<div id=\${y}>
<span class="fw-bold">\${x.text}</span>
<span class="small text-secondary">\${x.date}</span>
<p>\${x.description}</p>

<span class="options">
<i onClick= "editTask(this)" data-bs-toggle="modal" data-bs-target="#form" class="fas fa-edit"></i>
</span>
</div>
`);
});

resetForm();
};
``````

Cũng lưu ý rằng hàm sẽ không bao giờ chạy trừ khi bạn gọi nó bên trong `acceptData`hàm, như thế này: 👇

``````let acceptData = () => {
// Other codes are here

};
``````

Khi chúng tôi đã hoàn tất việc thu thập và chấp nhận dữ liệu từ người dùng, chúng tôi cần xóa các trường đầu vào. Đối với điều đó, chúng tôi tạo ra một hàm được gọi là `resetForm`. Cùng theo dõi: 👇

``````let resetForm = () => {
textInput.value = "";
dateInput.value = "";
textarea.value = "";
};
``````

Kết quả cho đến nay: 👇

Như bạn có thể thấy, không có phong cách nào với thẻ. Hãy thêm một số phong cách: 👇

``````#tasks {
display: grid;
grid-template-columns: 1fr;
gap: 14px;
}

border: 3px solid #abcea1;
background-color: #e2eede;
display: grid;
gap: 4px;
}
``````

Tạo kiểu cho các nút chỉnh sửa và xóa bằng mã này: 👇

``````#tasks div .options {
justify-self: center;
display: flex;
gap: 20px;
}

cursor: pointer;
}
``````

Kết quả cho đến nay: 👇

Chức năng xóa một nhiệm vụ

Xem kỹ ở đây, tôi đã thêm 3 dòng mã bên trong hàm.

• Dòng đầu tiên sẽ xóa phần tử HTML khỏi màn hình,
• dòng thứ hai sẽ xóa Nhiệm vụ được nhắm mục tiêu khỏi mảng dữ liệu,
• và dòng thứ ba sẽ cập nhật bộ nhớ cục bộ với dữ liệu mới.
``````let deleteTask = (e) => {
e.parentElement.parentElement.remove();

data.splice(e.parentElement.parentElement.id, 1);

localStorage.setItem("data", JSON.stringify(data));

console.log(data);
};
``````

Bây giờ, hãy tạo một tác vụ giả và cố gắng xóa nó. Kết quả cho đến nay trông như thế này: 👇

Chức năng chỉnh sửa nhiệm vụ

Hãy xem kỹ ở đây, tôi đã thêm 5 dòng mã bên trong hàm.

• Dòng 1 đang nhắm mục tiêu nhiệm vụ mà chúng tôi đã chọn để chỉnh sửa
• Các dòng 2, 3 và 4, đang nhắm mục tiêu các giá trị [nhiệm vụ, ngày tháng, mô tả] của nhiệm vụ mà chúng tôi đã chọn để chỉnh sửa
• dòng 5 đang chạy chức năng xóa để xóa cả dữ liệu đã chọn khỏi bộ nhớ cục bộ, phần tử HTML và mảng dữ liệu.
``````let editTask = (e) => {

};
``````

Bây giờ, hãy thử tạo một tác vụ giả và chỉnh sửa nó. Kết quả cho đến nay: 👇

Cách lấy dữ liệu từ bộ nhớ cục bộ

Nếu bạn làm mới trang, bạn sẽ lưu ý rằng tất cả dữ liệu của bạn đã biến mất. Để giải quyết vấn đề đó, chúng tôi chạy IIFE (Biểu thức hàm được gọi ngay lập tức) để truy xuất dữ liệu từ bộ nhớ cục bộ. Cùng theo dõi: 👇

``````(() => {
data = JSON.parse(localStorage.getItem("data")) || [];
console.log(data);