1673429760
FDS is an attempt to provide a very accessible high level framework to enable everyone to create apps using the Ethereum Web3 stack. At present it enables developers to:
Easy to use Dapp framework.
FDS make it easy for front end developers to create Dapps running on the Ethereum and Swarm networks.
The FDS Dapp Framework provides a high level javascript SDK for DApp developers to create and manage accounts, sign transactions using the associated Ethereum wallets, and interact with the Ethereum compatible blockchains and the Swarm network.
Use Node version 10. You may use Node Version Manager
npm i -D fds.js
If using the node at the command prompt, read the notes.
In FDS, most things are done in an account context, because transactions and data are authenticated and encrypted using Ethereum and Swarm compatible ECDSA algorithms before they leave your computer.
let fds = require('FDS.js')
let FDS = new fds()
let alice = await FDS.CreateAccount('alice', 'password')
let bob = await FDS.CreateAccount('bob', 'password')
Now you have two accounts, you can send an encrypted file from alice's private key...
let file = new File([`hello world `], `test.txt`, { type: 'text/plain' })
await alice.send('bob', file, '/shared/my-application/messages')
... and bob will receive and decrypt it with his private key - without ever having to exchange a key, or trust any third parties. ✨
let messages = await bob.messages('received', '/shared/my-application/messages')
messages[0].getFile()
let erc20 = b.getContract(erc20Abi, '0x35e46c...')
let totalSupply = await erc20.totalSupply()
let tx = await transfer.transfer('0xabcd...', 100)
create an issue on this repo if you'd like us to include an example of something you're working on!
You must first create an FDS object to work with.
To instantiate this using the default options:
let fds = new FDS()
It is also possible to specify values other than the defaults.
var fds = new FDS({
tokenName: 'gas',
swarmGateway: 'https://swarm.fairdatasociety.org',
ethGateway: 'https://geth-noordung.fairdatasociety.org',
faucetAddress: 'https://faucet-noordung.fairdatasociety.org/gimmie',
chainID: '235813',
httpTimeout: 1000,
walletVersion: 1,
scratchDir: '/tmp/something',
ensConfig: {
domain: 'datafund.eth',
registryAddress: '0xA1029cb176082eca658A67fD6807B9bDfB44A695',
subdomainRegistrarAddress: '0x0E6a3B5f6800145bAe95C48934B7b5a90Df50722',
resolverContractAddress: '0xC91AB84FFad79279D47a715eF91F5fbE86302E4D',
},
})
The FDS object is used to create and manage user accounts.
Creates a new FDS object.
let fds = new FDS()
Creates a new account with a wallet, ENS subdomain and Multibox contract, and saves it into local storage.
async FDS.CreateAccount( username, password, feedbackMessageCallback = console.log )
Inputs
.
delimited string satisfying Namehash requirements ].Returns
promise User (User Object)
let alice = await FDS.CreateAccount('alice', 'password', (message) => { ... });
Unlocks an account that already exists in local storage.
FDS.UnlockAccount( username, password )
Inputs
.
delimited string satisfying Namehash requirements ].Returns
User (User Object)
or
Failed (bool false)
let alice = await FDS.UnlockAccount('alice', 'password')
Gets a list of the accounts held in local storage.
async FDS.GetAccounts( walletVersion = [most recent wallet version] )
Inputs
Returns
Users (Array)[User Object]
FDS.GetAccounts()
Starts download of wallet backup file.
Must be in the browser environment.
async FDS.BackupAccount( subdomain )
Inputs
.
delimited string satisfying Namehash requirements, account must exist in local storage ].Returns
Success (bool)
FDS.BackupAccount('fds-ftw')
Returns a Ethereum style V3 wallet javascript object for a given username.
async FDS.BackupAccountAsJSON( username )
Inputs
Returns
WalletJSON (string)
note: take a note of the username here, you will need it when restoring this wallet.
let WalletJSON = await FDS.BackupAccountAsJSON('username')
Restores account from file object and saves it into the local storage.
async FDS.RestoreAccount( backupFile )
Inputs
fds-wallet-dan1234-backup.json
]Returns
promise Success (bool)
//retrieve backup from
await FDS.RestoreAccount(backupFile)
Restores account from a private key and saves it into the browser's localstorage.
async FDS.RestoreAccountFromPrivateKey( username, password, privateKey )
Inputs
Returns
promise User (User Object)
//retrieve backup from
await FDS.RestoreAccountFromPrivateKey('username', 'password', 'private-key-with-0x')
Restores account from a json V3 wallet and saves it into the browser's localstorage.
async FDS.RestoreAccountFromJSON( username, jsonString )
Inputs
Returns
promise User (User Object)
await FDS.RestoreAccountFromJSON('username', '{..}')
Deletes an account from localstorage.
async FDS.DeleteAccount( username )
Inputs
Returns
null
FDS.DeleteAccount('username')
Everything in FDS happens within a user context - this handles permissions, authorisation, encrytion and authentication under the hood so that everything is crypto-secure 🌍.
You may create or retrieve a user object using the CreateAccount, GetAccounts or UnlockAccount methods of the FDS object, then use it to interact with the FDS multiverse, Swarm and Ethereum networks.
Sends a file object from one user to another user's multibox path.
async user.send( recipientSubdomain, file, multiboxPath, encryptionCallback = console.log, uploadCallback = console.log, progressMessageCallback = console.log )
Inputs
Returns
Success (Bool)
let success = await bob.send(
'alice',
file,
'/shared/mail',
(encryptionStatus) => {},
(percentageUploaded) => {},
(progressMessageCallback) => {},
)
Checks to see if any files have been received by a user by multibox path.
async user.messages( query, multiboxPath, encryptionCallback = console.log, uploadCallback = console.log, progressMessageCallback = console.log )
Inputs
Returns
Messages (Array)[Message Object]
let messages = await bob.messages('received', '/shared/files')
Stores a private file. The file is encrypted using AES-256-CTR and the user's private key before it is uploaded into Swarm. An encrypted record of the location and metadata of the file is encrypted and stored into Swarm Feeds for later retrieval.
async user.store( file, encryptionCallback, uploadCallback, progressMessageCallback )
Inputs
Returns
Success (Bool)
let success = await bob.store(
file,
(encryptionStatus) => {},
(percentageUploaded) => {},
(progressMessage) => {},
)
Gets a list of stored files.
async user.stored( ** )
Inputs
Returns
StoredFiles (Array) [Hash Object]
let stored = await bob.stored()
Stores an encrypted string value
that can later be retrieved using the key
.
Useful for storing application state and much more. This value can only be accesed by the user
.
async user.storeValue( key, value )
Inputs
value
]Returns
StoredFiles (Array) [Hash Object]
let success = await a.storeValue('key231', 'hello encrypted value world')
Retrieves an encrypted string value
that can has been stored by the user
identified by a string key
.
async user.getValue( key )
Inputs
value
]Returns
Value (string)
let value = await a.retrieveValue('key231')
// 'hello encrypted value world'
Deploys a contract from the user's account context, returns a Contract Object with which you may call your Solidity contract's functions to interact with the blockchain.
async user.deployContract( abi, bytecode, args = [] )
Inputs
Returns
Contract (Contract Object)
let contract = await alice.deployContract([ { "inputs": [], ... } ] , '608060405234801561001057600080fd5b50...', ['my', 'arguments']);
Gets a Contract Object with the user's account context, which you may call the functions of to interact with the blockchain.
async user.getContract( abi, address )
Inputs
Returns
Value (string)
let success = await alice.getContract([ { "inputs": [], ... } ], '0xab234...' );
// true
Gets a user's balance.
async user.getBalance( ** )
Inputs
Returns
Value (string)
let balance = await alice.getBalance([ { "inputs": [], ... } ], '0xsa3bsdfs' );
191832026900000000
// true
Gets a user's balance.
async user.getBlockNumber( ** )
Inputs
Returns
Value (integer)
let bn = await alice.getBlockNumber()
123456789
// true
Pays a user native balance.
async user.pay( recipientSubdomain, amount, transactionCallback = console.log, transactionSignedCallback = console.log )
Inputs
Returns
TransactionHash (string)
let balance = await alice.pay('bob', '0.1')
//0x3cf52d1..
Pays a address native balance.
async user.pay( recipientAddress, amount, transactionCallback = console.log, transactionSignedCallback = console.log )
Inputs
Returns
TransactionHash (string)
let balance = await alice.pay('0x234...', '0.1')
//0x3cff2d1..
Signs a arbitary data.
async user.sign( message )
Inputs
Returns
Success (bool)
let balance = await alice.sign('message')
//0x3cff2d1..
Checks a message and signature and returns the address.
async user.recover( message, sig )
Inputs
Returns
Address (address)
let balance = await alice.recover('message', '0xabc...')
//0x3cff2d1..
Message objects are returned from user.messages()
Retrieves and decrypts a file from Swarm.
async message.getFile( decryptProgressCallback = console.log, downloadProgressCallback = console.log )
Inputs
Returns
File (File)
let file = await message.getFile()
Uses filesaver to prompt a file download from the browser environment.
async message.saveAs( decryptProgressCallback = console.log, downloadProgressCallback = console.log )
Inputs
Returns
File (File)
let file = await message.saveAs()
Hash objects are used to represent files encrypted and stored into swarm.
address: 'ece513967ad1d7610f280ff1a6c619ae7458780bbd0e0ba687e60ba6e3ae47e2', file: { name: 'test.txt', type: 'text/plain' }, time: 1569241451971,
Hash objects contain references to encrypted files stored in Swarm.
Retrieves and decrypts a file from Swarm.
async hash.getFile( decryptProgressCallback = console.log, downloadProgressCallback = console.log )
Inputs
Returns
promise File (File)
let file = await hash.getFile()
Uses filesaver to prompt a file download from the browser environment.
async hash.saveAs( decryptProgressCallback = console.log, downloadProgressCallback = console.log )
Inputs
Returns
null
let file = await hash.saveAs()
Uses filesaver to prompt a file download from the browser environment.
async hash.gatewayLink( ** )
Inputs
Returns
GatewayLink (string)
let file = await hash.gatewayLink()
The contract object exposes any Solidity methods, which can be called just like normal functions.
It is returned from GetContract or DeployContract.
async contract.myMethod( arg1, arg2, ... )
myMethod can be any function, getter or setter of your contract
Inputs
Because the node environment does not include the file object, you must include a stub file object so that FDS has knowledge of what meta information is associated with the file.
class File {
constructor(content, name, options) {
this.content = content
this.name = name
this.type = options.type
}
}
When using the Node at the command line, you may find it useful to enable top tier await functionality.
node --experimental-repl-await
Installation steps for Windows to setup FDS library to be used for development locally
Windows 10 node: 10.15.1 npm: 6.4.1
git clone https://github.com/fairDataSociety/fds.js.git
npm link
If you see the following errors:
npm WARN tarball tarball data for file-saver@1.3.8 seems to be corrupted. Trying one more time.
npm WARN tar ENOENT: no such file or directory, open 'C:\fds.js\node_modules\.staging\type-is-55fc7b2b\package.json'
...
npm ERR! code EINTEGRITY
npm ERR! Verification failed while extracting file-saver@1.3.8:
Install latest file-saver
npm install file-saver --save
If you get python errors like:
gyp ERR! stack Error: Command failed: C:\Users\TestUser\Anaconda3\python.exe -c import sys; print "%s.%s.%s" % sys.version_info[:3];
gyp ERR! stack File "<string>", line 1
gyp ERR! stack import sys; print "%s.%s.%s" % sys.version_info[:3];
gyp ERR! stack SyntaxError: invalid syntax
gyp ERR! stack at ChildProcess.exithandler (child_process.js:294:12)
gyp ERR! stack at ChildProcess.emit (events.js:182:13)
gyp ERR! stack at maybeClose (internal/child_process.js:962:16)
gyp ERR! stack at Process.ChildProcess._handle.onexit (internal/child_process.js:251:5)
....
This means you have the python 3 installed, if you already have python27 installed, set the environment variable by running:
npm config set python "path to python 2.7 exe"
If you encounter this error:
Error: Can't find Python executable "python", you can set the PYTHON env variable
This means python is not installed. Python 2.7 is required on the system. Download the installer from https://www.python.org/downloads/release/python-2716/
After installation is complete, open new command prompt window, change directory to the FDS repo and run: npm link
The libray should compile and build and if you come across vulnerabilities message:
Added 354 packages from 216 contributors and audited 172164 packages in 64.674s
found 2 high severity vulnerabilities
run `npm audit fix` to fix them, or `npm audit` for details
run twice:
npm audit fix
Now FDS repo is available to be imported in your project:
import FDS from 'fds';
Author: fairDataSociety
Source code: https://github.com/fairDataSociety/fds.js
License: LGPL-3.0, GPL-3.0 licenses found
#Web3 #ethereum
1667811060
RxTodo is an iOS application developed using ReactorKit. This project is for whom having trouble with learning how to build a RxSwift application due to lack of references. (as I did 😛)
Visit ReactorKit for detail.
Discussion and pull requests are welcomed 💖
Author: Devxoul
Source Code: https://github.com/devxoul/RxTodo
License: MIT license
1659460320
Imba • TodoMVC
Create complex web apps with ease! Imba is a new programming language for the web that compiles to highly performant and readable JavaScript. It has language level support for defining, extending, subclassing, instantiating and rendering dom nodes.
Let us know if you discover anything worth sharing.
npm install
index.html
in your browser to see it in action!The source resides in src/app.imba
. Run npm run watch
to recompile the source locally. Make sure you have installed Imba through npm: npm install -g imba
first.
Created by Sindre Aarsaether
Original article source at: https://github.com/somebee/todomvc-imba
1658012940
Ao
Ao is an unofficial, featureful, open source, community-driven, free Microsoft To-Do app, used by people in more than 120 countries.
Read this document in: Deutsch.
You can now support the development process through GitHub Sponsors.
Visit the contributing guidelines to learn more on how to translate this document into more languages.
Come over to Gitter or Twitter to share your thoughts on the project.
You can find more apps here.
Head to the releases page and download the appropriate installer for your system.
Ubuntu Linux users can directly install through Snapcraft
snap install ao
Macos users can directly install through Homebrew Cask
brew cask install ao
The version available on Homebrew Cask
may not be the latest, since unlike Snapcraft
, it is not offically maintained. If that is the case, please consider downloading directly from the Github releases page.
Visit the project homepage to view all features in detail.
~/.ao.json
or press Cmd/Ctrl . to modify any shortcut key. To reset delete ~/.ao.json
& restart the app.File
> Enable Global Shortcut Keys
option.40+ local keyboard shortcuts. Toggle anything in a flash.
View all the available local keyboard shortcuts.
Description | Keys |
---|---|
Activate Auto Night Mode | Cmd/Ctrl Alt N |
Add Due Date | Cmd/Ctrl Shift T |
Add Todo to My Day | Cmd/Ctrl K |
Complete Todo | Cmd/Ctrl Shift N |
Delete List | Cmd/Ctrl Shift D |
Delete Todo | Cmd/Ctrl D |
Edit Shortcut Keys | Cmd/Ctrl . |
Hide Completed Todos | Cmd/Ctrl Shift H |
Jump to List | Cmd/Ctrl 1 - 9 |
Make Text Larger | Cmd/Ctrl Shift = |
Make Text Smaller | Cmd/Ctrl - |
Navigate to Next List | Cmd/Ctrl Tab |
New List | Cmd/Ctrl L |
New Todo | Cmd/Ctrl N |
Rename List | Cmd/Ctrl Y |
Rename Todo | Cmd/Ctrl T |
Reset Zoom Level | Cmd/Ctrl 0 |
Return to Todos | Esc |
Search Todos | Cmd/Ctrl F |
Set Always on Top | Cmd/Ctrl Shift P |
Set Reminder | Cmd/Ctrl Shift E |
Sign out | Cmd/Ctrl Alt Q |
Toggle Black Theme | Cmd/Ctrl B |
Toggle Dark Theme | Cmd/Ctrl H |
Toggle Important | Cmd/Ctrl I |
Toggle My Day | Cmd/Ctrl M |
Toggle Planned | Cmd/Ctrl P |
Toggle Sepia Theme | Cmd/Ctrl G |
Toggle Settings | Cmd/Ctrl , |
Toggle Sidebar | Cmd/Ctrl O |
Toggle Tasks | Cmd/Ctrl J |
Toggle Window Menu | Alt |
Access Ao at any moment from anywhere within your operating system. All global shortcuts can be customized to match your own preference through the configuration file ~/.ao.json
.
View all the available global keyboard shortcuts.
Description | Global Shortcut |
---|---|
Create New Todo | Cmd/Ctrl Alt C |
Search Todos | Cmd/Ctrl Alt F |
Toggle Ao Window | Cmd/Ctrl Alt A |
For more info on how to contribute to the project, please read the contributing guidelines.
cd ao
npm install
or yarn install
npm start
or yarn start
npm test
or yarn test
npm run release
or yarn release
Ao is an unofficial, open source, third-party, community-driven, free app and is not affiliated in any way with Microsoft.
Author: Klaussinani
Source Code: https://github.com/klaussinani/ao
License: MIT license
1657967040
A todo list using leveldb
example
To add a new list
level-todo add "this is a name" "this is a value"
or
level-todo add read https://www.npmjs.com
Install
npm install -g level-todo
Usage
usage:
level-todo add name value
Add a new list.
level-todo get name
Get a list
level-todo rm/done name
Mark a list as done and remove it by the given name.
level-todo list
Show all the todo list
level-todo destroy
Clear all the list.
Author: Nickleefly
Source Code: https://github.com/nickleefly/level-todo
License: MIT license
1657029720
Trong hướng dẫn này, bạn sẽ học cách thiết lập Django với htmx và Tailwind CSS . Mục tiêu của cả htmx và Tailwind là đơn giản hóa việc phát triển web hiện đại để bạn có thể thiết kế và kích hoạt tính tương tác mà không bao giờ rời bỏ sự thoải mái và dễ dàng của HTML. Chúng ta cũng sẽ xem xét cách sử dụng Django Compressor để đóng gói và giảm thiểu nội dung tĩnh trong ứng dụng Django.
htmx là một thư viện cho phép bạn truy cập các tính năng hiện đại của trình duyệt như AJAX, CSS Transitions, WebSockets và Sự kiện do máy chủ gửi trực tiếp từ HTML, thay vì sử dụng JavaScript. Nó cho phép bạn xây dựng giao diện người dùng một cách nhanh chóng trực tiếp trong đánh dấu.
htmx mở rộng một số tính năng đã được tích hợp sẵn trong trình duyệt, như thực hiện các yêu cầu HTTP và phản hồi các sự kiện. Ví dụ: thay vì chỉ có thể thực hiện các yêu cầu GET và POST thông qua a
và form
các phần tử, bạn có thể sử dụng các thuộc tính HTML để gửi các yêu cầu GET, POST, PUT, PATCH hoặc DELETE trên bất kỳ phần tử HTML nào:
<button hx-delete="/user/1">Delete</button>
Bạn cũng có thể cập nhật các phần của trang để tạo Ứng dụng một trang (SPA): liên kết CodePen
Mở tab mạng trong các công cụ dành cho nhà phát triển của trình duyệt. Khi nút được nhấp, một yêu cầu XHR được gửi đến https://v2.jokeapi.dev/joke/Any?format=txt&safe-mode
điểm cuối. Phản hồi sau đó được thêm vào p
phần tử có id
đầu ra.
Để biết thêm ví dụ, hãy xem trang Ví dụ về giao diện người dùng từ tài liệu htmx chính thức.
Ưu điểm :
Nhược điểm :
Tailwind CSS là một khung CSS "ưu tiên tiện ích". Thay vì vận chuyển các thành phần được xây dựng sẵn (mà các khung công tác như Bootstrap và Bulma chuyên về), nó cung cấp các khối xây dựng dưới dạng các lớp tiện ích cho phép người ta tạo bố cục và thiết kế một cách nhanh chóng và dễ dàng.
Ví dụ: lấy HTML và CSS sau:
<style>
.hello {
height: 5px;
width: 10px;
background: gray;
border-width: 1px;
border-radius: 3px;
padding: 5px;
}
</style>
<div class="hello">Hello World</div>
Điều này có thể được triển khai với Tailwind như sau:
<div class="h-1 w-2 bg-gray-600 border rounded-sm p-1">Hello World</div>
Hãy xem Trình chuyển đổi CSS Tailwind để chuyển đổi CSS thô sang các lớp tiện ích tương đương trong Tailwind. So sánh các kết quả.
Ưu điểm :
<div class="bg-white dark:bg-black">
:.Nhược điểm :
Django Compressor là một tiện ích mở rộng được thiết kế để quản lý (nén / lưu vào bộ nhớ đệm) nội dung tĩnh trong ứng dụng Django. Với nó, bạn tạo một đường dẫn nội dung đơn giản cho:
Cùng với đó, hãy cùng xem cách làm việc với từng công cụ trên trong Django!
Để bắt đầu, hãy tạo một thư mục mới cho dự án của chúng tôi, tạo và kích hoạt một môi trường ảo mới và cài đặt Django cùng với Django Compressor:
$ mkdir django-htmx-tailwind && cd django-htmx-tailwind
$ python3.10 -m venv venv
$ source venv/bin/activate
(venv)$
(venv)$ pip install Django==4.0.3 django-compressor==3.1
Tiếp theo, hãy cài đặt pytailwindcss và tải xuống tệp nhị phân:
(venv)$ pip install pytailwindcss==0.1.4
(venv)$ tailwindcss
Tạo một dự án Django mới và một todos
ứng dụng:
(venv)$ django-admin startproject config .
(venv)$ python manage.py startapp todos
Thêm các ứng dụng vào INSTALLED_APPS
danh sách trong config / settings.py :
# config/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'todos', # new
'compressor', # new
]
Tạo một thư mục "mẫu" trong thư mục gốc của dự án của bạn. Sau đó, cập nhật các TEMPLATES
cài đặt như sau:
# config/settings.py
TEMPLATES = [
{
...
'DIRS': [BASE_DIR / 'templates'], # new
...
},
]
Hãy thêm một số cấu hình vào config / settings.py cho compressor
:
# config/settings.py
COMPRESS_ROOT = BASE_DIR / 'static'
COMPRESS_ENABLED = True
STATICFILES_FINDERS = ('compressor.finders.CompressorFinder',)
Ghi chú:
DEBUG
.django.contrib.staticfiles
được cài đặt.Khởi tạo CSS Tailwind trong dự án của bạn:
(venv)$ tailwindcss init
Lệnh này đã tạo một tệp tailwind.config.js trong thư mục gốc của dự án của bạn. Tất cả các tùy chỉnh liên quan đến Tailwind đều có trong tệp này.
Cập nhật tailwind.config.js như sau:
module.exports = {
content: [
'./templates/**/*.html',
],
theme: {
extend: {},
},
plugins: [],
}
Ghi chú phần nội dung . Tại đây, bạn định cấu hình đường dẫn đến các mẫu HTML của dự án. Tailwind CSS sẽ quét các mẫu của bạn, tìm kiếm tên lớp Tailwind. Tệp CSS đầu ra được tạo sẽ chỉ chứa CSS cho các tên lớp có liên quan được tìm thấy trong tệp mẫu của bạn. Điều này giúp giữ cho các tệp CSS được tạo nhỏ hơn vì chúng sẽ chỉ chứa các kiểu thực sự đang được sử dụng.
Tiếp theo, trong thư mục gốc của dự án, hãy tạo các tệp và thư mục sau:
static
└── src
└── main.css
Sau đó, thêm phần sau vào static / src / main.css :
/* static/src/main.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
Ở đây, chúng tôi đã xác định tất cả base
và components
các utilities
lớp từ Tailwind CSS.
Đó là nó. Bây giờ bạn có Máy nén Django và Tailwind được kết nối. Tiếp theo, chúng ta sẽ xem xét cách cung cấp tệp index.html để xem CSS đang hoạt động.
Cập nhật tệp todos / views.py như sau:
# todos/views.py
from django.shortcuts import render
def index(request):
return render(request, 'index.html')
Thêm chế độ xem vào todos / urls.py :
# todos/urls.py
from django.urls import path
from .views import index
urlpatterns = [
path('', index, name='index'),
]
Sau đó, thêm todos.urls
vào config / urls.py :
# config/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('todos.urls')), # new
]
Thêm tệp _base.html vào "mẫu":
<!-- templates/_base.html -->
{% load compress %}
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Django + HTMX + Tailwind CSS</title>
{% compress css %}
<link rel="stylesheet" href="{% static 'src/output.css' %}">
{% endcompress %}
</head>
<body class="bg-blue-100">
{% block content %}
{% endblock content %}
</body>
</html>
Ghi chú:
{% load compress %}
nhập tất cả các thẻ cần thiết để hoạt động với Django Compressor.{% load static %}
tải các tệp tĩnh vào mẫu.{% compress css %}
áp dụng các bộ lọc thích hợp cho tệp static / src / main.css .Ngoài ra, chúng tôi đã thêm một số màu vào nội dung HTML thông qua <body class="bg-blue-100">
. bg-blue-100
được sử dụng để thay đổi màu nền thành xanh lam nhạt.
Thêm tệp index.html :
<!-- templates/index.html -->
{% extends "_base.html" %}
{% block content %}
<h1>Hello World</h1>
{% endblock content %}
Bây giờ, hãy chạy lệnh sau trong thư mục gốc của dự án để quét các mẫu cho các lớp và tạo tệp CSS:
(venv)$ tailwindcss -i ./static/src/main.css -o ./static/src/output.css --minify
Áp dụng di chuyển và chạy máy chủ phát triển:
(venv)$ python manage.py migrate
(venv)$ python manage.py runserver
Điều hướng đến http: // localhost: 8000 trong trình duyệt của bạn để xem kết quả. Cũng lưu ý về tệp được tạo trong thư mục "static / CACHE / css".
Với Tailwind được định cấu hình, hãy thêm htmx vào hỗn hợp và tạo một tìm kiếm trực tiếp hiển thị kết quả khi bạn nhập.
Thay vì tìm nạp thư viện htmx từ CDN, hãy tải xuống và sử dụng Django Compressor để gói nó.
Tải xuống thư viện từ https://unpkg.com/htmx.org@1.7.0/dist/htmx.js và lưu nó vào static / src / htmx.js.
Để chúng tôi có một số dữ liệu để làm việc, hãy lưu https://github.com/testdrivenio/django-htmx-tailwind/blob/master/todos/todo.py vào một tệp mới có tên todos / todo.py.
Bây giờ, hãy thêm chế độ xem để triển khai chức năng tìm kiếm vào todos / views.py :
# todos/views.py
from django.shortcuts import render
from django.views.decorators.http import require_http_methods # new
from .todo import todos # new
def index(request):
return render(request, 'index.html', {'todos': []}) # modified
# new
@require_http_methods(['POST'])
def search(request):
res_todos = []
search = request.POST['search']
if len(search) == 0:
return render(request, 'todo.html', {'todos': []})
for i in todos:
if search in i['title']:
res_todos.append(i)
return render(request, 'todo.html', {'todos': res_todos})
Chúng tôi đã thêm một chế độ xem mới, search
tìm kiếm các việc cần làm và hiển thị mẫu todo.html với tất cả các kết quả.
Thêm dạng xem mới tạo vào todos / urls.py :
# todos/urls.py
from django.urls import path
from .views import index, search # modified
urlpatterns = [
path('', index, name='index'),
path('search/', search, name='search'), # new
]
Tiếp theo, thêm nội dung mới vào tệp _base.html :
<!-- templates/base.html -->
{% load compress %}
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Django + HTMX + Tailwind CSS</title>
{% compress css %}
<link rel="stylesheet" href="{% static 'src/output.css' %}">
{% endcompress %}
</head>
<body class="bg-blue-100">
{% block content %}
{% endblock content %}
<!-- new -->
{% compress js %}
<script type="text/javascript" src="{% static 'src/htmx.js' %}"></script>
{% endcompress %}
<!-- new -->
<script>
document.body.addEventListener('htmx:configRequest', (event) => {
event.detail.headers['X-CSRFToken'] = '{{ csrf_token }}';
})
</script>
</body>
</html>
Chúng tôi đã tải thư viện htmx bằng {% compress js %}
thẻ. Theo mặc địnhjs
, thẻ được áp dụng (theo đó, áp dụng rjsmin ). Vì vậy, điều này sẽ giảm thiểu static / src / htmx.js và phục vụ nó từ thư mục "static / CACHE".JSMinFilter
Chúng tôi cũng đã thêm tập lệnh sau:
document.body.addEventListener('htmx:configRequest', (event) => {
event.detail.headers['X-CSRFToken'] = '{{ csrf_token }}';
})
Trình nghe sự kiện này thêm mã thông báo CSRF vào tiêu đề yêu cầu.
Tiếp theo, hãy thêm khả năng tìm kiếm dựa trên tiêu đề của từng việc làm.
Cập nhật tệp index.html như sau:
<!-- templates/index.html -->
{% extends "_base.html" %}
{% block content %}
<div class="w-small w-2/3 mx-auto py-10 text-gray-600">
<input
type="text"
name="search"
hx-post="/search/"
hx-trigger="keyup changed delay:250ms"
hx-indicator=".htmx-indicator"
hx-target="#todo-results"
placeholder="Search"
class="bg-white h-10 px-5 pr-10 rounded-full text-2xl focus:outline-none"
>
<span class="htmx-indicator">Searching...</span>
</div>
<table class="border-collapse w-small w-2/3 mx-auto">
<thead>
<tr>
<th class="p-3 font-bold uppercase bg-gray-200 text-gray-600 border border-gray-300 hidden lg:table-cell">#</th>
<th class="p-3 font-bold uppercase bg-gray-200 text-gray-600 border border-gray-300 hidden lg:table-cell">Title</th>
<th class="p-3 font-bold uppercase bg-gray-200 text-gray-600 border border-gray-300 hidden lg:table-cell">Completed</th>
</tr>
</thead>
<tbody id="todo-results">
{% include "todo.html" %}
</tbody>
</table>
{% endblock content %}
Hãy dành một chút thời gian để xem xét các thuộc tính được xác định từ htmx:
<input
type="text"
name="search"
hx-post="/search/"
hx-trigger="keyup changed delay:250ms"
hx-indicator=".htmx-indicator"
hx-target="#todo-results"
placeholder="Search"
class="bg-white h-10 px-5 pr-10 rounded-full text-2xl focus:outline-none"
>
/search
điểm cuối.#todo-results
phần tử.Thêm tệp tin mẫu / todo.html :
<!-- templates/todo.html -->
{% for todo in todos %}
<tr
class="bg-white lg:hover:bg-gray-100 flex lg:table-row flex-row lg:flex-row flex-wrap lg:flex-no-wrap mb-10 lg:mb-0">
<td class="w-full lg:w-auto p-3 text-gray-800 text-center border border-b block lg:table-cell relative lg:static">
{{todo.id}}
</td>
<td class="w-full lg:w-auto p-3 text-gray-800 text-center border border-b block lg:table-cell relative lg:static">
{{todo.title}}
</td>
<td class="w-full lg:w-auto p-3 text-gray-800 text-center border border-b block lg:table-cell relative lg:static">
{% if todo.completed %}
<span class="rounded bg-green-400 py-1 px-3 text-xs font-bold">Yes</span>
{% else %}
<span class="rounded bg-red-400 py-1 px-3 text-xs font-bold">No</span>
{% endif %}
</td>
</tr>
{% endfor %}
Tệp này hiển thị các việc cần làm phù hợp với truy vấn tìm kiếm của chúng tôi.
Tạo tệp src / output.css mới :
(venv)$ tailwindcss -i ./static/src/main.css -o ./static/src/output.css --minify
Chạy ứng dụng bằng cách sử dụng python manage.py runserver
và điều hướng đến http: // localhost: 8000 một lần nữa để kiểm tra.
Trong hướng dẫn này, chúng tôi đã xem xét cách:
htmx có thể hiển thị các phần tử mà không cần tải lại trang. Quan trọng nhất, bạn có thể đạt được điều này mà không cần viết bất kỳ JavaScript nào. Mặc dù điều này làm giảm số lượng công việc cần thiết ở phía máy khách, nhưng dữ liệu được gửi từ máy chủ có thể cao hơn vì nó đang gửi HTML được kết xuất.
Việc cung cấp các mẫu HTML từng phần như thế này đã phổ biến vào đầu những năm 2000. htmx cung cấp một bước ngoặt hiện đại cho cách tiếp cận này. Nói chung, việc cung cấp các mẫu từng phần đang trở nên phổ biến trở lại do các khung công tác phức tạp như React và Vue. Bạn cũng có thể thêm WebSockets vào hỗn hợp để cung cấp các thay đổi theo thời gian thực. Phương pháp tương tự này được sử dụng bởi Phoenix LiveView nổi tiếng . Bạn có thể đọc thêm về HTML qua WebSockets trong Tương lai của Phần mềm Web là HTML-over-WebSockets và HTML Over WebSockets .
Thư viện vẫn còn non trẻ, nhưng tương lai có vẻ rất tươi sáng.
Tailwind là một khung CSS mạnh mẽ tập trung vào năng suất của nhà phát triển. Mặc dù hướng dẫn này không liên quan đến nó, nhưng Tailwind có thể tùy chỉnh cao.
Khi sử dụng Django, hãy đảm bảo kết hợp cả htmx và Tailwind với Django Compressor để đơn giản hóa việc quản lý tài sản tĩnh.
Bạn có thể tìm thấy mã đầy đủ trong kho lưu trữ django-htmx-tailwind .
Nguồn: https://testdriven.io
1657018860
В этом руководстве вы узнаете, как настроить Django с помощью htmx и Tailwind CSS . Цель как htmx, так и Tailwind — упростить современную веб-разработку, чтобы вы могли проектировать и обеспечивать интерактивность, не отказываясь от удобства и простоты HTML. Мы также рассмотрим, как использовать Django Compressor для объединения и минимизации статических ресурсов в приложении Django.
htmx — это библиотека, которая позволяет вам получать доступ к современным функциям браузера, таким как AJAX, переходы CSS, WebSockets и события, отправленные сервером, непосредственно из HTML, а не с помощью JavaScript. Он позволяет быстро создавать пользовательские интерфейсы непосредственно в разметке.
htmx расширяет несколько функций, уже встроенных в браузер, таких как выполнение HTTP-запросов и реагирование на события. Например, вместо того, чтобы делать запросы GET и POST только через элементы a
и form
, вы можете использовать атрибуты HTML для отправки запросов GET, POST, PUT, PATCH или DELETE для любого элемента HTML:
<button hx-delete="/user/1">Delete</button>
Вы также можете обновить части страницы, чтобы создать одностраничное приложение (SPA): ссылка CodePen
Откройте вкладку сети в инструментах разработчика браузера. При нажатии кнопки на конечную точку отправляется запрос XHR https://v2.jokeapi.dev/joke/Any?format=txt&safe-mode
. Затем ответ добавляется к p
элементу с id
выводом.
Дополнительные примеры см . на странице примеров пользовательского интерфейса в официальной документации htmx.
Плюсы :
Минусы :
Tailwind CSS — это CSS-фреймворк, ориентированный прежде всего на полезность. Вместо того, чтобы поставлять готовые компоненты (на которых специализируются такие фреймворки, как Bootstrap и Bulma ), он предоставляет строительные блоки в виде служебных классов, которые позволяют быстро и легко создавать макеты и проекты.
Например, возьмите следующий HTML и CSS:
<style>
.hello {
height: 5px;
width: 10px;
background: gray;
border-width: 1px;
border-radius: 3px;
padding: 5px;
}
</style>
<div class="hello">Hello World</div>
Это можно реализовать с помощью Tailwind следующим образом:
<div class="h-1 w-2 bg-gray-600 border rounded-sm p-1">Hello World</div>
Воспользуйтесь конвертером CSS Tailwind , чтобы преобразовать необработанный CSS в эквивалентные служебные классы в Tailwind. Сравните результаты.
Плюсы :
<div class="bg-white dark:bg-black">
.Минусы :
Django Compressor — это расширение, предназначенное для управления (сжатия/кэширования) статическими ресурсами в приложении Django. С его помощью вы создаете простой конвейер активов для:
Итак, давайте посмотрим, как работать с каждым из вышеперечисленных инструментов в Django!
Для начала создайте новую папку для нашего проекта, создайте и активируйте новую виртуальную среду и установите Django вместе с Django Compressor:
$ mkdir django-htmx-tailwind && cd django-htmx-tailwind
$ python3.10 -m venv venv
$ source venv/bin/activate
(venv)$
(venv)$ pip install Django==4.0.3 django-compressor==3.1
Далее давайте установим pytailwindcss и загрузим его бинарник:
(venv)$ pip install pytailwindcss==0.1.4
(venv)$ tailwindcss
Создайте новый проект Django и todos
приложение:
(venv)$ django-admin startproject config .
(venv)$ python manage.py startapp todos
Добавьте приложения в INSTALLED_APPS
список в config/settings.py :
# config/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'todos', # new
'compressor', # new
]
Создайте папку «templates» в корне вашего проекта. Затем обновите TEMPLATES
настройки следующим образом:
# config/settings.py
TEMPLATES = [
{
...
'DIRS': [BASE_DIR / 'templates'], # new
...
},
]
Давайте добавим некоторую конфигурацию в config/settings.py для compressor
:
# config/settings.py
COMPRESS_ROOT = BASE_DIR / 'static'
COMPRESS_ENABLED = True
STATICFILES_FINDERS = ('compressor.finders.CompressorFinder',)
Заметки:
DEBUG
.django.contrib.staticfiles
установке.Инициализируйте Tailwind CSS в своем проекте:
(venv)$ tailwindcss init
Эта команда создала файл tailwind.config.js в корне вашего проекта. Все настройки, связанные с Tailwind, находятся в этом файле.
Обновите tailwind.config.js следующим образом:
module.exports = {
content: [
'./templates/**/*.html',
],
theme: {
extend: {},
},
plugins: [],
}
Обратите внимание на раздел контента . Здесь вы настраиваете пути к HTML-шаблонам вашего проекта. Tailwind CSS просканирует ваши шаблоны в поисках имен классов Tailwind. Сгенерированный выходной файл CSS будет содержать только CSS для соответствующих имен классов, найденных в ваших файлах шаблонов. Это помогает уменьшить размер сгенерированных файлов CSS, поскольку они будут содержать только те стили, которые фактически используются.
Далее в корне проекта создайте следующие файлы и папки:
static
└── src
└── main.css
Затем добавьте в static/src/main.css следующее :
/* static/src/main.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
Здесь мы определили все классы base
, components
и utilities
из Tailwind CSS.
Вот и все. Теперь у вас подключены Django Compressor и Tailwind. Далее мы рассмотрим, как обслуживать файл index.html , чтобы увидеть CSS в действии.
Обновите файл todos/views.py следующим образом:
# todos/views.py
from django.shortcuts import render
def index(request):
return render(request, 'index.html')
Добавьте представление в todos/urls.py :
# todos/urls.py
from django.urls import path
from .views import index
urlpatterns = [
path('', index, name='index'),
]
Затем добавьте todos.urls
в config/urls.py :
# config/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('todos.urls')), # new
]
Добавьте файл _base.html в «шаблоны»:
<!-- templates/_base.html -->
{% load compress %}
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Django + HTMX + Tailwind CSS</title>
{% compress css %}
<link rel="stylesheet" href="{% static 'src/output.css' %}">
{% endcompress %}
</head>
<body class="bg-blue-100">
{% block content %}
{% endblock content %}
</body>
</html>
Заметки:
{% load compress %}
импортирует все необходимые теги для работы с Django Compressor.{% load static %}
загружает статические файлы в шаблон.{% compress css %}
применяет соответствующие фильтры к файлу static/src/main.css .Кроме того, мы добавили немного цвета в тело HTML с помощью <body class="bg-blue-100">
. bg-blue-100
используется для изменения цвета фона на голубой.
Добавьте файл index.html :
<!-- templates/index.html -->
{% extends "_base.html" %}
{% block content %}
<h1>Hello World</h1>
{% endblock content %}
Теперь выполните следующую команду в корне проекта, чтобы отсканировать шаблоны на наличие классов и сгенерировать файл CSS:
(venv)$ tailwindcss -i ./static/src/main.css -o ./static/src/output.css --minify
Примените миграции и запустите сервер разработки:
(venv)$ python manage.py migrate
(venv)$ python manage.py runserver
Перейдите по адресу http://localhost:8000 в браузере, чтобы увидеть результаты. Также обратите внимание на сгенерированный файл в папке «static/CACHE/css».
С настроенным Tailwind давайте добавим htmx и создадим живой поиск, который отображает результаты по мере ввода.
Вместо того, чтобы извлекать библиотеку htmx из CDN, давайте загрузим ее и используем Django Compressor для ее сборки.
Загрузите библиотеку с https://unpkg.com/htmx.org@1.7.0/dist/htmx.js и сохраните ее в static/src/htmx.js .
Чтобы у нас были данные для работы, сохраните https://github.com/testdrivenio/django-htmx-tailwind/blob/master/todos/todo.py в новый файл с именем todos/todo.py .
Теперь добавьте представление для реализации функции поиска в todos/views.py :
# todos/views.py
from django.shortcuts import render
from django.views.decorators.http import require_http_methods # new
from .todo import todos # new
def index(request):
return render(request, 'index.html', {'todos': []}) # modified
# new
@require_http_methods(['POST'])
def search(request):
res_todos = []
search = request.POST['search']
if len(search) == 0:
return render(request, 'todo.html', {'todos': []})
for i in todos:
if search in i['title']:
res_todos.append(i)
return render(request, 'todo.html', {'todos': res_todos})
Мы добавили новое представление, search
которое ищет задачи и отображает шаблон todo.html со всеми результатами.
Добавьте только что созданное представление в todos/urls.py :
# todos/urls.py
from django.urls import path
from .views import index, search # modified
urlpatterns = [
path('', index, name='index'),
path('search/', search, name='search'), # new
]
Затем добавьте новый ресурс в файл _base.html :
<!-- templates/base.html -->
{% load compress %}
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Django + HTMX + Tailwind CSS</title>
{% compress css %}
<link rel="stylesheet" href="{% static 'src/output.css' %}">
{% endcompress %}
</head>
<body class="bg-blue-100">
{% block content %}
{% endblock content %}
<!-- new -->
{% compress js %}
<script type="text/javascript" src="{% static 'src/htmx.js' %}"></script>
{% endcompress %}
<!-- new -->
<script>
document.body.addEventListener('htmx:configRequest', (event) => {
event.detail.headers['X-CSRFToken'] = '{{ csrf_token }}';
})
</script>
</body>
</html>
Мы загрузили библиотеку htmx с помощью {% compress js %}
тега. Тег js
по умолчанию применяется JSMinFilter
(который, в свою очередь, применяет rjsmin ). Таким образом, это минимизирует static/src/htmx.js и будет обслуживать его из папки «static/CACHE».
Мы также добавили следующий скрипт:
document.body.addEventListener('htmx:configRequest', (event) => {
event.detail.headers['X-CSRFToken'] = '{{ csrf_token }}';
})
Этот прослушиватель событий добавляет токен CSRF в заголовок запроса.
Далее давайте добавим возможность поиска по названию каждой задачи.
Обновите файл index.html следующим образом:
<!-- templates/index.html -->
{% extends "_base.html" %}
{% block content %}
<div class="w-small w-2/3 mx-auto py-10 text-gray-600">
<input
type="text"
name="search"
hx-post="/search/"
hx-trigger="keyup changed delay:250ms"
hx-indicator=".htmx-indicator"
hx-target="#todo-results"
placeholder="Search"
class="bg-white h-10 px-5 pr-10 rounded-full text-2xl focus:outline-none"
>
<span class="htmx-indicator">Searching...</span>
</div>
<table class="border-collapse w-small w-2/3 mx-auto">
<thead>
<tr>
<th class="p-3 font-bold uppercase bg-gray-200 text-gray-600 border border-gray-300 hidden lg:table-cell">#</th>
<th class="p-3 font-bold uppercase bg-gray-200 text-gray-600 border border-gray-300 hidden lg:table-cell">Title</th>
<th class="p-3 font-bold uppercase bg-gray-200 text-gray-600 border border-gray-300 hidden lg:table-cell">Completed</th>
</tr>
</thead>
<tbody id="todo-results">
{% include "todo.html" %}
</tbody>
</table>
{% endblock content %}
Давайте рассмотрим атрибуты, определенные в htmx:
<input
type="text"
name="search"
hx-post="/search/"
hx-trigger="keyup changed delay:250ms"
hx-indicator=".htmx-indicator"
hx-target="#todo-results"
placeholder="Search"
class="bg-white h-10 px-5 pr-10 rounded-full text-2xl focus:outline-none"
>
/search
конечную точку.#todo-results
элементе.Добавьте файл templates/todo.html :
<!-- templates/todo.html -->
{% for todo in todos %}
<tr
class="bg-white lg:hover:bg-gray-100 flex lg:table-row flex-row lg:flex-row flex-wrap lg:flex-no-wrap mb-10 lg:mb-0">
<td class="w-full lg:w-auto p-3 text-gray-800 text-center border border-b block lg:table-cell relative lg:static">
{{todo.id}}
</td>
<td class="w-full lg:w-auto p-3 text-gray-800 text-center border border-b block lg:table-cell relative lg:static">
{{todo.title}}
</td>
<td class="w-full lg:w-auto p-3 text-gray-800 text-center border border-b block lg:table-cell relative lg:static">
{% if todo.completed %}
<span class="rounded bg-green-400 py-1 px-3 text-xs font-bold">Yes</span>
{% else %}
<span class="rounded bg-red-400 py-1 px-3 text-xs font-bold">No</span>
{% endif %}
</td>
</tr>
{% endfor %}
Этот файл отображает задачи, соответствующие нашему поисковому запросу.
Создайте новый файл src/output.css :
(venv)$ tailwindcss -i ./static/src/main.css -o ./static/src/output.css --minify
Запустите приложение с помощью python manage.py runserver
и снова перейдите по адресу http://localhost:8000 , чтобы проверить его.
В этом уроке мы рассмотрели, как:
htmx может отображать элементы без перезагрузки страницы. Самое главное, вы можете добиться этого без написания JavaScript. Хотя это уменьшает объем работы, необходимой на стороне клиента, данные, отправляемые с сервера, могут быть выше, поскольку он отправляет обработанный HTML.
Подобные частичные HTML-шаблоны были популярны в начале 2000-х годов. htmx предлагает современный подход к этому подходу. В целом, обслуживание частичных шаблонов снова становится популярным из-за сложности таких фреймворков, как React и Vue. Вы также можете добавить WebSockets для внесения изменений в реальном времени. Этот же подход используется в знаменитом Phoenix LiveView . Вы можете узнать больше о HTML поверх WebSockets в статье Будущее веб-программного обеспечения — это HTML поверх WebSockets и HTML поверх WebSockets .
Библиотека еще молода, но будущее у нее очень светлое.
Tailwind — это мощная CSS-инфраструктура, ориентированная на продуктивность разработчиков. Хотя это руководство не затрагивало этого, Tailwind легко настраивается.
При использовании Django обязательно соедините htmx и Tailwind с Django Compressor, чтобы упростить управление статическими активами.
Полный код можно найти в репозитории django-htmx-tailwind .
Источник: https://testdriven.io
1657007820
Dans ce didacticiel, vous apprendrez à configurer Django avec htmx et Tailwind CSS . L'objectif de htmx et de Tailwind est de simplifier le développement Web moderne afin que vous puissiez concevoir et activer l'interactivité sans jamais quitter le confort et la facilité du HTML. Nous verrons également comment utiliser Django Compressor pour regrouper et minimiser les actifs statiques dans une application Django.
htmx est une bibliothèque qui vous permet d'accéder à des fonctionnalités de navigateur modernes telles que AJAX, CSS Transitions, WebSockets et Server-Sent Events directement à partir de HTML, plutôt que d'utiliser JavaScript. Il vous permet de créer rapidement des interfaces utilisateur directement dans le balisage.
htmx étend plusieurs fonctionnalités déjà intégrées au navigateur, telles que les requêtes HTTP et la réponse aux événements. Par exemple, plutôt que de pouvoir uniquement effectuer des requêtes GET et POST via des éléments a
et form
, vous pouvez utiliser des attributs HTML pour envoyer des requêtes GET, POST, PUT, PATCH ou DELETE sur n'importe quel élément HTML :
<button hx-delete="/user/1">Delete</button>
Vous pouvez également mettre à jour des parties d'une page pour créer une application monopage (SPA) : lien CodePen
Ouvrez l'onglet réseau dans les outils de développement du navigateur. Lorsque le bouton est cliqué, une demande XHR est envoyée au point de https://v2.jokeapi.dev/joke/Any?format=txt&safe-mode
terminaison. La réponse est ensuite ajoutée à l' p
élément avec une id
sortie of.
Pour plus d'exemples, consultez la page Exemples d'interface utilisateur de la documentation officielle htmx.
Avantages :
Inconvénients :
Tailwind CSS est un framework CSS "utility-first". Plutôt que d'expédier des composants prédéfinis (dans lesquels se spécialisent des frameworks comme Bootstrap et Bulma ), il fournit des blocs de construction sous la forme de classes utilitaires qui permettent de créer des mises en page et des conceptions rapidement et facilement.
Par exemple, prenez le code HTML et CSS suivant :
<style>
.hello {
height: 5px;
width: 10px;
background: gray;
border-width: 1px;
border-radius: 3px;
padding: 5px;
}
</style>
<div class="hello">Hello World</div>
Cela peut être implémenté avec Tailwind comme ceci :
<div class="h-1 w-2 bg-gray-600 border rounded-sm p-1">Hello World</div>
Découvrez le convertisseur CSS Tailwind pour convertir le CSS brut en classes utilitaires équivalentes dans Tailwind. Comparez les résultats.
Avantages :
<div class="bg-white dark:bg-black">
.Inconvénients :
Django Compressor est une extension conçue pour gérer (compresser/mettre en cache) les actifs statiques dans une application Django. Avec lui, vous créez un pipeline d'actifs simple pour :
Sur ce, regardons comment travailler avec chacun des outils ci-dessus dans Django !
Pour commencer, créez un nouveau dossier pour notre projet, créez et activez un nouvel environnement virtuel et installez Django avec Django Compressor :
$ mkdir django-htmx-tailwind && cd django-htmx-tailwind
$ python3.10 -m venv venv
$ source venv/bin/activate
(venv)$
(venv)$ pip install Django==4.0.3 django-compressor==3.1
Ensuite, installons pytailwindcss et téléchargeons son binaire :
(venv)$ pip install pytailwindcss==0.1.4
(venv)$ tailwindcss
Créez un nouveau projet Django et une todos
application :
(venv)$ django-admin startproject config .
(venv)$ python manage.py startapp todos
Ajoutez les applications à la INSTALLED_APPS
liste dans config/settings.py :
# config/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'todos', # new
'compressor', # new
]
Créez un dossier "templates" à la racine de votre projet. Ensuite, mettez à jour les TEMPLATES
paramètres comme suit :
# config/settings.py
TEMPLATES = [
{
...
'DIRS': [BASE_DIR / 'templates'], # new
...
},
]
Ajoutons un peu de configuration à config/settings.py pour compressor
:
# config/settings.py
COMPRESS_ROOT = BASE_DIR / 'static'
COMPRESS_ENABLED = True
STATICFILES_FINDERS = ('compressor.finders.CompressorFinder',)
Remarques:
DEBUG
.django.contrib.staticfiles
est installé.Initialisez Tailwind CSS dans votre projet :
(venv)$ tailwindcss init
Cette commande a créé un fichier tailwind.config.js à la racine de votre projet. Toutes les personnalisations liées à Tailwind vont dans ce fichier.
Mettez à jour tailwind.config.js comme suit :
module.exports = {
content: [
'./templates/**/*.html',
],
theme: {
extend: {},
},
plugins: [],
}
Prenez note de la section de contenu . Ici, vous configurez les chemins vers les modèles HTML de votre projet. Tailwind CSS analysera vos modèles, recherchant les noms de classe Tailwind. Le fichier CSS de sortie généré contiendra uniquement le CSS pour les noms de classe pertinents trouvés dans vos fichiers de modèle. Cela aide à garder les fichiers CSS générés petits puisqu'ils ne contiendront que les styles qui sont réellement utilisés.
Ensuite, à la racine du projet, créez les fichiers et dossiers suivants :
static
└── src
└── main.css
Ensuite, ajoutez ce qui suit à static/src/main.css :
/* static/src/main.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
Ici, nous avons défini toutes les classes base
, components
et utilities
de Tailwind CSS.
C'est ça. Vous avez maintenant Django Compressor et Tailwind câblés. Ensuite, nous verrons comment servir un fichier index.html pour voir le CSS en action.
Mettez à jour le fichier todos/views.py comme ceci :
# todos/views.py
from django.shortcuts import render
def index(request):
return render(request, 'index.html')
Ajoutez la vue à todos/urls.py :
# todos/urls.py
from django.urls import path
from .views import index
urlpatterns = [
path('', index, name='index'),
]
Ensuite, ajoutez todos.urls
à config/urls.py :
# config/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('todos.urls')), # new
]
Ajoutez un fichier _base.html à "templates" :
<!-- templates/_base.html -->
{% load compress %}
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Django + HTMX + Tailwind CSS</title>
{% compress css %}
<link rel="stylesheet" href="{% static 'src/output.css' %}">
{% endcompress %}
</head>
<body class="bg-blue-100">
{% block content %}
{% endblock content %}
</body>
</html>
Remarques:
{% load compress %}
importe toutes les balises requises pour fonctionner avec Django Compressor.{% load static %}
charge les fichiers statiques dans le modèle.{% compress css %}
applique les filtres appropriés au fichier static/src/main.css .De plus, nous avons ajouté de la couleur au corps HTML via <body class="bg-blue-100">
. bg-blue-100
est utilisé pour changer la couleur de fond en bleu clair.
Ajoutez un fichier index.html :
<!-- templates/index.html -->
{% extends "_base.html" %}
{% block content %}
<h1>Hello World</h1>
{% endblock content %}
Maintenant, exécutez la commande suivante à la racine du projet pour analyser les modèles de classes et générer un fichier CSS :
(venv)$ tailwindcss -i ./static/src/main.css -o ./static/src/output.css --minify
Appliquez les migrations et exécutez le serveur de développement :
(venv)$ python manage.py migrate
(venv)$ python manage.py runserver
Accédez à http://localhost:8000 dans votre navigateur pour voir les résultats. Prenez également note du fichier généré dans le dossier "static/CACHE/css".
Avec Tailwind configuré, ajoutons htmx dans le mélange et créons une recherche en direct qui affiche les résultats au fur et à mesure que vous tapez.
Plutôt que de récupérer la bibliothèque htmx à partir d'un CDN, téléchargeons-la et utilisons Django Compressor pour la regrouper.
Téléchargez la bibliothèque depuis https://unpkg.com/htmx.org@1.7.0/dist/htmx.js et enregistrez-la dans static/src/htmx.js .
Pour que nous ayons des données avec lesquelles travailler, enregistrez https://github.com/testdriveio/django-htmx-tailwind/blob/master/todos/todo.py dans un nouveau fichier appelé todos/todo.py .
Maintenant, ajoutez la vue pour implémenter la fonctionnalité de recherche à todos/views.py :
# todos/views.py
from django.shortcuts import render
from django.views.decorators.http import require_http_methods # new
from .todo import todos # new
def index(request):
return render(request, 'index.html', {'todos': []}) # modified
# new
@require_http_methods(['POST'])
def search(request):
res_todos = []
search = request.POST['search']
if len(search) == 0:
return render(request, 'todo.html', {'todos': []})
for i in todos:
if search in i['title']:
res_todos.append(i)
return render(request, 'todo.html', {'todos': res_todos})
Nous avons ajouté une nouvelle vue, search
, qui recherche les tâches et affiche le modèle todo.html avec tous les résultats.
Ajoutez la vue nouvellement créée à todos/urls.py :
# todos/urls.py
from django.urls import path
from .views import index, search # modified
urlpatterns = [
path('', index, name='index'),
path('search/', search, name='search'), # new
]
Ensuite, ajoutez le nouvel élément au fichier _base.html :
<!-- templates/base.html -->
{% load compress %}
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Django + HTMX + Tailwind CSS</title>
{% compress css %}
<link rel="stylesheet" href="{% static 'src/output.css' %}">
{% endcompress %}
</head>
<body class="bg-blue-100">
{% block content %}
{% endblock content %}
<!-- new -->
{% compress js %}
<script type="text/javascript" src="{% static 'src/htmx.js' %}"></script>
{% endcompress %}
<!-- new -->
<script>
document.body.addEventListener('htmx:configRequest', (event) => {
event.detail.headers['X-CSRFToken'] = '{{ csrf_token }}';
})
</script>
</body>
</html>
Nous avons chargé la bibliothèque htmx en utilisant la {% compress js %}
balise. La js
balise, par défaut , s'applique JSMinFilter
(qui, à son tour, applique rjsmin ). Ainsi, cela réduira static/src/htmx.js et le servira à partir du dossier "static/CACHE".
Nous avons également ajouté le script suivant :
document.body.addEventListener('htmx:configRequest', (event) => {
event.detail.headers['X-CSRFToken'] = '{{ csrf_token }}';
})
Cet écouteur d'événement ajoute le jeton CSRF à l'en-tête de la requête.
Ensuite, ajoutons la possibilité de rechercher en fonction du titre de chaque tâche.
Mettez à jour le fichier index.html comme suit :
<!-- templates/index.html -->
{% extends "_base.html" %}
{% block content %}
<div class="w-small w-2/3 mx-auto py-10 text-gray-600">
<input
type="text"
name="search"
hx-post="/search/"
hx-trigger="keyup changed delay:250ms"
hx-indicator=".htmx-indicator"
hx-target="#todo-results"
placeholder="Search"
class="bg-white h-10 px-5 pr-10 rounded-full text-2xl focus:outline-none"
>
<span class="htmx-indicator">Searching...</span>
</div>
<table class="border-collapse w-small w-2/3 mx-auto">
<thead>
<tr>
<th class="p-3 font-bold uppercase bg-gray-200 text-gray-600 border border-gray-300 hidden lg:table-cell">#</th>
<th class="p-3 font-bold uppercase bg-gray-200 text-gray-600 border border-gray-300 hidden lg:table-cell">Title</th>
<th class="p-3 font-bold uppercase bg-gray-200 text-gray-600 border border-gray-300 hidden lg:table-cell">Completed</th>
</tr>
</thead>
<tbody id="todo-results">
{% include "todo.html" %}
</tbody>
</table>
{% endblock content %}
Prenons un moment pour examiner les attributs définis à partir de htmx :
<input
type="text"
name="search"
hx-post="/search/"
hx-trigger="keyup changed delay:250ms"
hx-indicator=".htmx-indicator"
hx-target="#todo-results"
placeholder="Search"
class="bg-white h-10 px-5 pr-10 rounded-full text-2xl focus:outline-none"
>
/search
terminaison.#todo-results
élément.Ajoutez le fichier templates/todo.html :
<!-- templates/todo.html -->
{% for todo in todos %}
<tr
class="bg-white lg:hover:bg-gray-100 flex lg:table-row flex-row lg:flex-row flex-wrap lg:flex-no-wrap mb-10 lg:mb-0">
<td class="w-full lg:w-auto p-3 text-gray-800 text-center border border-b block lg:table-cell relative lg:static">
{{todo.id}}
</td>
<td class="w-full lg:w-auto p-3 text-gray-800 text-center border border-b block lg:table-cell relative lg:static">
{{todo.title}}
</td>
<td class="w-full lg:w-auto p-3 text-gray-800 text-center border border-b block lg:table-cell relative lg:static">
{% if todo.completed %}
<span class="rounded bg-green-400 py-1 px-3 text-xs font-bold">Yes</span>
{% else %}
<span class="rounded bg-red-400 py-1 px-3 text-xs font-bold">No</span>
{% endif %}
</td>
</tr>
{% endfor %}
Ce fichier affiche les tâches qui correspondent à notre requête de recherche.
Générez un nouveau fichier src/output.css :
(venv)$ tailwindcss -i ./static/src/main.css -o ./static/src/output.css --minify
Exécutez l'application à l'aide python manage.py runserver
de et accédez à nouveau à http://localhost:8000 pour la tester.
Dans ce tutoriel, nous avons vu comment :
htmx peut afficher des éléments sans recharger la page. Plus important encore, vous pouvez y parvenir sans écrire de code JavaScript. Bien que cela réduise la quantité de travail requise côté client, les données envoyées depuis le serveur peuvent être plus élevées car il envoie du HTML rendu.
Servir des modèles HTML partiels comme celui-ci était populaire au début des années 2000. htmx apporte une touche moderne à cette approche. En général, la diffusion de modèles partiels redevient populaire en raison de la complexité des frameworks tels que React et Vue. Vous pouvez ajouter WebSockets dans le mix pour apporter également des modifications en temps réel. Cette même approche est utilisée par le célèbre Phoenix LiveView . Vous pouvez en savoir plus sur HTML over WebSockets dans The Future of Web Software Is HTML-over-WebSockets and HTML Over WebSockets .
La bibliothèque est encore jeune, mais l'avenir s'annonce très prometteur.
Tailwind est un framework CSS puissant qui se concentre sur la productivité des développeurs. Bien que ce didacticiel n'en ait pas parlé, Tailwind est hautement personnalisable.
Lorsque vous utilisez Django, assurez-vous de coupler à la fois htmx et Tailwind avec Django Compressor pour simplifier la gestion des ressources statiques.
Le code complet se trouve dans le dépôt django-htmx-tailwind .
Source : https://testdrive.io
1657000560
En este tutorial, aprenderá cómo configurar Django con htmx y Tailwind CSS . El objetivo de htmx y Tailwind es simplificar el desarrollo web moderno para que pueda diseñar y habilitar la interactividad sin dejar nunca la comodidad y la facilidad de HTML. También veremos cómo usar Django Compressor para agrupar y minimizar activos estáticos en una aplicación de Django.
htmx es una biblioteca que le permite acceder a funciones de navegador modernas como AJAX, CSS Transitions, WebSockets y Server-Sent Events directamente desde HTML, en lugar de usar JavaScript. Le permite crear interfaces de usuario rápidamente directamente en el marcado.
htmx amplía varias funciones ya integradas en el navegador, como realizar solicitudes HTTP y responder a eventos. Por ejemplo, en lugar de solo poder realizar solicitudes GET y POST a través de elementos a
y form
, puede usar atributos HTML para enviar solicitudes GET, POST, PUT, PATCH o DELETE en cualquier elemento HTML:
<button hx-delete="/user/1">Delete</button>
También puede actualizar partes de una página para crear una aplicación de página única (SPA): enlace CodePen
Abra la pestaña de red en las herramientas de desarrollo del navegador. Cuando se hace clic en el botón, se envía una solicitud XHR al https://v2.jokeapi.dev/joke/Any?format=txt&safe-mode
punto final. Luego, la respuesta se agrega al p
elemento con un id
de salida.
Para obtener más ejemplos, consulte la página de ejemplos de interfaz de usuario de los documentos oficiales de htmx.
Ventajas :
Contras :
Tailwind CSS es un marco CSS de "utilidad primero". En lugar de enviar componentes preconstruidos (en los que se especializan marcos como Bootstrap y Bulma ), proporciona bloques de construcción en forma de clases de utilidad que permiten crear diseños y diseños de forma rápida y sencilla.
Por ejemplo, tome el siguiente HTML y CSS:
<style>
.hello {
height: 5px;
width: 10px;
background: gray;
border-width: 1px;
border-radius: 3px;
padding: 5px;
}
</style>
<div class="hello">Hello World</div>
Esto se puede implementar con Tailwind así:
<div class="h-1 w-2 bg-gray-600 border rounded-sm p-1">Hello World</div>
Consulte el convertidor CSS Tailwind para convertir CSS sin formato en las clases de utilidad equivalentes en Tailwind. Compara los resultados.
Ventajas :
<div class="bg-white dark:bg-black">
.Contras :
Django Compressor es una extensión diseñada para administrar (comprimir/almacenar en caché) activos estáticos en una aplicación Django. Con él, crea una canalización de activos simple para:
Con eso, ¡veamos cómo trabajar con cada una de las herramientas anteriores en Django!
Para comenzar, crea una nueva carpeta para nuestro proyecto, crea y activa un nuevo entorno virtual e instala Django junto con Django Compressor:
$ mkdir django-htmx-tailwind && cd django-htmx-tailwind
$ python3.10 -m venv venv
$ source venv/bin/activate
(venv)$
(venv)$ pip install Django==4.0.3 django-compressor==3.1
A continuación, instalemos pytailwindcss y descarguemos su binario:
(venv)$ pip install pytailwindcss==0.1.4
(venv)$ tailwindcss
Cree un nuevo proyecto Django y una todos
aplicación:
(venv)$ django-admin startproject config .
(venv)$ python manage.py startapp todos
Agregue las aplicaciones a la INSTALLED_APPS
lista en config/settings.py :
# config/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'todos', # new
'compressor', # new
]
Cree una carpeta de "plantillas" en la raíz de su proyecto. Luego, actualice la TEMPLATES
configuración de esta manera:
# config/settings.py
TEMPLATES = [
{
...
'DIRS': [BASE_DIR / 'templates'], # new
...
},
]
Agreguemos alguna configuración a config/settings.py para compressor
:
# config/settings.py
COMPRESS_ROOT = BASE_DIR / 'static'
COMPRESS_ENABLED = True
STATICFILES_FINDERS = ('compressor.finders.CompressorFinder',)
Notas:
DEBUG
.django.contrib.staticfiles
se instala.Inicializa Tailwind CSS en tu proyecto:
(venv)$ tailwindcss init
Este comando creó un archivo tailwind.config.js en la raíz de su proyecto. Todas las personalizaciones relacionadas con Tailwind van a este archivo.
Actualice tailwind.config.js así:
module.exports = {
content: [
'./templates/**/*.html',
],
theme: {
extend: {},
},
plugins: [],
}
Toma nota de la sección de contenido . Aquí configura las rutas a las plantillas HTML de su proyecto. Tailwind CSS escaneará sus plantillas, buscando nombres de clases de Tailwind. El archivo CSS de salida generado solo contendrá CSS para los nombres de clase relevantes que se encuentran en sus archivos de plantilla. Esto ayuda a mantener pequeños los archivos CSS generados, ya que solo contendrán los estilos que realmente se están utilizando.
A continuación, en la raíz del proyecto, cree los siguientes archivos y carpetas:
static
└── src
└── main.css
Luego, agregue lo siguiente a static/src/main.css :
/* static/src/main.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
Aquí, definimos todas las clases base
, components
y utilities
de Tailwind CSS.
Eso es todo. Ahora tienes Django Compressor y Tailwind conectados. A continuación, veremos cómo publicar un archivo index.html para ver el CSS en acción.
Actualice el archivo todos/views.py así:
# todos/views.py
from django.shortcuts import render
def index(request):
return render(request, 'index.html')
Agregue la vista a todos/urls.py :
# todos/urls.py
from django.urls import path
from .views import index
urlpatterns = [
path('', index, name='index'),
]
Luego, agregue todos.urls
a config/urls.py :
# config/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('todos.urls')), # new
]
Agregue un archivo _base.html a "plantillas":
<!-- templates/_base.html -->
{% load compress %}
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Django + HTMX + Tailwind CSS</title>
{% compress css %}
<link rel="stylesheet" href="{% static 'src/output.css' %}">
{% endcompress %}
</head>
<body class="bg-blue-100">
{% block content %}
{% endblock content %}
</body>
</html>
Notas:
{% load compress %}
importa todas las etiquetas requeridas para trabajar con Django Compressor.{% load static %}
carga archivos estáticos en la plantilla.{% compress css %}
aplica los filtros apropiados al archivo static/src/main.css .Además, agregamos algo de color al cuerpo HTML a través de <body class="bg-blue-100">
. bg-blue-100
se utiliza para cambiar el color de fondo a azul claro.
Agregue un archivo index.html :
<!-- templates/index.html -->
{% extends "_base.html" %}
{% block content %}
<h1>Hello World</h1>
{% endblock content %}
Ahora, ejecute el siguiente comando en la raíz del proyecto para escanear las plantillas en busca de clases y generar un archivo CSS:
(venv)$ tailwindcss -i ./static/src/main.css -o ./static/src/output.css --minify
Aplique las migraciones y ejecute el servidor de desarrollo:
(venv)$ python manage.py migrate
(venv)$ python manage.py runserver
Navegue a http://localhost:8000 en su navegador para ver los resultados. También tome nota del archivo generado en la carpeta "static/CACHE/css".
Con Tailwind configurado, agreguemos htmx a la combinación y construyamos una búsqueda en vivo que muestre los resultados a medida que escribe.
En lugar de obtener la biblioteca htmx de un CDN, descárguemosla y usemos Django Compressor para agruparla.
Descargue la biblioteca de https://unpkg.com/htmx.org@1.7.0/dist/htmx.js y guárdela en static/src/htmx.js .
Para que tengamos algunos datos con los que trabajar, guarde https://github.com/testdrivenio/django-htmx-tailwind/blob/master/todos/todo.py en un nuevo archivo llamado todos/todo.py .
Ahora, agregue la vista para implementar la función de búsqueda en todos/views.py :
# todos/views.py
from django.shortcuts import render
from django.views.decorators.http import require_http_methods # new
from .todo import todos # new
def index(request):
return render(request, 'index.html', {'todos': []}) # modified
# new
@require_http_methods(['POST'])
def search(request):
res_todos = []
search = request.POST['search']
if len(search) == 0:
return render(request, 'todo.html', {'todos': []})
for i in todos:
if search in i['title']:
res_todos.append(i)
return render(request, 'todo.html', {'todos': res_todos})
Agregamos una nueva vista, search
, que busca todos y presenta la plantilla todo.html con todos los resultados.
Agregue la vista recién creada a todos/urls.py :
# todos/urls.py
from django.urls import path
from .views import index, search # modified
urlpatterns = [
path('', index, name='index'),
path('search/', search, name='search'), # new
]
A continuación, agregue el nuevo activo al archivo _base.html :
<!-- templates/base.html -->
{% load compress %}
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Django + HTMX + Tailwind CSS</title>
{% compress css %}
<link rel="stylesheet" href="{% static 'src/output.css' %}">
{% endcompress %}
</head>
<body class="bg-blue-100">
{% block content %}
{% endblock content %}
<!-- new -->
{% compress js %}
<script type="text/javascript" src="{% static 'src/htmx.js' %}"></script>
{% endcompress %}
<!-- new -->
<script>
document.body.addEventListener('htmx:configRequest', (event) => {
event.detail.headers['X-CSRFToken'] = '{{ csrf_token }}';
})
</script>
</body>
</html>
Cargamos la biblioteca htmx usando la {% compress js %}
etiqueta. Se aplica la js
etiqueta, de forma predeterminadaJSMinFilter
(que, a su vez, aplica rjsmin ). Entonces, esto minimizará static/src/htmx.js y lo servirá desde la carpeta "static/CACHE".
También agregamos el siguiente script:
document.body.addEventListener('htmx:configRequest', (event) => {
event.detail.headers['X-CSRFToken'] = '{{ csrf_token }}';
})
Este detector de eventos agrega el token CSRF al encabezado de la solicitud.
A continuación, agreguemos la capacidad de buscar según el título de cada tarea pendiente.
Actualice el archivo index.html así:
<!-- templates/index.html -->
{% extends "_base.html" %}
{% block content %}
<div class="w-small w-2/3 mx-auto py-10 text-gray-600">
<input
type="text"
name="search"
hx-post="/search/"
hx-trigger="keyup changed delay:250ms"
hx-indicator=".htmx-indicator"
hx-target="#todo-results"
placeholder="Search"
class="bg-white h-10 px-5 pr-10 rounded-full text-2xl focus:outline-none"
>
<span class="htmx-indicator">Searching...</span>
</div>
<table class="border-collapse w-small w-2/3 mx-auto">
<thead>
<tr>
<th class="p-3 font-bold uppercase bg-gray-200 text-gray-600 border border-gray-300 hidden lg:table-cell">#</th>
<th class="p-3 font-bold uppercase bg-gray-200 text-gray-600 border border-gray-300 hidden lg:table-cell">Title</th>
<th class="p-3 font-bold uppercase bg-gray-200 text-gray-600 border border-gray-300 hidden lg:table-cell">Completed</th>
</tr>
</thead>
<tbody id="todo-results">
{% include "todo.html" %}
</tbody>
</table>
{% endblock content %}
Tomemos un momento para ver los atributos definidos desde htmx:
<input
type="text"
name="search"
hx-post="/search/"
hx-trigger="keyup changed delay:250ms"
hx-indicator=".htmx-indicator"
hx-target="#todo-results"
placeholder="Search"
class="bg-white h-10 px-5 pr-10 rounded-full text-2xl focus:outline-none"
>
/search
punto final.#todo-results
elemento.Agrega el archivo templates/todo.html :
<!-- templates/todo.html -->
{% for todo in todos %}
<tr
class="bg-white lg:hover:bg-gray-100 flex lg:table-row flex-row lg:flex-row flex-wrap lg:flex-no-wrap mb-10 lg:mb-0">
<td class="w-full lg:w-auto p-3 text-gray-800 text-center border border-b block lg:table-cell relative lg:static">
{{todo.id}}
</td>
<td class="w-full lg:w-auto p-3 text-gray-800 text-center border border-b block lg:table-cell relative lg:static">
{{todo.title}}
</td>
<td class="w-full lg:w-auto p-3 text-gray-800 text-center border border-b block lg:table-cell relative lg:static">
{% if todo.completed %}
<span class="rounded bg-green-400 py-1 px-3 text-xs font-bold">Yes</span>
{% else %}
<span class="rounded bg-red-400 py-1 px-3 text-xs font-bold">No</span>
{% endif %}
</td>
</tr>
{% endfor %}
Este archivo muestra todos los que coinciden con nuestra consulta de búsqueda.
Genere un nuevo archivo src/output.css :
(venv)$ tailwindcss -i ./static/src/main.css -o ./static/src/output.css --minify
Ejecute la aplicación usando python manage.py runserver
y navegue hasta http://localhost:8000 nuevamente para probarla.
En este tutorial, vimos cómo:
htmx puede representar elementos sin recargar la página. Lo más importante es que puede lograr esto sin escribir JavaScript. Aunque esto reduce la cantidad de trabajo requerido en el lado del cliente, los datos enviados desde el servidor pueden ser más altos ya que está enviando HTML renderizado.
Ofrecer plantillas HTML parciales como esta fue popular a principios de la década de 2000. htmx proporciona un giro moderno a este enfoque. En general, ofrecer plantillas parciales se está volviendo popular nuevamente debido a la complejidad de los marcos como React y Vue. Puede agregar WebSockets a la mezcla para ofrecer cambios en tiempo real también. Este mismo enfoque es utilizado por el famoso Phoenix LiveView . Puede leer más sobre HTML sobre WebSockets en El futuro del software web es HTML sobre WebSockets y HTML sobre WebSockets .
La biblioteca aún es joven, pero el futuro parece muy brillante.
Tailwind es un poderoso marco CSS que se enfoca en la productividad del desarrollador. Aunque este tutorial no lo abordó, Tailwind es altamente personalizable.
Cuando utilice Django, asegúrese de acoplar htmx y Tailwind con Django Compressor para simplificar la gestión de activos estáticos.
El código completo se puede encontrar en el repositorio django-htmx-tailwind .
Fuente: https://testdriven.io
1656989520
Neste tutorial, você aprenderá como configurar o Django com htmx e Tailwind CSS . O objetivo do htmx e do Tailwind é simplificar o desenvolvimento web moderno para que você possa projetar e permitir a interatividade sem nunca deixar o conforto e a facilidade do HTML. Também veremos como usar o Django Compressor para agrupar e reduzir ativos estáticos em um aplicativo Django.
htmx é uma biblioteca que permite acessar recursos modernos do navegador como AJAX, transições CSS, WebSockets e eventos enviados pelo servidor diretamente do HTML, em vez de usar JavaScript. Ele permite que você construa interfaces de usuário rapidamente diretamente na marcação.
htmx estende vários recursos já embutidos no navegador, como fazer solicitações HTTP e responder a eventos. Por exemplo, em vez de apenas poder fazer solicitações GET e POST por meio a
de elementos e form
, você pode usar atributos HTML para enviar solicitações GET, POST, PUT, PATCH ou DELETE em qualquer elemento HTML:
<button hx-delete="/user/1">Delete</button>
Você também pode atualizar partes de uma página para criar um aplicativo de página única (SPA): link CodePen
Abra a guia de rede nas ferramentas de desenvolvimento do navegador. Quando o botão é clicado, uma solicitação XHR é enviada ao https://v2.jokeapi.dev/joke/Any?format=txt&safe-mode
terminal. A resposta é então anexada ao p
elemento com uma id
saída.
Para obter mais exemplos, confira a página de exemplos de interface do usuário dos documentos htmx oficiais.
Prós :
Contras :
Tailwind CSS é um framework CSS "utilitário em primeiro lugar". Em vez de enviar componentes pré-construídos (nos quais frameworks como Bootstrap e Bulma se especializam), ele fornece blocos de construção na forma de classes de utilitários que permitem criar layouts e designs de maneira rápida e fácil.
Por exemplo, pegue o seguinte HTML e CSS:
<style>
.hello {
height: 5px;
width: 10px;
background: gray;
border-width: 1px;
border-radius: 3px;
padding: 5px;
}
</style>
<div class="hello">Hello World</div>
Isso pode ser implementado com o Tailwind assim:
<div class="h-1 w-2 bg-gray-600 border rounded-sm p-1">Hello World</div>
Confira o CSS Tailwind Converter para converter CSS bruto nas classes de utilitário equivalentes no Tailwind. Compare os resultados.
Prós :
<div class="bg-white dark:bg-black">
.Contras :
O Django Compressor é uma extensão projetada para gerenciar (compactar/armazenar em cache) ativos estáticos em um aplicativo Django. Com ele, você cria um pipeline de ativos simples para:
Com isso, vamos ver como trabalhar com cada uma das ferramentas acima no Django!
Para começar, crie uma nova pasta para o nosso projeto, crie e ative um novo ambiente virtual e instale o Django junto com o Django Compressor:
$ mkdir django-htmx-tailwind && cd django-htmx-tailwind
$ python3.10 -m venv venv
$ source venv/bin/activate
(venv)$
(venv)$ pip install Django==4.0.3 django-compressor==3.1
Em seguida, vamos instalar o pytailwindcss e fazer o download do binário:
(venv)$ pip install pytailwindcss==0.1.4
(venv)$ tailwindcss
Crie um novo projeto Django e um todos
aplicativo:
(venv)$ django-admin startproject config .
(venv)$ python manage.py startapp todos
Adicione os aplicativos à INSTALLED_APPS
lista em config/settings.py :
# config/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'todos', # new
'compressor', # new
]
Crie uma pasta "templates" na raiz do seu projeto. Em seguida, atualize as TEMPLATES
configurações assim:
# config/settings.py
TEMPLATES = [
{
...
'DIRS': [BASE_DIR / 'templates'], # new
...
},
]
Vamos adicionar alguma configuração ao config/settings.py para compressor
:
# config/settings.py
COMPRESS_ROOT = BASE_DIR / 'static'
COMPRESS_ENABLED = True
STATICFILES_FINDERS = ('compressor.finders.CompressorFinder',)
Notas:
DEBUG
.django.contrib.staticfiles
instalado.Inicialize o Tailwind CSS em seu projeto:
(venv)$ tailwindcss init
Este comando criou um arquivo tailwind.config.js na raiz do seu projeto. Todas as personalizações relacionadas ao Tailwind vão para este arquivo.
Atualize tailwind.config.js assim:
module.exports = {
content: [
'./templates/**/*.html',
],
theme: {
extend: {},
},
plugins: [],
}
Tome nota da seção de conteúdo . Aqui, você configura os caminhos para os modelos HTML do seu projeto. Tailwind CSS irá escanear seus templates, procurando por nomes de classes Tailwind. O arquivo CSS de saída gerado conterá apenas CSS para os nomes de classe relevantes encontrados em seus arquivos de modelo. Isso ajuda a manter os arquivos CSS gerados pequenos, pois eles conterão apenas os estilos que estão realmente sendo usados.
Em seguida, na raiz do projeto, crie os seguintes arquivos e pastas:
static
└── src
└── main.css
Em seguida, adicione o seguinte a static/src/main.css :
/* static/src/main.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
Aqui, definimos todas as classes base
, components
e utilities
do Tailwind CSS.
É isso. Agora você tem o Django Compressor e o Tailwind conectados. Em seguida, veremos como servir um arquivo index.html para ver o CSS em ação.
Atualize o arquivo todos/views.py assim:
# todos/views.py
from django.shortcuts import render
def index(request):
return render(request, 'index.html')
Adicione a visualização a todos/urls.py :
# todos/urls.py
from django.urls import path
from .views import index
urlpatterns = [
path('', index, name='index'),
]
Em seguida, adicione todos.urls
a config/urls.py :
# config/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('todos.urls')), # new
]
Adicione um arquivo _base.html a "templates":
<!-- templates/_base.html -->
{% load compress %}
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Django + HTMX + Tailwind CSS</title>
{% compress css %}
<link rel="stylesheet" href="{% static 'src/output.css' %}">
{% endcompress %}
</head>
<body class="bg-blue-100">
{% block content %}
{% endblock content %}
</body>
</html>
Notas:
{% load compress %}
importa todas as tags necessárias para trabalhar com o Django Compressor.{% load static %}
carrega arquivos estáticos no modelo.{% compress css %}
aplica os filtros apropriados ao arquivo static/src/main.css .Além disso, adicionamos algumas cores ao corpo HTML via <body class="bg-blue-100">
. bg-blue-100
é usado para alterar a cor de fundo para azul claro.
Adicione um arquivo index.html :
<!-- templates/index.html -->
{% extends "_base.html" %}
{% block content %}
<h1>Hello World</h1>
{% endblock content %}
Agora, execute o seguinte comando na raiz do projeto para verificar os modelos de classes e gerar um arquivo CSS:
(venv)$ tailwindcss -i ./static/src/main.css -o ./static/src/output.css --minify
Aplique as migrações e execute o servidor de desenvolvimento:
(venv)$ python manage.py migrate
(venv)$ python manage.py runserver
Navegue até http://localhost:8000 em seu navegador para ver os resultados. Anote também o arquivo gerado na pasta "static/CACHE/css".
Com o Tailwind configurado, vamos adicionar htmx à mistura e construir uma pesquisa ao vivo que exibe os resultados conforme você digita.
Em vez de buscar a biblioteca htmx de um CDN, vamos baixá-la e usar o Django Compressor para agrupá-la.
Baixe a biblioteca em https://unpkg.com/htmx.org@1.7.0/dist/htmx.js e salve-a em static/src/htmx.js .
Para que tenhamos alguns dados para trabalhar, salve https://github.com/testdrivedio/django-htmx-tailwind/blob/master/todos/todo.py em um novo arquivo chamado todos/todo.py .
Agora, adicione a visualização para implementar a funcionalidade de pesquisa em todos/views.py :
# todos/views.py
from django.shortcuts import render
from django.views.decorators.http import require_http_methods # new
from .todo import todos # new
def index(request):
return render(request, 'index.html', {'todos': []}) # modified
# new
@require_http_methods(['POST'])
def search(request):
res_todos = []
search = request.POST['search']
if len(search) == 0:
return render(request, 'todo.html', {'todos': []})
for i in todos:
if search in i['title']:
res_todos.append(i)
return render(request, 'todo.html', {'todos': res_todos})
Adicionamos uma nova visualização, search
, que procura por todos e renderiza o template todo.html com todos os resultados.
Adicione a visualização recém-criada a todos/urls.py :
# todos/urls.py
from django.urls import path
from .views import index, search # modified
urlpatterns = [
path('', index, name='index'),
path('search/', search, name='search'), # new
]
Em seguida, adicione o novo recurso ao arquivo _base.html :
<!-- templates/base.html -->
{% load compress %}
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Django + HTMX + Tailwind CSS</title>
{% compress css %}
<link rel="stylesheet" href="{% static 'src/output.css' %}">
{% endcompress %}
</head>
<body class="bg-blue-100">
{% block content %}
{% endblock content %}
<!-- new -->
{% compress js %}
<script type="text/javascript" src="{% static 'src/htmx.js' %}"></script>
{% endcompress %}
<!-- new -->
<script>
document.body.addEventListener('htmx:configRequest', (event) => {
event.detail.headers['X-CSRFToken'] = '{{ csrf_token }}';
})
</script>
</body>
</html>
Carregamos a biblioteca htmx usando a {% compress js %}
tag. A js
tag, por padrão , se aplica JSMinFilter
(que, por sua vez, aplica rjsmin ). Portanto, isso reduzirá o static/src/htmx.js e o servirá da pasta "static/CACHE".
Também adicionamos o seguinte script:
document.body.addEventListener('htmx:configRequest', (event) => {
event.detail.headers['X-CSRFToken'] = '{{ csrf_token }}';
})
Este ouvinte de eventos adiciona o token CSRF ao cabeçalho da solicitação.
Em seguida, vamos adicionar a capacidade de pesquisar com base no título de cada tarefa.
Atualize o arquivo index.html assim:
<!-- templates/index.html -->
{% extends "_base.html" %}
{% block content %}
<div class="w-small w-2/3 mx-auto py-10 text-gray-600">
<input
type="text"
name="search"
hx-post="/search/"
hx-trigger="keyup changed delay:250ms"
hx-indicator=".htmx-indicator"
hx-target="#todo-results"
placeholder="Search"
class="bg-white h-10 px-5 pr-10 rounded-full text-2xl focus:outline-none"
>
<span class="htmx-indicator">Searching...</span>
</div>
<table class="border-collapse w-small w-2/3 mx-auto">
<thead>
<tr>
<th class="p-3 font-bold uppercase bg-gray-200 text-gray-600 border border-gray-300 hidden lg:table-cell">#</th>
<th class="p-3 font-bold uppercase bg-gray-200 text-gray-600 border border-gray-300 hidden lg:table-cell">Title</th>
<th class="p-3 font-bold uppercase bg-gray-200 text-gray-600 border border-gray-300 hidden lg:table-cell">Completed</th>
</tr>
</thead>
<tbody id="todo-results">
{% include "todo.html" %}
</tbody>
</table>
{% endblock content %}
Vamos dar uma olhada nos atributos definidos de htmx:
<input
type="text"
name="search"
hx-post="/search/"
hx-trigger="keyup changed delay:250ms"
hx-indicator=".htmx-indicator"
hx-target="#todo-results"
placeholder="Search"
class="bg-white h-10 px-5 pr-10 rounded-full text-2xl focus:outline-none"
>
/search
terminal.#todo-results
elemento.Adicione o arquivo templates/todo.html :
<!-- templates/todo.html -->
{% for todo in todos %}
<tr
class="bg-white lg:hover:bg-gray-100 flex lg:table-row flex-row lg:flex-row flex-wrap lg:flex-no-wrap mb-10 lg:mb-0">
<td class="w-full lg:w-auto p-3 text-gray-800 text-center border border-b block lg:table-cell relative lg:static">
{{todo.id}}
</td>
<td class="w-full lg:w-auto p-3 text-gray-800 text-center border border-b block lg:table-cell relative lg:static">
{{todo.title}}
</td>
<td class="w-full lg:w-auto p-3 text-gray-800 text-center border border-b block lg:table-cell relative lg:static">
{% if todo.completed %}
<span class="rounded bg-green-400 py-1 px-3 text-xs font-bold">Yes</span>
{% else %}
<span class="rounded bg-red-400 py-1 px-3 text-xs font-bold">No</span>
{% endif %}
</td>
</tr>
{% endfor %}
Este arquivo renderiza os todos que correspondem à nossa consulta de pesquisa.
Gere um novo arquivo src/output.css :
(venv)$ tailwindcss -i ./static/src/main.css -o ./static/src/output.css --minify
Execute o aplicativo usando python manage.py runserver
e navegue até http://localhost:8000 novamente para testá-lo.
Neste tutorial, vimos como:
htmx pode renderizar elementos sem recarregar a página. Mais importante, você pode conseguir isso sem escrever nenhum JavaScript. Embora isso reduza a quantidade de trabalho necessária no lado do cliente, os dados enviados do servidor podem ser maiores, pois ele está enviando HTML renderizado.
Servir modelos HTML parciais como esse era popular no início dos anos 2000. htmx fornece um toque moderno a essa abordagem. Em geral, servir templates parciais está se tornando popular novamente devido à complexidade de frameworks como React e Vue. Você pode adicionar WebSockets à mistura para fornecer alterações em tempo real também. Essa mesma abordagem é usada pelo famoso Phoenix LiveView . Você pode ler mais sobre HTML sobre WebSockets em O futuro do software da Web é HTML sobre WebSockets e HTML sobre WebSockets .
A biblioteca ainda é jovem, mas o futuro parece muito brilhante.
Tailwind é um poderoso framework CSS que se concentra na produtividade do desenvolvedor. Embora este tutorial não tenha tocado nisso, o Tailwind é altamente personalizável.
Ao usar o Django, certifique-se de acoplar o htmx e o Tailwind com o Django Compressor para simplificar o gerenciamento de ativos estáticos.
O código completo pode ser encontrado no repositório django-htmx-tailwind .
Fonte: https://testdrive.io
1656892380
Trong hướng dẫn này, bạn sẽ học cách thiết lập Flask với htmx và Tailwind CSS . Mục tiêu của cả htmx và Tailwind là đơn giản hóa việc phát triển web hiện đại để bạn có thể thiết kế và kích hoạt tính tương tác mà không bao giờ rời bỏ sự thoải mái và dễ dàng của HTML. Chúng ta cũng sẽ xem xét cách sử dụng Flask-Assets để nhóm và giảm thiểu nội dung tĩnh trong ứng dụng Flask.
htmx là một thư viện cho phép bạn truy cập các tính năng hiện đại của trình duyệt như AJAX, CSS Transitions, WebSockets và Sự kiện do máy chủ gửi trực tiếp từ HTML, thay vì sử dụng JavaScript. Nó cho phép bạn xây dựng giao diện người dùng một cách nhanh chóng trực tiếp trong đánh dấu.
htmx mở rộng một số tính năng đã được tích hợp sẵn trong trình duyệt, như thực hiện các yêu cầu HTTP và phản hồi các sự kiện. Ví dụ: thay vì chỉ có thể thực hiện các yêu cầu GET và POST thông qua a
và form
các phần tử, bạn có thể sử dụng các thuộc tính HTML để gửi các yêu cầu GET, POST, PUT, PATCH hoặc DELETE trên bất kỳ phần tử HTML nào:
<button hx-delete="/user/1">Delete</button>
Bạn cũng có thể cập nhật các phần của trang để tạo Ứng dụng một trang (SPA): liên kết CodePen
Mở tab mạng trong các công cụ dành cho nhà phát triển của trình duyệt. Khi nút được nhấp, một yêu cầu XHR được gửi đến https://v2.jokeapi.dev/joke/Any?format=txt&safe-mode
điểm cuối. Phản hồi sau đó được thêm vào p
phần tử có id
đầu ra.
Để biết thêm ví dụ, hãy xem trang Ví dụ về giao diện người dùng từ tài liệu htmx chính thức.
Ưu điểm :
Nhược điểm :
Tailwind CSS là một khung CSS "ưu tiên tiện ích". Thay vì vận chuyển các thành phần được xây dựng sẵn (mà các khung công tác như Bootstrap và Bulma chuyên về), nó cung cấp các khối xây dựng dưới dạng các lớp tiện ích cho phép người ta tạo bố cục và thiết kế một cách nhanh chóng và dễ dàng.
Ví dụ: lấy HTML và CSS sau:
<style>
.hello {
height: 5px;
width: 10px;
background: gray;
border-width: 1px;
border-radius: 3px;
padding: 5px;
}
</style>
<div class="hello">Hello World</div>
Điều này có thể được triển khai với Tailwind như sau:
<div class="h-1 w-2 bg-gray-600 border rounded-sm p-1">Hello World</div>
Hãy xem Trình chuyển đổi CSS Tailwind để chuyển đổi CSS thô sang các lớp tiện ích tương đương trong Tailwind. So sánh các kết quả.
Ưu điểm :
<div class="bg-white dark:bg-black">
:.Nhược điểm :
Flask-Assets là một phần mở rộng được thiết kế để quản lý các tài sản tĩnh trong ứng dụng Flask. Với nó, bạn tạo một đường dẫn nội dung đơn giản cho:
Cùng với đó, hãy cùng xem cách làm việc với từng công cụ trên trong Flask!
Để bắt đầu, hãy tạo một thư mục mới cho dự án của chúng tôi, tạo và kích hoạt một môi trường ảo mới và cài đặt Flask cùng với Flask-Assets:
$ mkdir flask-htmx-tailwind && cd flask-htmx-tailwind
$ python3.10 -m venv venv
$ source venv/bin/activate
(venv)$
(venv)$ pip install Flask==2.1.1 Flask-Assets==2.0
Tiếp theo, hãy cài đặt pytailwindcss và tải xuống tệp nhị phân:
(venv)$ pip install pytailwindcss==0.1.4
(venv)$ tailwindcss
Tiếp theo, thêm tệp app.py :
# app.py
from flask import Flask
from flask_assets import Bundle, Environment
app = Flask(__name__)
assets = Environment(app)
css = Bundle("src/main.css", output="dist/main.css")
assets.register("css", css)
css.build()
Sau khi nhập Gói và Môi trường , chúng tôi đã tạo một Environment
nội dung CSS mới và đăng ký với nó thông qua a Bundle
.
Gói mà chúng tôi đã tạo lấy src / main.css làm đầu vào, sau đó sẽ được xử lý và xuất ra dist / main.css khi chúng tôi chạy Tailwind CSS CLI.
Vì tất cả các tệp tĩnh của Flask nằm trong thư mục "tĩnh" theo mặc định, các thư mục "src" và "dist" nói trên nằm trong thư mục "tĩnh".
Cùng với đó, hãy thiết lập Tailwind.
Bắt đầu bằng cách tạo tệp cấu hình Tailwind:
(venv)$ tailwindcss init
Lệnh này đã tạo một tệp tailwind.config.js trong thư mục gốc của dự án của bạn. Tất cả các tùy chỉnh liên quan đến Tailwind đều có trong tệp này.
Cập nhật tailwind.config.js như sau:
module.exports = {
content: [
'./templates/**/*.html',
],
theme: {
extend: {},
},
plugins: [],
}
Ghi chú phần nội dung . Tại đây, bạn định cấu hình đường dẫn đến các mẫu HTML của dự án. Tailwind CSS sẽ quét các mẫu của bạn, tìm kiếm tên lớp Tailwind. Tệp CSS đầu ra được tạo sẽ chỉ chứa CSS cho các tên lớp có liên quan được tìm thấy trong tệp mẫu của bạn. Điều này giúp giữ cho các tệp CSS được tạo nhỏ hơn vì chúng sẽ chỉ chứa các kiểu thực sự đang được sử dụng.
Thêm phần sau vào static / src / main.css :
/* static/src/main.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
Ở đây, chúng tôi đã định nghĩa tất cả base
và components
các utilities
lớp từ Tailwind CSS.
Giờ đây, bạn đã có Flask-Assets và Tailwind. Tiếp theo, chúng ta sẽ xem xét cách cung cấp tệp index.html để xem CSS đang hoạt động.
Thêm một tuyến đường cùng với một khối chính để chạy máy chủ phát triển Flask vào app.py như sau:
# app.py
from flask import Flask, render_template
from flask_assets import Bundle, Environment
app = Flask(__name__)
assets = Environment(app)
css = Bundle("src/main.css", output="dist/main.css")
assets.register("css", css)
css.build()
@app.route("/")
def homepage():
return render_template("index.html")
if __name__ == "__main__":
app.run(debug=True)
Tạo một thư mục "mẫu". Sau đó, thêm tệp base.html vào đó:
<!-- templates/base.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{% assets 'css' %}
<link rel="stylesheet" href="{{ ASSET_URL }}">
{% endassets %}
<title>Flask + htmlx + Tailwind CSS</title>
</head>
<body class="bg-blue-100">
{% block content %}
{% endblock content %}
</body>
</html>
Lưu ý về {% assets 'css' %}
khối. Vì chúng tôi đã đăng ký gói CSS với môi trường ứng dụng, chúng tôi có thể truy cập nó bằng tên đã đăng ký css
và {{ ASSET_URL }}
sẽ tự động sử dụng đường dẫn.
Ngoài ra, chúng tôi đã thêm một số màu vào nội dung HTML thông qua bg-blue-100
, màu này sẽ thay đổi màu nền thành màu xanh lam nhạt.
Thêm tệp index.html :
<!-- templates/index.html -->
{% extends "base.html" %}
{% block content %}
<h1>Hello World</h1>
{% endblock content %}
Bây giờ, hãy chạy lệnh sau trong thư mục gốc của dự án để quét các mẫu cho các lớp và tạo tệp CSS:
(venv)$ tailwindcss -i ./static/src/main.css -o ./static/dist/main.css --minify
Bạn sẽ thấy một thư mục mới có tên "dist" bên trong thư mục "static".
Lưu ý đến tệp static / dist / main.css đã tạo .
Khởi động máy chủ phát triển thông qua python app.py
và điều hướng đến http: // localhost: 5000 trong trình duyệt của bạn để xem kết quả.
Với Tailwind được định cấu hình, hãy thêm htmx vào hỗn hợp và tạo một tìm kiếm trực tiếp hiển thị kết quả khi bạn nhập.
Thay vì tìm nạp thư viện htmx từ CDN, hãy tải xuống và sử dụng Flask-Assets để nhóm nó.
Tải xuống thư viện từ https://unpkg.com/htmx.org@1.7.0/dist/htmx.js và lưu nó vào "static / src".
Bây giờ, để tạo một gói mới cho các tệp JavaScript của chúng tôi, hãy cập nhật app.py như sau:
# app.py
from flask import Flask, render_template
from flask_assets import Bundle, Environment
app = Flask(__name__)
assets = Environment(app)
css = Bundle("src/main.css", output="dist/main.css")
js = Bundle("src/*.js", output="dist/main.js") # new
assets.register("css", css)
assets.register("js", js) # new
css.build()
js.build() # new
@app.route("/")
def homepage():
return render_template("index.html")
if __name__ == "__main__":
app.run(debug=True)
Ở đây, chúng tôi đã tạo một gói mới có tên js
, xuất ra static / dist / main.js. Vì chúng tôi không sử dụng bất kỳ bộ lọc nào ở đây nên tệp nguồn và tệp đích sẽ giống nhau.
Tiếp theo, thêm nội dung mới vào tệp base.html của chúng tôi :
<!-- templates/base.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{% assets 'css' %}
<link rel="stylesheet" href="{{ ASSET_URL }}">
{% endassets %}
<!-- new -->
{% assets 'js' %}
<script type="text/javascript" src="{{ ASSET_URL }}"></script>
{% endassets %}
<title>Flask + htmlx + Tailwind CSS</title>
</head>
<body class="bg-blue-100">
{% block content %}
{% endblock content %}
</body>
</html>
Để chúng tôi có một số dữ liệu để làm việc, hãy lưu https://github.com/testdrivenio/flask-htmx-tailwind/blob/master/todo.py vào một tệp mới có tên todo.py.
Chúng tôi sẽ thêm khả năng tìm kiếm dựa trên tiêu đề của từng việc làm.
Cập nhật tệp index.html như sau:
<!-- templates/index.html -->
{% extends 'base.html' %}
{% block content %}
<div class="w-small w-2/3 mx-auto py-10 text-gray-600">
<input
type="text"
name="search"
hx-post="/search"
hx-trigger="keyup changed delay:250ms"
hx-indicator=".htmx-indicator"
hx-target="#todo-results"
placeholder="Search"
class="bg-white h-10 px-5 pr-10 rounded-full text-2xl focus:outline-none"
>
<span class="htmx-indicator">Searching...</span>
</div>
<table class="border-collapse w-small w-2/3 mx-auto">
<thead>
<tr>
<th class="p-3 font-bold uppercase bg-gray-200 text-gray-600 border border-gray-300 hidden lg:table-cell">#</th>
<th class="p-3 font-bold uppercase bg-gray-200 text-gray-600 border border-gray-300 hidden lg:table-cell">Title</th>
<th class="p-3 font-bold uppercase bg-gray-200 text-gray-600 border border-gray-300 hidden lg:table-cell">Completed</th>
</tr>
</thead>
<tbody id="todo-results">
{% include 'todo.html' %}
</tbody>
</table>
{% endblock content %}
Hãy dành một chút thời gian để xem xét các thuộc tính được xác định từ htmx:
<input
type="text"
name="search"
hx-post="/search"
hx-trigger="keyup changed delay:250ms"
hx-indicator=".htmx-indicator"
hx-target="#todo-results"
placeholder="Search"
class="bg-white h-10 px-5 pr-10 rounded-full text-2xl focus:outline-none"
>
/search
điểm cuối.#todo-results
phần tử.Thêm tệp mẫu / todo.html :
<!-- templates/todo.html -->
{% if todos|length>0 %}
{% for todo in todos %}
<tr class="bg-white lg:hover:bg-gray-100 flex lg:table-row flex-row lg:flex-row flex-wrap lg:flex-no-wrap mb-10 lg:mb-0">
<td class="w-full lg:w-auto p-3 text-gray-800 text-center border border-b block lg:table-cell relative lg:static">{{todo.id}}</td>
<td class="w-full lg:w-auto p-3 text-gray-800 text-center border border-b block lg:table-cell relative lg:static">{{todo.title}}</td>
<td class="w-full lg:w-auto p-3 text-gray-800 text-center border border-b block lg:table-cell relative lg:static">
{% if todo.completed %}
<span class="rounded bg-green-400 py-1 px-3 text-xs font-bold">Yes</span>
{% else %}
<span class="rounded bg-red-400 py-1 px-3 text-xs font-bold">No</span>
{% endif %}
</td>
</tr>
{% endfor %}
{% endif %}
Tệp này hiển thị các việc cần làm phù hợp với truy vấn tìm kiếm của chúng tôi.
Cuối cùng, thêm trình xử lý tuyến đường vào app.py :
@app.route("/search", methods=["POST"])
def search_todo():
search_term = request.form.get("search")
if not len(search_term):
return render_template("todo.html", todos=[])
res_todos = []
for todo in todos:
if search_term in todo["title"]:
res_todos.append(todo)
return render_template("todo.html", todos=res_todos)
Điểm /search
cuối tìm kiếm các việc cần làm và hiển thị mẫu todo.html với tất cả các kết quả.
Cập nhật các lần nhập ở đầu:
from flask import Flask, render_template, request
from flask_assets import Bundle, Environment
from todo import todos
Tiếp theo, cập nhật tệp CSS đầu ra:
(venv)$ tailwindcss -i ./static/src/main.css -o ./static/dist/main.css --minify
Chạy ứng dụng bằng cách sử dụng python app.py
và điều hướng đến http: // localhost: 5000 một lần nữa để kiểm tra:
Trong hướng dẫn này, chúng tôi đã xem xét cách:
htmx có thể hiển thị các phần tử mà không cần tải lại trang. Quan trọng nhất, bạn có thể đạt được điều này mà không cần viết bất kỳ JavaScript nào. Mặc dù điều này làm giảm số lượng công việc cần thiết ở phía máy khách, nhưng dữ liệu được gửi từ máy chủ có thể cao hơn vì nó đang gửi HTML được kết xuất.
Việc cung cấp các mẫu HTML từng phần như thế này đã phổ biến vào đầu những năm 2000. htmx cung cấp một bước ngoặt hiện đại cho cách tiếp cận này. Nói chung, việc cung cấp các mẫu từng phần đang trở nên phổ biến trở lại do các khung công tác phức tạp như React và Vue. Bạn cũng có thể thêm WebSockets vào hỗn hợp để cung cấp các thay đổi theo thời gian thực. Phương pháp tương tự này được sử dụng bởi Phoenix LiveView nổi tiếng . Bạn có thể đọc thêm về HTML qua WebSockets trong Tương lai của Phần mềm Web là HTML-over-WebSockets và HTML Over WebSockets .
Thư viện vẫn còn non trẻ, nhưng tương lai có vẻ rất tươi sáng.
Nguồn: https://testdriven.io
1656885060
В этом руководстве вы узнаете, как настроить Flask с помощью htmx и Tailwind CSS . Цель как htmx, так и Tailwind — упростить современную веб-разработку, чтобы вы могли проектировать и обеспечивать интерактивность, не отказываясь от удобства и простоты HTML. Мы также рассмотрим, как использовать Flask-Assets для объединения и минимизации статических ресурсов в приложении Flask.
htmx — это библиотека, которая позволяет вам получать доступ к современным функциям браузера, таким как AJAX, переходы CSS, WebSockets и события, отправленные сервером, непосредственно из HTML, а не с помощью JavaScript. Он позволяет быстро создавать пользовательские интерфейсы непосредственно в разметке.
htmx расширяет несколько функций, уже встроенных в браузер, таких как выполнение HTTP-запросов и реагирование на события. Например, вместо того, чтобы делать запросы GET и POST только через элементы a
и form
, вы можете использовать атрибуты HTML для отправки запросов GET, POST, PUT, PATCH или DELETE для любого элемента HTML:
<button hx-delete="/user/1">Delete</button>
Вы также можете обновить части страницы, чтобы создать одностраничное приложение (SPA): ссылка CodePen
Откройте вкладку сети в инструментах разработчика браузера. При нажатии кнопки на конечную точку отправляется запрос XHR https://v2.jokeapi.dev/joke/Any?format=txt&safe-mode
. Затем ответ добавляется к p
элементу с id
выводом.
Дополнительные примеры см . на странице примеров пользовательского интерфейса в официальной документации htmx.
Плюсы :
Минусы :
Tailwind CSS — это CSS-фреймворк, ориентированный прежде всего на полезность. Вместо того, чтобы поставлять готовые компоненты (на которых специализируются такие фреймворки, как Bootstrap и Bulma ), он предоставляет строительные блоки в виде служебных классов, которые позволяют быстро и легко создавать макеты и проекты.
Например, возьмите следующий HTML и CSS:
<style>
.hello {
height: 5px;
width: 10px;
background: gray;
border-width: 1px;
border-radius: 3px;
padding: 5px;
}
</style>
<div class="hello">Hello World</div>
Это можно реализовать с помощью Tailwind следующим образом:
<div class="h-1 w-2 bg-gray-600 border rounded-sm p-1">Hello World</div>
Воспользуйтесь конвертером CSS Tailwind , чтобы преобразовать необработанный CSS в эквивалентные служебные классы в Tailwind. Сравните результаты.
Плюсы :
<div class="bg-white dark:bg-black">
.Минусы :
Flask-Assets — это расширение, предназначенное для управления статическими активами в приложении Flask. С его помощью вы создаете простой конвейер активов для:
Итак, давайте посмотрим, как работать с каждым из вышеперечисленных инструментов во Flask!
Для начала создайте новый каталог для нашего проекта, создайте и активируйте новую виртуальную среду и установите Flask вместе с Flask-Assets:
$ mkdir flask-htmx-tailwind && cd flask-htmx-tailwind
$ python3.10 -m venv venv
$ source venv/bin/activate
(venv)$
(venv)$ pip install Flask==2.1.1 Flask-Assets==2.0
Далее давайте установим pytailwindcss и загрузим его бинарник:
(venv)$ pip install pytailwindcss==0.1.4
(venv)$ tailwindcss
Затем добавьте файл app.py :
# app.py
from flask import Flask
from flask_assets import Bundle, Environment
app = Flask(__name__)
assets = Environment(app)
css = Bundle("src/main.css", output="dist/main.css")
assets.register("css", css)
css.build()
После импорта Bundle и Environment мы создали новый Environment
и зарегистрировали в нем наши активы CSS через файл Bundle
.
Созданный нами пакет принимает src/main.css в качестве входных данных, которые затем будут обработаны и выведены в dist/main.css при запуске интерфейса командной строки Tailwind CSS.
Поскольку все статические файлы Flask по умолчанию находятся в «статической» папке, вышеупомянутые папки «src» и «dist» находятся в «статической» папке.
Итак, давайте настроим Tailwind.
Начните с создания файла конфигурации Tailwind:
(venv)$ tailwindcss init
Эта команда создала файл tailwind.config.js в корне вашего проекта. Все настройки, связанные с Tailwind, находятся в этом файле.
Обновите tailwind.config.js следующим образом:
module.exports = {
content: [
'./templates/**/*.html',
],
theme: {
extend: {},
},
plugins: [],
}
Обратите внимание на раздел контента . Здесь вы настраиваете пути к HTML-шаблонам вашего проекта. Tailwind CSS просканирует ваши шаблоны в поисках имен классов Tailwind. Сгенерированный выходной файл CSS будет содержать только CSS для соответствующих имен классов, найденных в ваших файлах шаблонов. Это помогает уменьшить размер сгенерированных файлов CSS, поскольку они будут содержать только те стили, которые фактически используются.
Добавьте следующее в static/src/main.css :
/* static/src/main.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
Здесь мы определили все классы base
, components
и utilities
из Tailwind CSS.
Теперь у вас есть подключенные Flask-Assets и Tailwind. Далее мы рассмотрим, как обслуживать файл index.html , чтобы увидеть CSS в действии.
Добавьте маршрут вместе с основным блоком для запуска сервера разработки Flask в app.py следующим образом:
# app.py
from flask import Flask, render_template
from flask_assets import Bundle, Environment
app = Flask(__name__)
assets = Environment(app)
css = Bundle("src/main.css", output="dist/main.css")
assets.register("css", css)
css.build()
@app.route("/")
def homepage():
return render_template("index.html")
if __name__ == "__main__":
app.run(debug=True)
Создайте папку «шаблоны». Затем добавьте к нему файл base.html :
<!-- templates/base.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{% assets 'css' %}
<link rel="stylesheet" href="{{ ASSET_URL }}">
{% endassets %}
<title>Flask + htmlx + Tailwind CSS</title>
</head>
<body class="bg-blue-100">
{% block content %}
{% endblock content %}
</body>
</html>
Обратите внимание на {% assets 'css' %}
блок. Поскольку мы зарегистрировали пакет CSS в среде приложения, мы можем получить к нему доступ, используя зарегистрированное имя, css
и {{ ASSET_URL }}
автоматически будет использоваться путь.
Кроме того, мы добавили некоторый цвет в тело HTML с помощью bg-blue-100
, который меняет цвет фона на светло-голубой.
Добавьте файл index.html :
<!-- templates/index.html -->
{% extends "base.html" %}
{% block content %}
<h1>Hello World</h1>
{% endblock content %}
Теперь выполните следующую команду в корне проекта, чтобы отсканировать шаблоны на наличие классов и сгенерировать файл CSS:
(venv)$ tailwindcss -i ./static/src/main.css -o ./static/dist/main.css --minify
Вы должны увидеть новый каталог с именем «dist» внутри «статической» папки.
Обратите внимание на сгенерированный файл static/dist/main.css .
Запустите сервер разработки через python app.py
и перейдите по адресу http://localhost:5000 в браузере, чтобы увидеть результаты.
С настроенным Tailwind давайте добавим htmx и создадим живой поиск, который отображает результаты по мере ввода.
Вместо того, чтобы извлекать библиотеку htmx из CDN, давайте загрузим ее и используем Flask-Assets для ее объединения.
Загрузите библиотеку с https://unpkg.com/htmx.org@1.7.0/dist/htmx.js и сохраните ее в «static/src».
Теперь, чтобы создать новый пакет для наших файлов JavaScript, обновите app.py следующим образом:
# app.py
from flask import Flask, render_template
from flask_assets import Bundle, Environment
app = Flask(__name__)
assets = Environment(app)
css = Bundle("src/main.css", output="dist/main.css")
js = Bundle("src/*.js", output="dist/main.js") # new
assets.register("css", css)
assets.register("js", js) # new
css.build()
js.build() # new
@app.route("/")
def homepage():
return render_template("index.html")
if __name__ == "__main__":
app.run(debug=True)
Здесь мы создали новый пакет с именем js
, который выводит в static/dist/main.js . Поскольку здесь мы не используем никаких фильтров, исходный и целевой файлы будут одинаковыми.
Затем добавьте новый актив в наш файл base.html :
<!-- templates/base.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{% assets 'css' %}
<link rel="stylesheet" href="{{ ASSET_URL }}">
{% endassets %}
<!-- new -->
{% assets 'js' %}
<script type="text/javascript" src="{{ ASSET_URL }}"></script>
{% endassets %}
<title>Flask + htmlx + Tailwind CSS</title>
</head>
<body class="bg-blue-100">
{% block content %}
{% endblock content %}
</body>
</html>
Чтобы у нас были данные для работы, сохраните https://github.com/testdrivenio/flask-htmx-tailwind/blob/master/todo.py в новый файл с именем todo.py.
Мы добавим возможность поиска по названию каждой задачи.
Обновите файл index.html следующим образом:
<!-- templates/index.html -->
{% extends 'base.html' %}
{% block content %}
<div class="w-small w-2/3 mx-auto py-10 text-gray-600">
<input
type="text"
name="search"
hx-post="/search"
hx-trigger="keyup changed delay:250ms"
hx-indicator=".htmx-indicator"
hx-target="#todo-results"
placeholder="Search"
class="bg-white h-10 px-5 pr-10 rounded-full text-2xl focus:outline-none"
>
<span class="htmx-indicator">Searching...</span>
</div>
<table class="border-collapse w-small w-2/3 mx-auto">
<thead>
<tr>
<th class="p-3 font-bold uppercase bg-gray-200 text-gray-600 border border-gray-300 hidden lg:table-cell">#</th>
<th class="p-3 font-bold uppercase bg-gray-200 text-gray-600 border border-gray-300 hidden lg:table-cell">Title</th>
<th class="p-3 font-bold uppercase bg-gray-200 text-gray-600 border border-gray-300 hidden lg:table-cell">Completed</th>
</tr>
</thead>
<tbody id="todo-results">
{% include 'todo.html' %}
</tbody>
</table>
{% endblock content %}
Давайте рассмотрим атрибуты, определенные в htmx:
<input
type="text"
name="search"
hx-post="/search"
hx-trigger="keyup changed delay:250ms"
hx-indicator=".htmx-indicator"
hx-target="#todo-results"
placeholder="Search"
class="bg-white h-10 px-5 pr-10 rounded-full text-2xl focus:outline-none"
>
/search
конечную точку.#todo-results
элементе.Добавьте файл templates/todo.html :
<!-- templates/todo.html -->
{% if todos|length>0 %}
{% for todo in todos %}
<tr class="bg-white lg:hover:bg-gray-100 flex lg:table-row flex-row lg:flex-row flex-wrap lg:flex-no-wrap mb-10 lg:mb-0">
<td class="w-full lg:w-auto p-3 text-gray-800 text-center border border-b block lg:table-cell relative lg:static">{{todo.id}}</td>
<td class="w-full lg:w-auto p-3 text-gray-800 text-center border border-b block lg:table-cell relative lg:static">{{todo.title}}</td>
<td class="w-full lg:w-auto p-3 text-gray-800 text-center border border-b block lg:table-cell relative lg:static">
{% if todo.completed %}
<span class="rounded bg-green-400 py-1 px-3 text-xs font-bold">Yes</span>
{% else %}
<span class="rounded bg-red-400 py-1 px-3 text-xs font-bold">No</span>
{% endif %}
</td>
</tr>
{% endfor %}
{% endif %}
Этот файл отображает задачи, соответствующие нашему поисковому запросу.
Наконец, добавьте обработчик маршрута в app.py :
@app.route("/search", methods=["POST"])
def search_todo():
search_term = request.form.get("search")
if not len(search_term):
return render_template("todo.html", todos=[])
res_todos = []
for todo in todos:
if search_term in todo["title"]:
res_todos.append(todo)
return render_template("todo.html", todos=res_todos)
Конечная /search
точка выполняет поиск задач и отображает шаблон todo.html со всеми результатами.
Обновите импорт вверху:
from flask import Flask, render_template, request
from flask_assets import Bundle, Environment
from todo import todos
Затем обновите выходной файл CSS:
(venv)$ tailwindcss -i ./static/src/main.css -o ./static/dist/main.css --minify
Запустите приложение с помощью python app.py
и снова перейдите по адресу http://localhost:5000 , чтобы проверить его:
В этом уроке мы рассмотрели, как:
htmx может отображать элементы без перезагрузки страницы. Самое главное, вы можете добиться этого без написания JavaScript. Хотя это уменьшает объем работы, необходимой на стороне клиента, данные, отправляемые с сервера, могут быть выше, поскольку он отправляет обработанный HTML.
Подобные частичные HTML-шаблоны были популярны в начале 2000-х годов. htmx предлагает современный подход к этому подходу. В целом, обслуживание частичных шаблонов снова становится популярным из-за сложности таких фреймворков, как React и Vue. Вы также можете добавить WebSockets для внесения изменений в реальном времени. Этот же подход используется в знаменитом Phoenix LiveView . Вы можете узнать больше о HTML поверх WebSockets в статье Будущее веб-программного обеспечения — это HTML поверх WebSockets и HTML поверх WebSockets .
Библиотека еще молода, но будущее у нее очень светлое.
Источник: https://testdriven.io
1656880980
Dans ce didacticiel, vous apprendrez à configurer Flask avec htmx et Tailwind CSS . L'objectif de htmx et de Tailwind est de simplifier le développement Web moderne afin que vous puissiez concevoir et activer l'interactivité sans jamais quitter le confort et la facilité du HTML. Nous verrons également comment utiliser Flask-Assets pour regrouper et minimiser les actifs statiques dans une application Flask.
htmx est une bibliothèque qui vous permet d'accéder à des fonctionnalités de navigateur modernes telles que AJAX, CSS Transitions, WebSockets et Server-Sent Events directement à partir de HTML, plutôt que d'utiliser JavaScript. Il vous permet de créer rapidement des interfaces utilisateur directement dans le balisage.
htmx étend plusieurs fonctionnalités déjà intégrées au navigateur, telles que les requêtes HTTP et la réponse aux événements. Par exemple, plutôt que de pouvoir uniquement effectuer des requêtes GET et POST via des éléments a
et form
, vous pouvez utiliser des attributs HTML pour envoyer des requêtes GET, POST, PUT, PATCH ou DELETE sur n'importe quel élément HTML :
<button hx-delete="/user/1">Delete</button>
Vous pouvez également mettre à jour des parties d'une page pour créer une application monopage (SPA) : lien CodePen
Ouvrez l'onglet réseau dans les outils de développement du navigateur. Lorsque le bouton est cliqué, une demande XHR est envoyée au point de https://v2.jokeapi.dev/joke/Any?format=txt&safe-mode
terminaison. La réponse est ensuite ajoutée à l' p
élément avec une id
sortie of.
Pour plus d'exemples, consultez la page Exemples d'interface utilisateur de la documentation officielle htmx.
Avantages :
Inconvénients :
Tailwind CSS est un framework CSS "utility-first". Plutôt que d'expédier des composants prédéfinis (dans lesquels se spécialisent des frameworks comme Bootstrap et Bulma ), il fournit des blocs de construction sous la forme de classes utilitaires qui permettent de créer des mises en page et des conceptions rapidement et facilement.
Par exemple, prenez le code HTML et CSS suivant :
<style>
.hello {
height: 5px;
width: 10px;
background: gray;
border-width: 1px;
border-radius: 3px;
padding: 5px;
}
</style>
<div class="hello">Hello World</div>
Cela peut être implémenté avec Tailwind comme ceci :
<div class="h-1 w-2 bg-gray-600 border rounded-sm p-1">Hello World</div>
Découvrez le convertisseur CSS Tailwind pour convertir le CSS brut en classes utilitaires équivalentes dans Tailwind. Comparez les résultats.
Avantages :
<div class="bg-white dark:bg-black">
.Inconvénients :
Flask-Assets est une extension conçue pour gérer les actifs statiques dans une application Flask. Avec lui, vous créez un pipeline d'actifs simple pour :
Sur ce, regardons comment travailler avec chacun des outils ci-dessus dans Flask !
Pour commencer, créez un nouveau répertoire pour notre projet, créez et activez un nouvel environnement virtuel et installez Flask avec Flask-Assets :
$ mkdir flask-htmx-tailwind && cd flask-htmx-tailwind
$ python3.10 -m venv venv
$ source venv/bin/activate
(venv)$
(venv)$ pip install Flask==2.1.1 Flask-Assets==2.0
Ensuite, installons pytailwindcss et téléchargeons son binaire :
(venv)$ pip install pytailwindcss==0.1.4
(venv)$ tailwindcss
Ensuite, ajoutez un fichier app.py :
# app.py
from flask import Flask
from flask_assets import Bundle, Environment
app = Flask(__name__)
assets = Environment(app)
css = Bundle("src/main.css", output="dist/main.css")
assets.register("css", css)
css.build()
Après avoir importé Bundle et Environment , nous avons créé un nouveau Environment
fichier et y avons enregistré nos actifs CSS via un fichier Bundle
.
Le bundle que nous avons créé prend src/main.css en entrée, qui sera ensuite traité et envoyé à dist/main.css lorsque nous exécuterons la CLI Tailwind CSS.
Étant donné que tous les fichiers statiques Flask résident dans le dossier "static" par défaut, les dossiers "src" et "dist" mentionnés ci-dessus résident dans le dossier "static".
Sur ce, configurons Tailwind.
Commencez par créer un fichier de configuration Tailwind :
(venv)$ tailwindcss init
Cette commande a créé un fichier tailwind.config.js à la racine de votre projet. Toutes les personnalisations liées à Tailwind vont dans ce fichier.
Mettez à jour tailwind.config.js comme suit :
module.exports = {
content: [
'./templates/**/*.html',
],
theme: {
extend: {},
},
plugins: [],
}
Prenez note de la section de contenu . Ici, vous configurez les chemins vers les modèles HTML de votre projet. Tailwind CSS analysera vos modèles, recherchant les noms de classe Tailwind. Le fichier CSS de sortie généré contiendra uniquement le CSS pour les noms de classe pertinents trouvés dans vos fichiers de modèle. Cela aide à garder les fichiers CSS générés petits puisqu'ils ne contiendront que les styles qui sont réellement utilisés.
Ajoutez ce qui suit à static/src/main.css :
/* static/src/main.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
Ici, nous avons défini toutes les classes base
, components
et utilities
de Tailwind CSS.
Vous avez maintenant Flask-Assets et Tailwind câblés. Ensuite, nous verrons comment servir un fichier index.html pour voir le CSS en action.
Ajoutez une route avec un bloc principal pour exécuter le serveur de développement Flask sur app.py comme ceci :
# app.py
from flask import Flask, render_template
from flask_assets import Bundle, Environment
app = Flask(__name__)
assets = Environment(app)
css = Bundle("src/main.css", output="dist/main.css")
assets.register("css", css)
css.build()
@app.route("/")
def homepage():
return render_template("index.html")
if __name__ == "__main__":
app.run(debug=True)
Créez un dossier "modèles". Ensuite, ajoutez-y un fichier base.html :
<!-- templates/base.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{% assets 'css' %}
<link rel="stylesheet" href="{{ ASSET_URL }}">
{% endassets %}
<title>Flask + htmlx + Tailwind CSS</title>
</head>
<body class="bg-blue-100">
{% block content %}
{% endblock content %}
</body>
</html>
Prenez note du {% assets 'css' %}
bloc. Étant donné que nous avons enregistré le bundle CSS avec l'environnement de l'application, nous pouvons y accéder en utilisant le nom enregistré css
, et {{ ASSET_URL }}
utilisera automatiquement le chemin.
De plus, nous avons ajouté de la couleur au corps HTML via bg-blue-100
, ce qui change la couleur d'arrière-plan en bleu clair.
Ajoutez le fichier index.html :
<!-- templates/index.html -->
{% extends "base.html" %}
{% block content %}
<h1>Hello World</h1>
{% endblock content %}
Maintenant, exécutez la commande suivante à la racine du projet pour analyser les modèles de classes et générer un fichier CSS :
(venv)$ tailwindcss -i ./static/src/main.css -o ./static/dist/main.css --minify
Vous devriez voir un nouveau répertoire nommé "dist" dans le dossier "static".
Prenez note du fichier static/dist/main.css généré .
Démarrez le serveur de développement via python app.py
et accédez à http://localhost:5000 dans votre navigateur pour voir les résultats.
Avec Tailwind configuré, ajoutons htmx dans le mélange et créons une recherche en direct qui affiche les résultats au fur et à mesure que vous tapez.
Plutôt que de récupérer la bibliothèque htmx à partir d'un CDN, téléchargeons-la et utilisons Flask-Assets pour la regrouper.
Téléchargez la bibliothèque depuis https://unpkg.com/htmx.org@1.7.0/dist/htmx.js et enregistrez-la dans "static/src".
Maintenant, pour créer un nouveau bundle pour nos fichiers JavaScript, mettez à jour app.py comme ceci :
# app.py
from flask import Flask, render_template
from flask_assets import Bundle, Environment
app = Flask(__name__)
assets = Environment(app)
css = Bundle("src/main.css", output="dist/main.css")
js = Bundle("src/*.js", output="dist/main.js") # new
assets.register("css", css)
assets.register("js", js) # new
css.build()
js.build() # new
@app.route("/")
def homepage():
return render_template("index.html")
if __name__ == "__main__":
app.run(debug=True)
Ici, nous avons créé un nouveau bundle nommé js
, qui sort vers static/dist/main.js . Comme nous n'utilisons aucun filtre ici, les fichiers source et cible seront les mêmes.
Ensuite, ajoutez le nouvel élément à notre fichier base.html :
<!-- templates/base.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{% assets 'css' %}
<link rel="stylesheet" href="{{ ASSET_URL }}">
{% endassets %}
<!-- new -->
{% assets 'js' %}
<script type="text/javascript" src="{{ ASSET_URL }}"></script>
{% endassets %}
<title>Flask + htmlx + Tailwind CSS</title>
</head>
<body class="bg-blue-100">
{% block content %}
{% endblock content %}
</body>
</html>
Pour que nous ayons des données avec lesquelles travailler, enregistrez https://github.com/testdriveio/flask-htmx-tailwind/blob/master/todo.py dans un nouveau fichier appelé todo.py .
Nous ajouterons la possibilité de rechercher en fonction du titre de chaque tâche.
Mettez à jour le fichier index.html comme suit :
<!-- templates/index.html -->
{% extends 'base.html' %}
{% block content %}
<div class="w-small w-2/3 mx-auto py-10 text-gray-600">
<input
type="text"
name="search"
hx-post="/search"
hx-trigger="keyup changed delay:250ms"
hx-indicator=".htmx-indicator"
hx-target="#todo-results"
placeholder="Search"
class="bg-white h-10 px-5 pr-10 rounded-full text-2xl focus:outline-none"
>
<span class="htmx-indicator">Searching...</span>
</div>
<table class="border-collapse w-small w-2/3 mx-auto">
<thead>
<tr>
<th class="p-3 font-bold uppercase bg-gray-200 text-gray-600 border border-gray-300 hidden lg:table-cell">#</th>
<th class="p-3 font-bold uppercase bg-gray-200 text-gray-600 border border-gray-300 hidden lg:table-cell">Title</th>
<th class="p-3 font-bold uppercase bg-gray-200 text-gray-600 border border-gray-300 hidden lg:table-cell">Completed</th>
</tr>
</thead>
<tbody id="todo-results">
{% include 'todo.html' %}
</tbody>
</table>
{% endblock content %}
Prenons un moment pour examiner les attributs définis à partir de htmx :
<input
type="text"
name="search"
hx-post="/search"
hx-trigger="keyup changed delay:250ms"
hx-indicator=".htmx-indicator"
hx-target="#todo-results"
placeholder="Search"
class="bg-white h-10 px-5 pr-10 rounded-full text-2xl focus:outline-none"
>
/search
terminaison.#todo-results
élément.Ajoutez le fichier templates/todo.html :
<!-- templates/todo.html -->
{% if todos|length>0 %}
{% for todo in todos %}
<tr class="bg-white lg:hover:bg-gray-100 flex lg:table-row flex-row lg:flex-row flex-wrap lg:flex-no-wrap mb-10 lg:mb-0">
<td class="w-full lg:w-auto p-3 text-gray-800 text-center border border-b block lg:table-cell relative lg:static">{{todo.id}}</td>
<td class="w-full lg:w-auto p-3 text-gray-800 text-center border border-b block lg:table-cell relative lg:static">{{todo.title}}</td>
<td class="w-full lg:w-auto p-3 text-gray-800 text-center border border-b block lg:table-cell relative lg:static">
{% if todo.completed %}
<span class="rounded bg-green-400 py-1 px-3 text-xs font-bold">Yes</span>
{% else %}
<span class="rounded bg-red-400 py-1 px-3 text-xs font-bold">No</span>
{% endif %}
</td>
</tr>
{% endfor %}
{% endif %}
Ce fichier affiche les tâches qui correspondent à notre requête de recherche.
Enfin, ajoutez le gestionnaire de route à app.py :
@app.route("/search", methods=["POST"])
def search_todo():
search_term = request.form.get("search")
if not len(search_term):
return render_template("todo.html", todos=[])
res_todos = []
for todo in todos:
if search_term in todo["title"]:
res_todos.append(todo)
return render_template("todo.html", todos=res_todos)
Le /search
point de terminaison recherche les tâches et affiche le modèle todo.html avec tous les résultats.
Mettez à jour les importations en haut :
from flask import Flask, render_template, request
from flask_assets import Bundle, Environment
from todo import todos
Ensuite, mettez à jour le fichier CSS de sortie :
(venv)$ tailwindcss -i ./static/src/main.css -o ./static/dist/main.css --minify
Exécutez l'application à l'aide python app.py
de et accédez à nouveau à http://localhost:5000 pour la tester :
Dans ce tutoriel, nous avons vu comment :
htmx peut afficher des éléments sans recharger la page. Plus important encore, vous pouvez y parvenir sans écrire de code JavaScript. Bien que cela réduise la quantité de travail requise côté client, les données envoyées depuis le serveur peuvent être plus élevées car il envoie du HTML rendu.
Servir des modèles HTML partiels comme celui-ci était populaire au début des années 2000. htmx apporte une touche moderne à cette approche. En général, la diffusion de modèles partiels redevient populaire en raison de la complexité des frameworks tels que React et Vue. Vous pouvez ajouter WebSockets dans le mix pour apporter également des modifications en temps réel. Cette même approche est utilisée par le célèbre Phoenix LiveView . Vous pouvez en savoir plus sur HTML over WebSockets dans The Future of Web Software Is HTML-over-WebSockets and HTML Over WebSockets .
La bibliothèque est encore jeune, mais l'avenir s'annonce très prometteur.
Source : https://testdrive.io
1656873660
En este tutorial, aprenderá a configurar Flask con htmx y Tailwind CSS . El objetivo de htmx y Tailwind es simplificar el desarrollo web moderno para que pueda diseñar y habilitar la interactividad sin abandonar la comodidad y la facilidad de HTML. También veremos cómo usar Flask-Assets para agrupar y minimizar activos estáticos en una aplicación Flask.
htmx es una biblioteca que le permite acceder a funciones de navegador modernas como AJAX, CSS Transitions, WebSockets y Server-Sent Events directamente desde HTML, en lugar de usar JavaScript. Le permite crear interfaces de usuario rápidamente directamente en el marcado.
htmx amplía varias funciones ya integradas en el navegador, como realizar solicitudes HTTP y responder a eventos. Por ejemplo, en lugar de solo poder realizar solicitudes GET y POST a través de elementos a
y form
, puede usar atributos HTML para enviar solicitudes GET, POST, PUT, PATCH o DELETE en cualquier elemento HTML:
<button hx-delete="/user/1">Delete</button>
También puede actualizar partes de una página para crear una aplicación de página única (SPA): enlace CodePen
Abra la pestaña de red en las herramientas de desarrollo del navegador. Cuando se hace clic en el botón, se envía una solicitud XHR al https://v2.jokeapi.dev/joke/Any?format=txt&safe-mode
punto final. Luego, la respuesta se agrega al p
elemento con un id
de salida.
Para obtener más ejemplos, consulte la página de ejemplos de interfaz de usuario de los documentos oficiales de htmx.
Ventajas :
Contras :
Tailwind CSS es un marco CSS de "utilidad primero". En lugar de enviar componentes preconstruidos (en los que se especializan marcos como Bootstrap y Bulma ), proporciona bloques de construcción en forma de clases de utilidad que permiten crear diseños y diseños de forma rápida y sencilla.
Por ejemplo, tome el siguiente HTML y CSS:
<style>
.hello {
height: 5px;
width: 10px;
background: gray;
border-width: 1px;
border-radius: 3px;
padding: 5px;
}
</style>
<div class="hello">Hello World</div>
Esto se puede implementar con Tailwind así:
<div class="h-1 w-2 bg-gray-600 border rounded-sm p-1">Hello World</div>
Consulte el convertidor CSS Tailwind para convertir CSS sin formato en las clases de utilidad equivalentes en Tailwind. Compara los resultados.
Ventajas :
<div class="bg-white dark:bg-black">
.Contras :
Flask-Assets es una extensión diseñada para administrar activos estáticos en una aplicación Flask. Con él, crea una canalización de activos simple para:
Con eso, ¡veamos cómo trabajar con cada una de las herramientas anteriores en Flask!
Para comenzar, cree un nuevo directorio para nuestro proyecto, cree y active un nuevo entorno virtual e instale Flask junto con Flask-Assets:
$ mkdir flask-htmx-tailwind && cd flask-htmx-tailwind
$ python3.10 -m venv venv
$ source venv/bin/activate
(venv)$
(venv)$ pip install Flask==2.1.1 Flask-Assets==2.0
A continuación, instalemos pytailwindcss y descarguemos su binario:
(venv)$ pip install pytailwindcss==0.1.4
(venv)$ tailwindcss
A continuación, agregue un archivo app.py :
# app.py
from flask import Flask
from flask_assets import Bundle, Environment
app = Flask(__name__)
assets = Environment(app)
css = Bundle("src/main.css", output="dist/main.css")
assets.register("css", css)
css.build()
Después de importar Bundle y Environment , creamos un nuevo Environment
y registramos nuestros activos CSS a través de un archivo Bundle
.
El paquete que creamos toma src/main.css como entrada, que luego se procesará y enviará a dist/main.css cuando ejecutemos Tailwind CSS CLI.
Dado que todos los archivos estáticos de Flask residen en la carpeta "estática" de forma predeterminada, las carpetas "src" y "dist" mencionadas anteriormente residen en la carpeta "estática".
Con eso, configuremos Tailwind.
Comience creando un archivo de configuración de Tailwind:
(venv)$ tailwindcss init
Este comando creó un archivo tailwind.config.js en la raíz de su proyecto. Todas las personalizaciones relacionadas con Tailwind van a este archivo.
Actualice tailwind.config.js así:
module.exports = {
content: [
'./templates/**/*.html',
],
theme: {
extend: {},
},
plugins: [],
}
Toma nota de la sección de contenido . Aquí configura las rutas a las plantillas HTML de su proyecto. Tailwind CSS escaneará sus plantillas, buscando nombres de clases de Tailwind. El archivo CSS de salida generado solo contendrá CSS para los nombres de clase relevantes que se encuentran en sus archivos de plantilla. Esto ayuda a mantener pequeños los archivos CSS generados, ya que solo contendrán los estilos que realmente se están utilizando.
Agrega lo siguiente a static/src/main.css :
/* static/src/main.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
Aquí, definimos todas las clases base
, components
y utilities
de Tailwind CSS.
Ahora tiene Flask-Assets y Tailwind conectados. A continuación, veremos cómo publicar un archivo index.html para ver el CSS en acción.
Agregue una ruta junto con un bloque principal para ejecutar el servidor de desarrollo de Flask en app.py de la siguiente manera:
# app.py
from flask import Flask, render_template
from flask_assets import Bundle, Environment
app = Flask(__name__)
assets = Environment(app)
css = Bundle("src/main.css", output="dist/main.css")
assets.register("css", css)
css.build()
@app.route("/")
def homepage():
return render_template("index.html")
if __name__ == "__main__":
app.run(debug=True)
Cree una carpeta de "plantillas". Luego, agregue un archivo base.html :
<!-- templates/base.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{% assets 'css' %}
<link rel="stylesheet" href="{{ ASSET_URL }}">
{% endassets %}
<title>Flask + htmlx + Tailwind CSS</title>
</head>
<body class="bg-blue-100">
{% block content %}
{% endblock content %}
</body>
</html>
Toma nota del {% assets 'css' %}
bloque. Dado que registramos el paquete CSS con el entorno de la aplicación, podemos acceder a él usando el nombre registrado css
, y {{ ASSET_URL }}
automáticamente usará la ruta.
Además, agregamos algo de color al cuerpo HTML a través bg-blue-100
de , que cambia el color de fondo a azul claro.
Agregue el archivo index.html :
<!-- templates/index.html -->
{% extends "base.html" %}
{% block content %}
<h1>Hello World</h1>
{% endblock content %}
Ahora, ejecute el siguiente comando en la raíz del proyecto para escanear las plantillas en busca de clases y generar un archivo CSS:
(venv)$ tailwindcss -i ./static/src/main.css -o ./static/dist/main.css --minify
Debería ver un nuevo directorio llamado "dist" dentro de la carpeta "estática".
Tome nota del archivo static/dist/main.css generado .
Inicie el servidor de desarrollo a través python app.py
de y navegue hasta http://localhost:5000 en su navegador para ver los resultados.
Con Tailwind configurado, agreguemos htmx a la mezcla y construyamos una búsqueda en vivo que muestre los resultados a medida que escribe.
En lugar de obtener la biblioteca htmx de un CDN, descárguemosla y usemos Flask-Assets para agruparla.
Descargue la biblioteca de https://unpkg.com/htmx.org@1.7.0/dist/htmx.js y guárdela en "static/src".
Ahora, para crear un nuevo paquete para nuestros archivos JavaScript, actualice app.py así:
# app.py
from flask import Flask, render_template
from flask_assets import Bundle, Environment
app = Flask(__name__)
assets = Environment(app)
css = Bundle("src/main.css", output="dist/main.css")
js = Bundle("src/*.js", output="dist/main.js") # new
assets.register("css", css)
assets.register("js", js) # new
css.build()
js.build() # new
@app.route("/")
def homepage():
return render_template("index.html")
if __name__ == "__main__":
app.run(debug=True)
Aquí, creamos un nuevo paquete llamado js
, que sale a static/dist/main.js . Como no estamos usando ningún filtro aquí, los archivos de origen y de destino serán los mismos.
A continuación, agregue el nuevo activo a nuestro archivo base.html :
<!-- templates/base.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{% assets 'css' %}
<link rel="stylesheet" href="{{ ASSET_URL }}">
{% endassets %}
<!-- new -->
{% assets 'js' %}
<script type="text/javascript" src="{{ ASSET_URL }}"></script>
{% endassets %}
<title>Flask + htmlx + Tailwind CSS</title>
</head>
<body class="bg-blue-100">
{% block content %}
{% endblock content %}
</body>
</html>
Para que tengamos algunos datos con los que trabajar, guarde https://github.com/testdrivenio/flask-htmx-tailwind/blob/master/todo.py en un nuevo archivo llamado todo.py.
Agregaremos la capacidad de buscar según el título de cada tarea pendiente.
Actualice el archivo index.html así:
<!-- templates/index.html -->
{% extends 'base.html' %}
{% block content %}
<div class="w-small w-2/3 mx-auto py-10 text-gray-600">
<input
type="text"
name="search"
hx-post="/search"
hx-trigger="keyup changed delay:250ms"
hx-indicator=".htmx-indicator"
hx-target="#todo-results"
placeholder="Search"
class="bg-white h-10 px-5 pr-10 rounded-full text-2xl focus:outline-none"
>
<span class="htmx-indicator">Searching...</span>
</div>
<table class="border-collapse w-small w-2/3 mx-auto">
<thead>
<tr>
<th class="p-3 font-bold uppercase bg-gray-200 text-gray-600 border border-gray-300 hidden lg:table-cell">#</th>
<th class="p-3 font-bold uppercase bg-gray-200 text-gray-600 border border-gray-300 hidden lg:table-cell">Title</th>
<th class="p-3 font-bold uppercase bg-gray-200 text-gray-600 border border-gray-300 hidden lg:table-cell">Completed</th>
</tr>
</thead>
<tbody id="todo-results">
{% include 'todo.html' %}
</tbody>
</table>
{% endblock content %}
Tomemos un momento para ver los atributos definidos desde htmx:
<input
type="text"
name="search"
hx-post="/search"
hx-trigger="keyup changed delay:250ms"
hx-indicator=".htmx-indicator"
hx-target="#todo-results"
placeholder="Search"
class="bg-white h-10 px-5 pr-10 rounded-full text-2xl focus:outline-none"
>
/search
punto final.#todo-results
elemento.Agrega el archivo templates/todo.html :
<!-- templates/todo.html -->
{% if todos|length>0 %}
{% for todo in todos %}
<tr class="bg-white lg:hover:bg-gray-100 flex lg:table-row flex-row lg:flex-row flex-wrap lg:flex-no-wrap mb-10 lg:mb-0">
<td class="w-full lg:w-auto p-3 text-gray-800 text-center border border-b block lg:table-cell relative lg:static">{{todo.id}}</td>
<td class="w-full lg:w-auto p-3 text-gray-800 text-center border border-b block lg:table-cell relative lg:static">{{todo.title}}</td>
<td class="w-full lg:w-auto p-3 text-gray-800 text-center border border-b block lg:table-cell relative lg:static">
{% if todo.completed %}
<span class="rounded bg-green-400 py-1 px-3 text-xs font-bold">Yes</span>
{% else %}
<span class="rounded bg-red-400 py-1 px-3 text-xs font-bold">No</span>
{% endif %}
</td>
</tr>
{% endfor %}
{% endif %}
Este archivo muestra todos los que coinciden con nuestra consulta de búsqueda.
Finalmente, agregue el controlador de ruta a app.py :
@app.route("/search", methods=["POST"])
def search_todo():
search_term = request.form.get("search")
if not len(search_term):
return render_template("todo.html", todos=[])
res_todos = []
for todo in todos:
if search_term in todo["title"]:
res_todos.append(todo)
return render_template("todo.html", todos=res_todos)
El /search
punto final busca todos y presenta la plantilla todo.html con todos los resultados.
Actualice las importaciones en la parte superior:
from flask import Flask, render_template, request
from flask_assets import Bundle, Environment
from todo import todos
A continuación, actualice el archivo CSS de salida:
(venv)$ tailwindcss -i ./static/src/main.css -o ./static/dist/main.css --minify
Ejecute la aplicación usando python app.py
y navegue hasta http://localhost:5000 nuevamente para probarla:
En este tutorial, vimos cómo:
htmx puede representar elementos sin recargar la página. Lo más importante es que puede lograr esto sin escribir JavaScript. Aunque esto reduce la cantidad de trabajo requerido en el lado del cliente, los datos enviados desde el servidor pueden ser más altos ya que está enviando HTML renderizado.
Ofrecer plantillas HTML parciales como esta fue popular a principios de la década de 2000. htmx proporciona un giro moderno a este enfoque. En general, ofrecer plantillas parciales se está volviendo popular nuevamente debido a la complejidad de los marcos como React y Vue. Puede agregar WebSockets a la mezcla para ofrecer cambios en tiempo real también. Este mismo enfoque es utilizado por el famoso Phoenix LiveView . Puede leer más sobre HTML sobre WebSockets en El futuro del software web es HTML sobre WebSockets y HTML sobre WebSockets .
La biblioteca aún es joven, pero el futuro parece muy prometedor.
Fuente: https://testdriven.io
1656866280
Neste tutorial, você aprenderá como configurar o Flask com htmx e Tailwind CSS . O objetivo do htmx e do Tailwind é simplificar o desenvolvimento web moderno para que você possa projetar e permitir a interatividade sem nunca deixar o conforto e a facilidade do HTML. Também veremos como usar Flask-Assets para agrupar e reduzir ativos estáticos em um aplicativo Flask.
htmx é uma biblioteca que permite acessar recursos modernos do navegador como AJAX, transições CSS, WebSockets e eventos enviados pelo servidor diretamente do HTML, em vez de usar JavaScript. Ele permite que você construa interfaces de usuário rapidamente diretamente na marcação.
htmx estende vários recursos já embutidos no navegador, como fazer solicitações HTTP e responder a eventos. Por exemplo, em vez de apenas poder fazer solicitações GET e POST por meio a
de elementos e form
, você pode usar atributos HTML para enviar solicitações GET, POST, PUT, PATCH ou DELETE em qualquer elemento HTML:
<button hx-delete="/user/1">Delete</button>
Você também pode atualizar partes de uma página para criar um aplicativo de página única (SPA): link CodePen
Abra a guia de rede nas ferramentas de desenvolvimento do navegador. Quando o botão é clicado, uma solicitação XHR é enviada ao https://v2.jokeapi.dev/joke/Any?format=txt&safe-mode
terminal. A resposta é então anexada ao p
elemento com uma id
saída.
Para obter mais exemplos, confira a página de exemplos de interface do usuário dos documentos htmx oficiais.
Prós :
Contras :
Tailwind CSS é um framework CSS "utilitário em primeiro lugar". Em vez de enviar componentes pré-construídos (nos quais frameworks como Bootstrap e Bulma se especializam), ele fornece blocos de construção na forma de classes de utilitários que permitem criar layouts e designs de maneira rápida e fácil.
Por exemplo, pegue o seguinte HTML e CSS:
<style>
.hello {
height: 5px;
width: 10px;
background: gray;
border-width: 1px;
border-radius: 3px;
padding: 5px;
}
</style>
<div class="hello">Hello World</div>
Isso pode ser implementado com o Tailwind assim:
<div class="h-1 w-2 bg-gray-600 border rounded-sm p-1">Hello World</div>
Confira o CSS Tailwind Converter para converter CSS bruto nas classes de utilitário equivalentes no Tailwind. Compare os resultados.
Prós :
<div class="bg-white dark:bg-black">
.Contras :
Flask-Assets é uma extensão projetada para gerenciar ativos estáticos em um aplicativo Flask. Com ele, você cria um pipeline de ativos simples para:
Com isso, vamos ver como trabalhar com cada uma das ferramentas acima no Flask!
Para começar, crie um novo diretório para nosso projeto, crie e ative um novo ambiente virtual e instale o Flask junto com o Flask-Assets:
$ mkdir flask-htmx-tailwind && cd flask-htmx-tailwind
$ python3.10 -m venv venv
$ source venv/bin/activate
(venv)$
(venv)$ pip install Flask==2.1.1 Flask-Assets==2.0
Em seguida, vamos instalar o pytailwindcss e fazer o download do binário:
(venv)$ pip install pytailwindcss==0.1.4
(venv)$ tailwindcss
Em seguida, adicione um arquivo app.py :
# app.py
from flask import Flask
from flask_assets import Bundle, Environment
app = Flask(__name__)
assets = Environment(app)
css = Bundle("src/main.css", output="dist/main.css")
assets.register("css", css)
css.build()
Depois de importar Bundle e Environment , criamos um novo Environment
e registramos nossos ativos CSS nele por meio de um arquivo Bundle
.
O pacote que criamos recebe src/main.css como entrada, que será processada e enviada para dist/main.css quando executarmos a CLI do Tailwind CSS.
Como todos os arquivos estáticos do Flask residem na pasta "static" por padrão, as pastas "src" e "dist" mencionadas acima residem na pasta "static".
Com isso, vamos configurar o Tailwind.
Comece criando um arquivo de configuração do Tailwind:
(venv)$ tailwindcss init
Este comando criou um arquivo tailwind.config.js na raiz do seu projeto. Todas as personalizações relacionadas ao Tailwind vão para este arquivo.
Atualize tailwind.config.js assim:
module.exports = {
content: [
'./templates/**/*.html',
],
theme: {
extend: {},
},
plugins: [],
}
Tome nota da seção de conteúdo . Aqui, você configura os caminhos para os modelos HTML do seu projeto. Tailwind CSS irá escanear seus templates, procurando por nomes de classes Tailwind. O arquivo CSS de saída gerado conterá apenas CSS para os nomes de classe relevantes encontrados em seus arquivos de modelo. Isso ajuda a manter os arquivos CSS gerados pequenos, pois eles conterão apenas os estilos que estão realmente sendo usados.
Adicione o seguinte a static/src/main.css :
/* static/src/main.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
Aqui, definimos todas as classes base
, components
e utilities
do Tailwind CSS.
Agora você tem Flask-Assets e Tailwind conectados. Em seguida, veremos como servir um arquivo index.html para ver o CSS em ação.
Adicione uma rota junto com um bloco principal para executar o servidor de desenvolvimento Flask para app.py assim:
# app.py
from flask import Flask, render_template
from flask_assets import Bundle, Environment
app = Flask(__name__)
assets = Environment(app)
css = Bundle("src/main.css", output="dist/main.css")
assets.register("css", css)
css.build()
@app.route("/")
def homepage():
return render_template("index.html")
if __name__ == "__main__":
app.run(debug=True)
Crie uma pasta "modelos". Em seguida, adicione um arquivo base.html a ele:
<!-- templates/base.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{% assets 'css' %}
<link rel="stylesheet" href="{{ ASSET_URL }}">
{% endassets %}
<title>Flask + htmlx + Tailwind CSS</title>
</head>
<body class="bg-blue-100">
{% block content %}
{% endblock content %}
</body>
</html>
Tome nota do {% assets 'css' %}
bloco. Como registramos o pacote CSS no ambiente do aplicativo, podemos acessá-lo usando o nome registrado css
, e o {{ ASSET_URL }}
usará automaticamente o caminho.
Além disso, adicionamos algumas cores ao corpo do HTML via bg-blue-100
, que altera a cor do plano de fundo para azul claro.
Adicione o arquivo index.html :
<!-- templates/index.html -->
{% extends "base.html" %}
{% block content %}
<h1>Hello World</h1>
{% endblock content %}
Agora, execute o seguinte comando na raiz do projeto para verificar os modelos de classes e gerar um arquivo CSS:
(venv)$ tailwindcss -i ./static/src/main.css -o ./static/dist/main.css --minify
Você deve ver um novo diretório chamado "dist" dentro da pasta "static".
Anote o arquivo static/dist/main.css gerado.
Inicie o servidor de desenvolvimento via python app.py
e navegue até http://localhost:5000 em seu navegador para ver os resultados.
Com o Tailwind configurado, vamos adicionar htmx à mistura e construir uma pesquisa ao vivo que exibe os resultados conforme você digita.
Em vez de buscar a biblioteca htmx de um CDN, vamos baixá-la e usar o Flask-Assets para agrupá-la.
Baixe a biblioteca de https://unpkg.com/htmx.org@1.7.0/dist/htmx.js e salve-a em "static/src".
Agora, para criar um novo pacote para nossos arquivos JavaScript, atualize app.py assim:
# app.py
from flask import Flask, render_template
from flask_assets import Bundle, Environment
app = Flask(__name__)
assets = Environment(app)
css = Bundle("src/main.css", output="dist/main.css")
js = Bundle("src/*.js", output="dist/main.js") # new
assets.register("css", css)
assets.register("js", js) # new
css.build()
js.build() # new
@app.route("/")
def homepage():
return render_template("index.html")
if __name__ == "__main__":
app.run(debug=True)
Aqui, criamos um novo pacote chamado js
, que gera static/dist/main.js . Como não estamos usando nenhum filtro aqui, os arquivos de origem e destino serão os mesmos.
Em seguida, adicione o novo ativo ao nosso arquivo base.html :
<!-- templates/base.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{% assets 'css' %}
<link rel="stylesheet" href="{{ ASSET_URL }}">
{% endassets %}
<!-- new -->
{% assets 'js' %}
<script type="text/javascript" src="{{ ASSET_URL }}"></script>
{% endassets %}
<title>Flask + htmlx + Tailwind CSS</title>
</head>
<body class="bg-blue-100">
{% block content %}
{% endblock content %}
</body>
</html>
Para que tenhamos alguns dados para trabalhar, salve https://github.com/testdrivedio/flask-htmx-tailwind/blob/master/todo.py em um novo arquivo chamado todo.py .
Adicionaremos a capacidade de pesquisar com base no título de cada tarefa.
Atualize o arquivo index.html assim:
<!-- templates/index.html -->
{% extends 'base.html' %}
{% block content %}
<div class="w-small w-2/3 mx-auto py-10 text-gray-600">
<input
type="text"
name="search"
hx-post="/search"
hx-trigger="keyup changed delay:250ms"
hx-indicator=".htmx-indicator"
hx-target="#todo-results"
placeholder="Search"
class="bg-white h-10 px-5 pr-10 rounded-full text-2xl focus:outline-none"
>
<span class="htmx-indicator">Searching...</span>
</div>
<table class="border-collapse w-small w-2/3 mx-auto">
<thead>
<tr>
<th class="p-3 font-bold uppercase bg-gray-200 text-gray-600 border border-gray-300 hidden lg:table-cell">#</th>
<th class="p-3 font-bold uppercase bg-gray-200 text-gray-600 border border-gray-300 hidden lg:table-cell">Title</th>
<th class="p-3 font-bold uppercase bg-gray-200 text-gray-600 border border-gray-300 hidden lg:table-cell">Completed</th>
</tr>
</thead>
<tbody id="todo-results">
{% include 'todo.html' %}
</tbody>
</table>
{% endblock content %}
Vamos dar uma olhada nos atributos definidos de htmx:
<input
type="text"
name="search"
hx-post="/search"
hx-trigger="keyup changed delay:250ms"
hx-indicator=".htmx-indicator"
hx-target="#todo-results"
placeholder="Search"
class="bg-white h-10 px-5 pr-10 rounded-full text-2xl focus:outline-none"
>
/search
terminal.#todo-results
elemento.Adicione o arquivo templates/todo.html :
<!-- templates/todo.html -->
{% if todos|length>0 %}
{% for todo in todos %}
<tr class="bg-white lg:hover:bg-gray-100 flex lg:table-row flex-row lg:flex-row flex-wrap lg:flex-no-wrap mb-10 lg:mb-0">
<td class="w-full lg:w-auto p-3 text-gray-800 text-center border border-b block lg:table-cell relative lg:static">{{todo.id}}</td>
<td class="w-full lg:w-auto p-3 text-gray-800 text-center border border-b block lg:table-cell relative lg:static">{{todo.title}}</td>
<td class="w-full lg:w-auto p-3 text-gray-800 text-center border border-b block lg:table-cell relative lg:static">
{% if todo.completed %}
<span class="rounded bg-green-400 py-1 px-3 text-xs font-bold">Yes</span>
{% else %}
<span class="rounded bg-red-400 py-1 px-3 text-xs font-bold">No</span>
{% endif %}
</td>
</tr>
{% endfor %}
{% endif %}
Este arquivo renderiza os todos que correspondem à nossa consulta de pesquisa.
Por fim, adicione o manipulador de rotas a app.py :
@app.route("/search", methods=["POST"])
def search_todo():
search_term = request.form.get("search")
if not len(search_term):
return render_template("todo.html", todos=[])
res_todos = []
for todo in todos:
if search_term in todo["title"]:
res_todos.append(todo)
return render_template("todo.html", todos=res_todos)
O /search
terminal procura todos e renderiza o modelo todo.html com todos os resultados.
Atualize as importações no topo:
from flask import Flask, render_template, request
from flask_assets import Bundle, Environment
from todo import todos
Em seguida, atualize o arquivo CSS de saída:
(venv)$ tailwindcss -i ./static/src/main.css -o ./static/dist/main.css --minify
Execute o aplicativo usando python app.py
e navegue até http://localhost:5000 novamente para testá-lo:
Neste tutorial, vimos como:
htmx pode renderizar elementos sem recarregar a página. Mais importante, você pode conseguir isso sem escrever nenhum JavaScript. Embora isso reduza a quantidade de trabalho necessária no lado do cliente, os dados enviados do servidor podem ser maiores, pois ele está enviando HTML renderizado.
Servir modelos HTML parciais como esse era popular no início dos anos 2000. htmx fornece um toque moderno a essa abordagem. Em geral, servir templates parciais está se tornando popular novamente devido à complexidade de frameworks como React e Vue. Você pode adicionar WebSockets à mistura para fornecer alterações em tempo real também. Essa mesma abordagem é usada pelo famoso Phoenix LiveView . Você pode ler mais sobre HTML sobre WebSockets em O futuro do software da Web é HTML sobre WebSockets e HTML sobre WebSockets .
A biblioteca ainda é jovem, mas o futuro parece muito brilhante.
Fonte: https://testdrive.io