1603549200
Dnit is a task runner based on typescript and Deno. It uses typescript variables for tasks and dependencies and is aimed at larger projects with tasks split across many files or shared between projects.
import {task, exec, file} from "https://deno.land/x/dnit@dnit-v1.5.0/dnit.ts";
/// A file to be tracked as a target and dependency:
export const msg = file({
path: './msg.txt'
});
/// A task definition. No side effect is incurred by creating a task.
export const helloWorld = task({
name: 'helloWorld',
description: "foo",
action: async () => { /// Actions are typescript async ()=> Promise<void> functions.
await Deno.run({
cmd: ["./writeMsg.sh"],
}).status();
},
deps: [
file({
path: "./writeMsg.sh"
})
],
targets: [
msg
]
});
export const goodbye = task({
name: 'goodbye',
action: async () => {
// use ordinary typescript idiomatically if several actions are required
const actions = [
async () => {
const txt = await Deno.readTextFile(msg.path);
console.log(txt);
},
async () => {
console.log("...");
},
];
for (const action of actions) {
await action();
}
},
deps: [msg] /// Dependency added as a typescript variable
/// Dependencies can be file dependency or task dependencies.
});
/// Register cmdline args & tasks with the tool.
exec(Deno.args, [helloWorld, goodbye]);
dnit list
dnit helloWorld
dnit list --verbose
In verbose mode the tool logs to stderr (fd #2)
It is recommended to use deno install
to install the tool, which provides a convenient entrypoint script and aliases the permission flags.
deno install --allow-read --allow-write --allow-run --unstable -f --name dnit https://deno.land/x/dnit@dnit-v1.5.0/main.ts
Install from source checkout:
deno install --allow-read --allow-write --allow-run --unstable -f --name dnit ./main.ts
Files are tracked by the exported export function file(fileParams: FileParams) : TrackedFile
/** User params for a tracked file */
export type FileParams = {
/// File path
path: string;
/// Optional function for how to hash the file. Defaults to the sha1 hash of the file contents.
/// A file is out of date if the file timestamp and the hash are different than that in the task manifest
gethash?: GetFileHash;
};
TrackedFile
objects are used in tasks, either as targets or dependencies.
Tasks are created by the exported function task(taskParams: TaskParams): Task
/** User definition of a task */
export type TaskParams = {
/// Name: (string) - The key used to initiate a task
name: A.TaskName;
/// Description (string) - Freeform text description shown on help
description?: string;
/// Action executed on execution of the task (async or sync)
action: Action;
/// Optional list of explicit task dependencies
task_deps?: Task[];
/// Optional list of explicit file dependencies
file_deps?: TrackedFile[];
/// Optional list of task or file dependencies
deps?: (Task|TrackedFile)[];
/// Targets (files which will be produced by execution of this task)
targets?: TrackedFile[];
/// Custom up-to-date definition - Can be used to make a task *less* up to date. Eg; use uptodate: runAlways to run always on request regardless of dependencies being up to date.
uptodate?: IsUpToDate;
};
Tasks are passed to the exported export async function exec(cliArgs: string[], tasks: Task[]) : Promise<void>
This exposes the tasks for execution by the CLI and executes them according to the cliArgs
passed in.
exec(Deno.args, tasks);
This tool aims to support “large” projects with many tasks and even sharing task definitions across projects.
dnit
directory. This provides a place to have a (deno) typescript tree for the task scripting, which encourages tasks to be separated into modules and generally organised as a typescript project tree.import_map.json
file in order to import tasks and utils more flexibly.dnit
tool can be executed on its own (see section on Installation above)The dnit
tool searches for a user script to execute, in order to support the abovementioned directory of sources.
dnit
is the main it runs the launch
function to run the user’s scripts.findUserSource
findUserSource
looks for subdirectory dnit
and looks for sources main.ts
or dnit.ts
import_map.json
or .import_map.json
to use as the import map.findUserSource
in the parent directory.Eg: with a file layout:
repo
dnit
main.ts
import_map.json
src
project.ts
package.json
tsconfig.json
Executing dnit
anywhere in a subdirectory of repo
will execute the main.ts
. Any relative paths used for dependencies and targets will resolve relative to the repo
root, since it is where the subdirectory and file dnit/main.ts
was found.
Note that the other directories can contain (non-deno) typescript project(s) and having the (deno) typescript sources in a nominal dnit
tree helps prevent confusion between the two.
Author: PaulThompson
Source Code: https://github.com/PaulThompson/dnit
#deno #node #nodejs #typescript
1603549200
Dnit is a task runner based on typescript and Deno. It uses typescript variables for tasks and dependencies and is aimed at larger projects with tasks split across many files or shared between projects.
import {task, exec, file} from "https://deno.land/x/dnit@dnit-v1.5.0/dnit.ts";
/// A file to be tracked as a target and dependency:
export const msg = file({
path: './msg.txt'
});
/// A task definition. No side effect is incurred by creating a task.
export const helloWorld = task({
name: 'helloWorld',
description: "foo",
action: async () => { /// Actions are typescript async ()=> Promise<void> functions.
await Deno.run({
cmd: ["./writeMsg.sh"],
}).status();
},
deps: [
file({
path: "./writeMsg.sh"
})
],
targets: [
msg
]
});
export const goodbye = task({
name: 'goodbye',
action: async () => {
// use ordinary typescript idiomatically if several actions are required
const actions = [
async () => {
const txt = await Deno.readTextFile(msg.path);
console.log(txt);
},
async () => {
console.log("...");
},
];
for (const action of actions) {
await action();
}
},
deps: [msg] /// Dependency added as a typescript variable
/// Dependencies can be file dependency or task dependencies.
});
/// Register cmdline args & tasks with the tool.
exec(Deno.args, [helloWorld, goodbye]);
dnit list
dnit helloWorld
dnit list --verbose
In verbose mode the tool logs to stderr (fd #2)
It is recommended to use deno install
to install the tool, which provides a convenient entrypoint script and aliases the permission flags.
deno install --allow-read --allow-write --allow-run --unstable -f --name dnit https://deno.land/x/dnit@dnit-v1.5.0/main.ts
Install from source checkout:
deno install --allow-read --allow-write --allow-run --unstable -f --name dnit ./main.ts
Files are tracked by the exported export function file(fileParams: FileParams) : TrackedFile
/** User params for a tracked file */
export type FileParams = {
/// File path
path: string;
/// Optional function for how to hash the file. Defaults to the sha1 hash of the file contents.
/// A file is out of date if the file timestamp and the hash are different than that in the task manifest
gethash?: GetFileHash;
};
TrackedFile
objects are used in tasks, either as targets or dependencies.
Tasks are created by the exported function task(taskParams: TaskParams): Task
/** User definition of a task */
export type TaskParams = {
/// Name: (string) - The key used to initiate a task
name: A.TaskName;
/// Description (string) - Freeform text description shown on help
description?: string;
/// Action executed on execution of the task (async or sync)
action: Action;
/// Optional list of explicit task dependencies
task_deps?: Task[];
/// Optional list of explicit file dependencies
file_deps?: TrackedFile[];
/// Optional list of task or file dependencies
deps?: (Task|TrackedFile)[];
/// Targets (files which will be produced by execution of this task)
targets?: TrackedFile[];
/// Custom up-to-date definition - Can be used to make a task *less* up to date. Eg; use uptodate: runAlways to run always on request regardless of dependencies being up to date.
uptodate?: IsUpToDate;
};
Tasks are passed to the exported export async function exec(cliArgs: string[], tasks: Task[]) : Promise<void>
This exposes the tasks for execution by the CLI and executes them according to the cliArgs
passed in.
exec(Deno.args, tasks);
This tool aims to support “large” projects with many tasks and even sharing task definitions across projects.
dnit
directory. This provides a place to have a (deno) typescript tree for the task scripting, which encourages tasks to be separated into modules and generally organised as a typescript project tree.import_map.json
file in order to import tasks and utils more flexibly.dnit
tool can be executed on its own (see section on Installation above)The dnit
tool searches for a user script to execute, in order to support the abovementioned directory of sources.
dnit
is the main it runs the launch
function to run the user’s scripts.findUserSource
findUserSource
looks for subdirectory dnit
and looks for sources main.ts
or dnit.ts
import_map.json
or .import_map.json
to use as the import map.findUserSource
in the parent directory.Eg: with a file layout:
repo
dnit
main.ts
import_map.json
src
project.ts
package.json
tsconfig.json
Executing dnit
anywhere in a subdirectory of repo
will execute the main.ts
. Any relative paths used for dependencies and targets will resolve relative to the repo
root, since it is where the subdirectory and file dnit/main.ts
was found.
Note that the other directories can contain (non-deno) typescript project(s) and having the (deno) typescript sources in a nominal dnit
tree helps prevent confusion between the two.
Author: PaulThompson
Source Code: https://github.com/PaulThompson/dnit
#deno #node #nodejs #typescript
1650391200
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.
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.
pip install ansible-kernel
python -m ansible_kernel.install
pip install -e .
python -m ansible_kernel.install
pip install ansible-kernel
python -m ansible_kernel.install --sys-prefix
jupyter notebook
# In the notebook interface, select Ansible from the 'New' menu
docker run -p 8888:8888 benthomasson/ansible-jupyter-kernel
Then copy the URL from the output into your browser:
http://localhost:8888/?token=ABCD1234
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.
The inventory that your tasks will use
#inventory
[all]
ahost ansible_connection=local
anotherhost examplevar=val
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
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
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
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
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
Provides overrides typically found in ansible.cfg
#ansible.cfg
[defaults]
host_key_checking=False
You can find various example notebooks in the repository
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
License: Apache-2.0 License
1596666360
TypeScript provides a command-line utility tsc
that compiles (transpiles) TypeScript files (_.ts_
) into JavaScript. However, the tsc
compiler (short for TypeScript compiler) needs a JSON configuration file to look for TypeScript files in the project and generate valid output files at a correct location.
When you run tsc
command in a directory, TypeScript compiler looks for the tsconfig.json
file in the current directory and if it doesn’t find one, then it keeps looking up the directory tree until it finds one. The directory where the tsconfig.json
is located is considered as the root of the project.
You can manually provide a path to the tsconfig.json
file using --project
or -p
command-line flag. This file doesn’t need to have the tsconfig.json
filename if you are using this flag with the exact file path. However, you can also provide the directory path that contains the tsconfig.json
file.
$ tsc -p /proj/x/tsconfig.dev.json
If the TypeScript compiler fails to locate this configuration file, you would get an error. But you can provide settings enlisted in this file through the equivalent command-line options which we will cover in the next lesson.
tsconfig.json
So what does this file contain and what exactly it controls?
{
"files": [
"src/lib/person.ts",
"src/lib/student.ts",
"src/main.ts"
],
"compilerOptions": {
"target": "ES6",
"module": "CommonJS",
"outDir": "./dist/development"
}
}
The tsconfig.json
file is a standard JSON file, however, it supports JSON5 specifications, so you can use comments, single quotes, and more. It contains some root-level options and some compiler options. The root-level options are options that are outside of the compilerOptions
object, so in the above example, files
is a root-level option.
The root-level options control how the project is presented to the TypeScript compiler, such as which TypeScript files to consider for the compilation. The compiler options contain settings for the TypeScript compiler such as where to output the compiled JavaScript files in the project directory.
These options control how the project is presented to the TypeScript compiler for the compilation and static type analysis. These options must be kept outside compilerOptions
object of the tsconfig.json
file.
files
The files
array contains the location of the TypeScript files to consider for the compilation. These can be either relative paths or absolute paths on the disk. A relative path is located relative to the location of the tsconfig.json
file (AKA root of the project).
/projects/sample/
├── a.ts
├── src/
| ├── b.ts
| ├── c.ts
| ├── ignore.ts
| └── lib/
| ├── d.ts
| └── e.ts
└── tsconfig.json
Let’s consider that we have the above directory structure in our project. As you can see, the TypeScript files (.ts) are located in multiple directories. We want to compile all the .ts
files except the ignore.ts
file. Hence we would provide relative paths of these files in the files
options of tsconfig.json
.
// tsconfig.json
{
"files": [
"a.ts",
"src/b.ts",
"./src/c.ts",
"src/lib/d.ts",
"./src/lib/e.ts"
]
}
You can also provide absolute paths of these files but relative paths are most recommended since they would be consistent on all the systems. All the relative paths are resolved against the path of tsconfig.json
file in the project. You can optionally provide ./
or ../
prefix to locate the file.
Since we haven’t provided any compilerOptions
values, all the default values for the compiler options are used which we will talk about in a bit. The TypeScript compiler compiles these files and outputs the JavaScript with .js
extension by keeping the same file name as the individual input file.
The TypeScript compiler also preserves the original file path, hence the .js
output file will be generated where the input file was in the directory structure. When you run the tsc
command from the directory where your tsconfig.json
file is located, you are going to see the result below.
/projects/sample/
├── a.js
├── a.ts
├── src/
| ├── b.js
| ├── b.ts
| ├── c.js
| ├── c.ts
| ├── ignore.ts
| └── lib/
| ├── d.js
| ├── d.ts
| ├── e.js
| └── e.ts
└── tsconfig.json
As you can see, the TypeScript compiler compiled all the input TypeScript files listed inside files
array of tsconfig.json
. You can’t see the ignore.js
file since ignore.ts
file was not included in the files
array.
The directory where the tsconfig.json
file is located is considered as the root of the project, AKA the root directory. You can also include a file from outside this root directory, such by including "../x.ts"
in the files
array where x
would be in the parent directory of the root directory. Since the TypeScript compiler preserves the input file path, it will generate x.js
in the parent directory of the root directory.
include & exclude
The files
option is great when you have relatively few files to work with. But when your project is big and contains hundreds of source files located in a nested directory structure, then handpicking file paths is not practical.
To solve this issue, we can use include
option. This option is just like files, however, we can optionally provide glob patterns to locate input files. The exclude
options behave the same, except it removes the files from the compilation that may have been included by the include
option.
// tsconfig.json
{
"include": [
"a.ts",
"src/**/*.ts"
],
"exclude": [
"./**/*/ignore.ts"
]
}
In the above tsconfig.json
, we have removed the files
option and added include
which adds a.ts
file from the root directory and all the .ts
file from the src
directory. Since this would also include any ignore.ts
from the src
directory, we have provided the exclude
option that excludes any ignore.ts
file from the compilation if located inside the src
directory.
When we run the tsc
command now, results won’t be any different since the files considered for the compilation both in the previous example and this example are the same.
/projects/sample/
├── a.js
├── a.ts
├── src/
| ├── b.js
| ├── b.ts
| ├── c.js
| ├── c.ts
| ├── ignore.ts
| └── lib/
| ├── d.js
| ├── d.ts
| ├── e.js
| └── e.ts
└── tsconfig.json
The TypeScript compiler automatically excludes files from the "node_modules"
, "bower_components"
, "jspm_packages"
and "<outDir>"
directories, where <outDir>
is the value of outDir
compiler-option provided by you. This prevents any .ts
file from these directories getting included in the compilation process by accident.
#nodejs #typescript #deno #programming #javascript #deno
1650021960
今日は、Todoアプリを作成してJavaScriptでCRUD操作を行う方法を学びます。始めましょう🔥
これは私たちが今日作っているアプリです:
CRUDは-の略です
CRUDは、データの作成、データの読み取り、編集、およびそれらのデータの削除を可能にするメカニズムの一種です。この例では、Todoアプリを作成するので、タスクの作成、タスクの読み取り、タスクの更新、またはタスクの削除を行うための4つのオプションがあります。
チュートリアルを開始する前に、まず、CRUDの原則を理解しましょう。そのために、非常に単純なソーシャルメディアアプリケーションを作成しましょう。
このプロジェクトでは、以下の手順に従います。
bodyタグ内に、クラス名を使用してdivを作成します.container
。そこには2つのセクションがあり.left
、.right
👇
<body>
<h1>Social Media App</h1>
<div class="container">
<div class="left"></div>
<div class="right"></div>
</div>
</body>
左側に投稿を作成します。右側では、投稿を表示、更新、削除できます。次に、.leftdivタグ内にフォームを作成します👇
<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>
このコードをHTML内に記述して、投稿を右側に表示できるようにします👇
<div class="right">
<h3>Your posts here</h3>
<div id="posts"></div>
</div>
次に、プロジェクトでそのフォントを使用するために、headタグ内にfont-awesomeCDNを挿入します👇
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css" />
次に、削除アイコンと編集アイコンを使用してサンプル投稿を作成します。このコードをdiv内に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>
これまでの結果は次のようになります。
シンプルにしましょう。スタイルシート内にこれらのスタイルを記述します:👇
body {
font-family: sans-serif;
margin: 0 50px;
}
.container {
display: flex;
gap: 50px;
}
#posts {
width: 400px;
}
i {
cursor: pointer;
}
次に、投稿divとオプションアイコンに次のスタイルを記述します。👇
#posts div {
display: flex;
align-items: center;
justify-content: space-between;
}
.options {
display: flex;
gap: 25px;
}
#msg {
color: red;
}
これまでの結果は次のようになります:👇
このフローチャートに従って、プロジェクトを進めていきます。心配しないでください、私は途中ですべてを説明します。👇
まず、JavaScriptのHTMLからすべてのIDセレクターをターゲットにしましょう。このように:👇
let form = document.getElementById("form");
let input = document.getElementById("input");
let msg = document.getElementById("msg");
let posts = document.getElementById("posts");
次に、フォームの送信イベントリスナーを作成して、アプリのデフォルトの動作を防ぐことができるようにします。同時に、という名前の関数を作成しますformValidation
。👇
form.addEventListener("submit", (e) => {
e.preventDefault();
console.log("button clicked");
formValidation();
});
let formValidation = () => {};
次に、関数内でifelseステートメントを作成しformValidation
ます。これにより、ユーザーが空白の入力フィールドを送信するのを防ぐことができます。👇
let formValidation = () => {
if (input.value === "") {
msg.innerHTML = "Post cannot be blank";
console.log("failure");
} else {
console.log("successs");
msg.innerHTML = "";
}
};
これまでの結果は次のとおりです:👇
ご覧のとおり、ユーザーがフォームを空白で送信しようとすると、メッセージも表示されます。
入力フィールドから取得するデータが何であれ、それらをオブジェクトに格納します。。という名前のオブジェクトを作成しましょうdata
。そして、次の名前の関数を作成しますacceptData
:👇
let data = {};
let acceptData = () => {};
主なアイデアは、関数を使用して、入力からデータを収集し、という名前のオブジェクトに格納することdata
です。acceptData
それでは、関数の作成を完了しましょう。
let acceptData = () => {
data["text"] = input.value;
console.log(data);
};
また、acceptData
ユーザーが送信ボタンをクリックしたときに機能する機能が必要です。そのために、関数のelseステートメントでこの関数を起動しformValidation
ます。👇
let formValidation = () => {
if (input.value === "") {
// Other codes are here
} else {
// Other codes are here
acceptData();
}
};
データを入力してフォームを送信すると、コンソールにユーザーの入力値を保持しているオブジェクトが表示されます。このように:👇
入力データを右側に投稿するには、div要素を作成し、それを投稿divに追加する必要があります。まず、関数を作成して次の行を記述しましょう:👇
let createPost = () => {
posts.innerHTML += ``;
};
バックティックはテンプレートリテラルです。それは私たちのテンプレートとして機能します。ここでは、親div、入力自体、および編集アイコンと削除アイコンを保持するオプションdivの3つが必要です。機能を終了しましょう👇
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 = "";
};
acceptdata
関数では、関数を起動しますcreatePost
。このように:👇
let acceptData = () => {
// Other codes are here
createPost();
};
これまでの結果:👇
これまでのところ、プロジェクト1はほぼ完了しています。
投稿を削除するために、まず、javascriptファイル内に関数を作成しましょう。
let deletePost = (e) => {};
deletePost
次に、onClick属性を使用して、すべての削除アイコン内でこの関数を起動します。これらの行は、HTMLとテンプレートリテラルで記述します。👇
<i onClick="deletePost(this)" class="fas fa-trash-alt"></i>
this
キーワードは、イベントを発生させた要素を参照します。この場合、this
は削除ボタンを指します。
注意深く見てください。削除ボタンの親は、クラス名オプションのあるスパンです。スパンの親はdivです。parentElement
したがって、削除アイコンからdivにジャンプし、直接ターゲットにして削除できるように、2回書き込みます。
関数を終了しましょう。👇
let deletePost = (e) => {
e.parentElement.parentElement.remove();
};
これまでの結果:👇
投稿を編集するために、まず、JavaScriptファイル内に関数を作成しましょう。
let editPost = (e) => {};
editPost
次に、onClick属性を使用して、すべての編集アイコン内でこの関数を起動します。これらの行は、HTMLとテンプレートリテラルで記述します。👇
<i onClick="editPost(this)" class="fas fa-edit"></i>
this
キーワードは、イベントを発生させた要素を参照します。この場合、this
は編集ボタンを指します。
注意深く見てください。編集ボタンの親は、クラス名オプションのあるスパンです。スパンの親はdivです。parentElement
したがって、編集アイコンからdivにジャンプし、直接ターゲットにして削除できるように、2回書き込みます。
次に、投稿内のデータが何であれ、入力フィールドに戻して編集します。
関数を終了しましょう。👇
let editPost = (e) => {
input.value = e.parentElement.previousElementSibling.innerHTML;
e.parentElement.parentElement.remove();
};
これまでの結果:👇
プロジェクト1を完了してくださった皆さん、おめでとうございます。さあ、ちょっと休憩してください。
CRUD操作を使用してToDoアプリを作成する方法
To-Doアプリであるプロジェクト2の作成を始めましょう。
このプロジェクトでは、以下の手順に従います。
このスターターコードをHTMLファイル内に記述します:👇
<div class="app">
<h4 class="mb-3">TODO App</h4>
<div id="addNew" data-bs-toggle="modal" data-bs-target="#form">
<span>Add New Task</span>
<i class="fas fa-plus"></i>
</div>
</div>
IDを持つdivaddNew
は、モーダルを開くボタンです。ボタンにスパンが表示されます。これi
はfont-awesomeのアイコンです。
ブートストラップを使用してモーダルを作成します。モーダルを使用して新しいタスクを追加します。そのために、headタグ内にブートストラップCDNリンクを追加します。👇
<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>
作成されたタスクを確認するには、クラス名アプリを使用したdiv内で、idタスクを使用したdivを使用します。👇
<h5 class="text-center my-3">Tasks</h5>
<div id="tasks"></div>
プロジェクトでフォントを使用するには、headタグ内にfont-awesomeCDNを挿入します👇
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css" />
ブートストラップモーダルからの以下のコードをコピーして貼り付けます。3つの入力フィールドと送信ボタンを備えたフォームがあります。必要に応じて、検索バーに「モーダル」と入力して、BootstrapのWebサイトを検索できます。
<!-- Modal -->
<form
class="modal fade"
id="form"
tabindex="-1"
aria-labelledby="exampleModalLabel"
aria-hidden="true"
>
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Add New Task</h5>
<button
type="button"
class="btn-close"
data-bs-dismiss="modal"
aria-label="Close"
></button>
</div>
<div class="modal-body">
<p>Task Title</p>
<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>
<button type="submit" id="add" class="btn btn-primary">Add</button>
</div>
</div>
</div>
</form>
これまでの結果:👇
HTMLファイルの設定は完了です。CSSを始めましょう。
これらのスタイルを本文に追加して、アプリを画面の正確な中央に配置できるようにします。
body {
font-family: sans-serif;
margin: 0 50px;
background-color: #e5e5e5;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
classnameアプリでdivのスタイルを設定しましょう。👇
.app {
background-color: #fff;
width: 300px;
height: 500px;
border: 5px solid #abcea1;
border-radius: 8px;
padding: 15px;
}
これまでの結果:👇
それでは、idを使用してボタンのスタイルを設定しましょうaddNew
。👇
#addNew {
display: flex;
justify-content: space-between;
align-items: center;
background-color: rgba(171, 206, 161, 0.35);
padding: 5px 10px;
border-radius: 5px;
cursor: pointer;
}
.fa-plus {
background-color: #abcea1;
padding: 3px;
border-radius: 3px;
}
これまでの結果:👇
ボタンをクリックすると、モーダルが次のようにポップアップ表示されます:👇
JavaScriptファイルで、まず、使用する必要のあるHTMLからすべてのセレクターを選択します。👇
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");
let tasks = document.getElementById("tasks");
let add = document.getElementById("add");
ユーザーに空白の入力フィールドを送信させることはできません。したがって、入力フィールドを検証する必要があります。👇
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 = "";
}
};
また、CSS内に次の行を追加します。
#msg {
color: red;
}
これまでの結果:👇
ご覧のとおり、検証は機能しています。JavaScriptコードでは、ユーザーが空白の入力フィールドを送信することはできません。そうしないと、エラーメッセージが表示されます。
ユーザーが書き込んだ入力が何であれ、それらを収集してローカルストレージに保存する必要があります。
まず、という名前の関数とという名前acceptData
の配列を使用して、入力フィールドからデータを収集しますdata
。次に、次のようにローカルストレージ内にプッシュします:👇
let data = [];
let acceptData = () => {
data.push({
text: textInput.value,
date: dateInput.value,
description: textarea.value,
});
localStorage.setItem("data", JSON.stringify(data));
console.log(data);
};
acceptData
また、フォーム検証のelseステートメント内で関数を呼び出さない限り、これは機能しないことに注意してください。ここに従ってください:👇
let formValidation = () => {
// Other codes are here
else {
// Other codes are here
acceptData();
}
};
モーダルが自動的に閉じないことに気付いたかもしれません。これを解決するには、フォーム検証のelseステートメント内にこの小さな関数を記述します。👇
let formValidation = () => {
// Other codes are here
else {
// Other codes are here
acceptData();
add.setAttribute("data-bs-dismiss", "modal");
add.click();
(() => {
add.setAttribute("data-bs-dismiss", "");
})();
}
};
Chrome開発ツールを開く場合は、アプリケーションに移動してローカルストレージを開きます。あなたはこの結果を見ることができます:👇
新しいタスクを作成するには、関数を作成し、テンプレートリテラルを使用してHTML要素を作成し、マップを使用してユーザーから収集したデータをテンプレート内にプッシュする必要があります。ここに従ってください:👇
let createTasks = () => {
tasks.innerHTML = "";
data.map((x, y) => {
return (tasks.innerHTML += `
<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>
<i onClick ="deleteTask(this);createTasks()" class="fas fa-trash-alt"></i>
</span>
</div>
`);
});
resetForm();
};
また、次のacceptData
ように、関数内で呼び出さない限り、関数は実行されないことに注意してください。👇
let acceptData = () => {
// Other codes are here
createTasks();
};
ユーザーからのデータの収集と受け入れが完了したら、入力フィールドをクリアする必要があります。そのために、と呼ばれる関数を作成しますresetForm
。フォローする:👇
let resetForm = () => {
textInput.value = "";
dateInput.value = "";
textarea.value = "";
};
これまでの結果:👇
ご覧のとおり、このカードにはスタイルはありません。いくつかのスタイルを追加しましょう:👇
#tasks {
display: grid;
grid-template-columns: 1fr;
gap: 14px;
}
#tasks div {
border: 3px solid #abcea1;
background-color: #e2eede;
border-radius: 6px;
padding: 5px;
display: grid;
gap: 4px;
}
次のコードで編集ボタンと削除ボタンのスタイルを設定します:👇
#tasks div .options {
justify-self: center;
display: flex;
gap: 20px;
}
#tasks div .options i {
cursor: pointer;
}
これまでの結果:👇
ここを注意深く見てください。関数内に3行のコードを追加しました。
let deleteTask = (e) => {
e.parentElement.parentElement.remove();
data.splice(e.parentElement.parentElement.id, 1);
localStorage.setItem("data", JSON.stringify(data));
console.log(data);
};
次に、ダミータスクを作成し、それを削除してみます。これまでの結果は次のようになります:👇
ここを注意深く見てください。関数内に5行のコードを追加しました。
let editTask = (e) => {
let selectedTask = e.parentElement.parentElement;
textInput.value = selectedTask.children[0].innerHTML;
dateInput.value = selectedTask.children[1].innerHTML;
textarea.value = selectedTask.children[2].innerHTML;
deleteTask(e);
};
次に、ダミータスクを作成して編集してみます。これまでの結果:👇
ページを更新すると、すべてのデータが失われていることに気付くでしょう。この問題を解決するために、IIFE(即時呼び出し関数式)を実行して、ローカルストレージからデータを取得します。フォローする:👇
(() => {
data = JSON.parse(localStorage.getItem("data")) || [];
console.log(data);
createTasks();
})();
これで、ページを更新してもデータが表示されます。
このチュートリアルを正常に完了しました。おめでとうございます。CRUD操作を使用してToDoリストアプリケーションを作成する方法を学習しました。これで、このチュートリアルを使用して独自のCRUDアプリケーションを作成できます。
これが最後まで読むためのあなたのメダルです。❤️
ソース:https ://www.freecodecamp.org/news/learn-crud-operations-in-javascript-by-building-todo-app/
1650865080
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à viết tắt của -
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ụ.
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.
Đối với dự án này, chúng tôi sẽ thực hiện theo các bước sau:
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
và .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">
<h3>Your posts here</h3>
<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:
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: 👇
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. 👇
Đầ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.
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: 👇
Để đă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.
Để 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: 👇
Để 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: 👇
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.
Đối với dự án này, chúng tôi sẽ thực hiện theo các bước sau:
Viết mã khởi động này bên trong tệp HTML: 👇
<div class="app">
<h4 class="mb-3">TODO App</h4>
<div id="addNew" data-bs-toggle="modal" data-bs-target="#form">
<span>Add New Task</span>
<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>
<div id="tasks"></div>
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
class="modal fade"
id="form"
tabindex="-1"
aria-labelledby="exampleModalLabel"
aria-hidden="true"
>
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Add New Task</h5>
<button
type="button"
class="btn-close"
data-bs-dismiss="modal"
aria-label="Close"
></button>
</div>
<div class="modal-body">
<p>Task Title</p>
<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>
<button type="submit" id="add" class="btn btn-primary">Add</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.
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;
border-radius: 8px;
padding: 15px;
}
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);
padding: 5px 10px;
border-radius: 5px;
cursor: pointer;
}
.fa-plus {
background-color: #abcea1;
padding: 3px;
border-radius: 3px;
}
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: 👇
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");
let tasks = document.getElementById("tasks");
let add = document.getElementById("add");
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.
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();
add.setAttribute("data-bs-dismiss", "modal");
add.click();
(() => {
add.setAttribute("data-bs-dismiss", "");
})();
}
};
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: 👇
Để 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 = () => {
tasks.innerHTML = "";
data.map((x, y) => {
return (tasks.innerHTML += `
<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>
<i onClick ="deleteTask(this);createTasks()" class="fas fa-trash-alt"></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
createTasks();
};
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;
}
#tasks div {
border: 3px solid #abcea1;
background-color: #e2eede;
border-radius: 6px;
padding: 5px;
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;
}
#tasks div .options i {
cursor: pointer;
}
Kết quả cho đến nay: 👇
Xem kỹ ở đây, tôi đã thêm 3 dòng mã bên trong hàm.
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: 👇
Hãy xem kỹ ở đây, tôi đã thêm 5 dòng mã bên trong hàm.
let editTask = (e) => {
let selectedTask = e.parentElement.parentElement;
textInput.value = selectedTask.children[0].innerHTML;
dateInput.value = selectedTask.children[1].innerHTML;
textarea.value = selectedTask.children[2].innerHTML;
deleteTask(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: 👇
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);
createTasks();
})();
Bây giờ dữ liệu sẽ hiển thị ngay cả khi bạn làm mới trang.
Xin chúc mừng vì đã hoàn thành thành công hướng dẫn này. Bạn đã học cách tạo một ứng dụng danh sách việc cần làm bằng các phép toán CRUD. Bây giờ, bạn có thể tạo ứng dụng CRUD của riêng mình bằng cách sử dụng hướng dẫn này.
Nguồn: https://www.freecodecamp.org/news/learn-crud-operations-in-javascript-by-building-todo-app/