Teagan  Taylor

Teagan Taylor

1673429760

Fds.js: Ethereum Web3 Stack SDK Prototype

FDS Dapp Framework

Overview

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:

  • Create Password Protected Account
  • Unlock (Sign In) Account
  • Store values using an encrypted key/value store
  • Store a file encrypted using AES-256-CTR
  • Send encrypted files to another account

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.

New in 0.2.0

  • Added support for node >=14.
  • Now works over the xDAI network
  • Minor bugs has been fixed!
  • ...and much more!

New in 0.1.0

  • Now works in a Node environment.
  • Plug in your own contracts!!
    • Simply deploy your own contracts then call functions, tx's are signed in the browser by FDS.
    • Work with existing contracts.
  • Much improved docs in await/sync syntax for your understanding pleasure. 💖
  • 🎁 The mighty multibox brings data interoperability to all of your FDIP Compliant Dapps!
  • ...and much more!

Features

  • Easily create Ethereum wallets
  • Store and share e2e encrypted values and files in the Swarm network.
  • Send Tokens and balance
  • Simply include your contract's abi and call functions - the rest is taken care of.
  • Totally decentralised, Zero Data, nothing leaves the your computer unencrypted, and only you have the key.

Install

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.

Quick Start

Create 2 Accounts

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')

Send a File from Alice to Bob

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')

Bob Receives It

... 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()

Examples

Use Your Own Contracts

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!


API Reference

FDS Object

You must first create an FDS object to work with.

To instantiate this using the default options:

let fds = new FDS()

Config

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',
  },
})

FDS Object


Description

The FDS object is used to create and manage user accounts.

Initialise FDS Object

Creates a new FDS object.

let fds = new FDS()

CreateAccount

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

  • username (string) [ . delimited string satisfying Namehash requirements ].
  • password (string)
  • feedbackMessageCallback (function) [callback for displaying progress messages]

Returns

promise User (User Object)

let alice = await FDS.CreateAccount('alice', 'password', (message) => { ... });

UnlockAccount

Unlocks an account that already exists in local storage.

FDS.UnlockAccount( username, password )

Inputs

  • username (string) [ . delimited string satisfying Namehash requirements ].
  • password (string)

Returns

User (User Object)

or

Failed (bool false)

let alice = await FDS.UnlockAccount('alice', 'password')

GetAccounts

Gets a list of the accounts held in local storage.

async FDS.GetAccounts( walletVersion = [most recent wallet version] )

Inputs

  • walletVersion (string) [ version of wallet to use, older wallets relate to legacy versions of fds.js ].

Returns

Users (Array)[User Object]

FDS.GetAccounts()

BackupAccount

Starts download of wallet backup file.

Must be in the browser environment.

async FDS.BackupAccount( subdomain )

Inputs

  • username (string) [ . delimited string satisfying Namehash requirements, account must exist in local storage ].

Returns

Success (bool)

FDS.BackupAccount('fds-ftw')

BackupAccountAsJSON

Returns a Ethereum style V3 wallet javascript object for a given username.

async FDS.BackupAccountAsJSON( username )

Inputs

  • subdomain (string) [ the username that is associated with the wallet]

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')

RestoreAccount

Restores account from file object and saves it into the local storage.

async FDS.RestoreAccount( backupFile )

Inputs

  • backupFile (file) [ A File object containing JSON V3 wallet and with filename in the format fds-wallet-dan1234-backup.json ]

Returns

promise Success (bool)

//retrieve backup from
await FDS.RestoreAccount(backupFile)

RestoreAccountFromPrivateKey

Restores account from a private key and saves it into the browser's localstorage.

async FDS.RestoreAccountFromPrivateKey( username, password, privateKey )

Inputs

  • username (string) [ the username that is associated with the private key]
  • password (string) [ the password with which to encrypt the private key in localstorage]
  • privateKey (string) [ private key as hex string without 0x prefix ]

Returns

promise User (User Object)

//retrieve backup from
await FDS.RestoreAccountFromPrivateKey('username', 'password', 'private-key-with-0x')

RestoreAccountFromJSON

Restores account from a json V3 wallet and saves it into the browser's localstorage.

async FDS.RestoreAccountFromJSON( username, jsonString )

Inputs

  • subdomain (string) [ the username that is associated with the wallet]
  • jsonString (string) [ V3 encrypted wallet ]

Returns

promise User (User Object)

await FDS.RestoreAccountFromJSON('username', '{..}')

DeleteAccount(username);

Deletes an account from localstorage.

async FDS.DeleteAccount( username )

Inputs

  • username (string) [ the username to be deleted]

Returns

null

FDS.DeleteAccount('username')

User Object


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.


Attributes

  • subdomain (string) the username of the account
  • address (string) the address of the account
  • publicKey (string) the public key of the account
  • privateKey (string) the private key of the account
  • nonce (int)

Functions

send

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

  • recipientSubdomain (string) [ the user name of the recipient ]
  • file (File Object) [ the file to be sent, should be either a browser File object or a File Stub Object. ]
  • multiboxPath (string) [ the Multibox path to send your file to ]
  • encryptionCallback (function) [ callback function, encryptionStatus true if encryption complete ]
  • uploadCallback (function) [ callback function, returns percentage uploaded as first argument (see examples) ]
  • progressMessageCallback (function) [ callback function, returns string progress messages

Returns

Success (Bool)

let success = await bob.send(
  'alice',
  file,
  '/shared/mail',
  (encryptionStatus) => {},
  (percentageUploaded) => {},
  (progressMessageCallback) => {},
)

messages

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

  • type (string) [ 'received' or 'sent' ]
  • multiboxPath (string) [ the Multibox path to check for messages ]
  • encryptionCallback (function) [ callback function, fires when encryption complete ]
  • uploadCallback (function) [ callback function, returns percentage uploaded as first argument (see examples) ]
  • progressMessageCallback (function) [ callback function, returns string progress messages ]

Returns

Messages (Array)[Message Object]

let messages = await bob.messages('received', '/shared/files')

store

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

  • file (File Object) [ the file to be sent, should be either a browser File object or a File Stub Object. ]
  • encryptionCallback (function) [ callback function, encryptionStatus true when complete, ] default: console.log
  • uploadCallback (function) [ callback function, returns percentage uploaded as first argument (see examples) ] default: console.log
  • progressMessageCallback (function) [ callback function, returns string progress messages default: console.log

Returns

Success (Bool)

let success = await bob.store(
  file,
  (encryptionStatus) => {},
  (percentageUploaded) => {},
  (progressMessage) => {},
)

stored

Gets a list of stored files.

async user.stored( ** )

Inputs

Returns

StoredFiles (Array) [Hash Object]

let stored = await bob.stored()

storeValue

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

  • key (string) [ a string identifier for the value]
  • value (string) [ a string ]

Returns

StoredFiles (Array) [Hash Object]

let success = await a.storeValue('key231', 'hello encrypted value world')

getValue

Retrieves an encrypted string value that can has been stored by the user identified by a string key.

async user.getValue( key )

Inputs

  • key (string) [ a string identifier for the required value ]

Returns

Value (string)

let value = await a.retrieveValue('key231')
// 'hello encrypted value world'

deployContract

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.

See example

async user.deployContract( abi, bytecode, args = [] )

Inputs

  • abi (object) [ the application binary interface of the contract to be deployed ]
  • bytecode (string) [ 0x prefixed of the contract to be deployed ]
  • args (array) [ an array of arguments to be passed to the contract constructor ]
  • contractAddress (string) [address of the contract]

Returns

Contract (Contract Object)

let contract = await alice.deployContract([ { "inputs": [], ... } ] , '608060405234801561001057600080fd5b50...', ['my', 'arguments']);

getContract

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

Get a User's Balance

Gets a user's balance.

async user.getBalance( ** )

Inputs

Returns

Value (string)

let balance = await alice.getBalance([ { "inputs": [], ... } ], '0xsa3bsdfs' );
191832026900000000
// true

Get Current BlockNumber

Gets a user's balance.

async user.getBlockNumber( ** )

Inputs

Returns

Value (integer)

let bn = await alice.getBlockNumber()
123456789
// true

Pay a User

Pays a user native balance.

async user.pay( recipientSubdomain, amount, transactionCallback = console.log, transactionSignedCallback = console.log )

Inputs

  • abi (object) [ the application binary interface of the contract to be deployed ]
  • address (string) [ address of the deployed contract ]
  • transactionCallback (function)
  • transactionSignedCallback (function)

Returns

TransactionHash (string)

let balance = await alice.pay('bob', '0.1')
//0x3cf52d1..

Pay an Address

Pays a address native balance.

async user.pay( recipientAddress, amount, transactionCallback = console.log, transactionSignedCallback = console.log )

Inputs

  • abi (object) [ the application binary interface of the contract to be deployed ]
  • address (string) [ address of the deployed contract ]
  • transactionCallback (function)
  • transactionSignedCallback (function)

Returns

TransactionHash (string)

let balance = await alice.pay('0x234...', '0.1')
//0x3cff2d1..

Sign Data

Signs a arbitary data.

async user.sign( message )

Inputs

  • message (string) [ the message to be signed ]

Returns

Success (bool)

let balance = await alice.sign('message')
//0x3cff2d1..

Recover Address from Signature

Checks a message and signature and returns the address.

async user.recover( message, sig )

Inputs

  • message (string) [ the message which has been signed ]
  • sig (string) [ the signature ]

Returns

Address (address)

let balance = await alice.recover('message', '0xabc...')
//0x3cff2d1..

Message Object


Description

Message objects are returned from user.messages()


Attributes

  • to (string) [user the message was sent to]
  • from (string) [user the message was sent from]
  • hash (Hash Object) [the hash for the file that was sent]

Functions

getFile

Retrieves and decrypts a file from Swarm.

async message.getFile( decryptProgressCallback = console.log, downloadProgressCallback = console.log )

Inputs

  • decryptProgressCallback (string) [ the user name of the recipient ]
  • downloadProgressCallback (File Object) [ the file to be sent, should be either a browser File object or a File Stub Object. ]

Returns

File (File)

let file = await message.getFile()

saveAs

Uses filesaver to prompt a file download from the browser environment.

async message.saveAs( decryptProgressCallback = console.log, downloadProgressCallback = console.log )

Inputs

  • decryptProgressCallback (function) [ decryption progress callback ]
  • downloadProgressCallback (function) [ download progress callback ]

Returns

File (File)

let file = await message.saveAs()

Hash Object


Description

Hash objects are used to represent files encrypted and stored into swarm.


Attributes

address: 'ece513967ad1d7610f280ff1a6c619ae7458780bbd0e0ba687e60ba6e3ae47e2', file: { name: 'test.txt', type: 'text/plain' }, time: 1569241451971,

  • address (string) [location in Swarm]
  • file (object) [file meta info]
  • time (string) [unix epoch file created date]

Hash objects contain references to encrypted files stored in Swarm.


Functions

getFile

Retrieves and decrypts a file from Swarm.

async hash.getFile( decryptProgressCallback = console.log, downloadProgressCallback = console.log )

Inputs

  • decryptProgressCallback (function) [ decryption progress callback ]
  • downloadProgressCallback (function) [ download progress callback ]

Returns

promise File (File)

let file = await hash.getFile()

saveAs

Uses filesaver to prompt a file download from the browser environment.

async hash.saveAs( decryptProgressCallback = console.log, downloadProgressCallback = console.log )

Inputs

  • decryptProgressCallback (function) [ decryption progress callback ]
  • downloadProgressCallback (function) [ download progress callback ]

Returns

null

let file = await hash.saveAs()

gatewayLink

Uses filesaver to prompt a file download from the browser environment.

async hash.gatewayLink( ** )

Inputs

  • decryptProgressCallback (function) [ decryption progress callback ]
  • downloadProgressCallback (function) [ download progress callback ]

Returns

GatewayLink (string)

let file = await hash.gatewayLink()

Contract Object


The contract object exposes any Solidity methods, which can be called just like normal functions.

It is returned from GetContract or DeployContract.

  • contractAddress (string) [ address of the deployed contract ]
  • web3Instance (object) [ web3 instance of the deployed contract ]

async contract.myMethod( arg1, arg2, ... )

myMethod can be any function, getter or setter of your contract

Inputs

  • arg1 (any) [ the argument to the solidty function ] #todo

Notes


Notes on the Node Environment

File Objects in Node

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
  }
}

Node REPL

When using the Node at the command line, you may find it useful to enable top tier await functionality.

node --experimental-repl-await

Multibox Contract

alt text

Troubleshooting Windows installation

Installation steps for Windows to setup FDS library to be used for development locally

Windows 10 node: 10.15.1 npm: 6.4.1

  1. Clone the FDS repo:
git clone https://github.com/fairDataSociety/fds.js.git
  1. Change directory to the FDS repo
  2. Run:
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';

Download details:

Author: fairDataSociety
Source code: https://github.com/fairDataSociety/fds.js

License: LGPL-3.0, GPL-3.0 licenses found

#Web3 #ethereum 

Fds.js: Ethereum Web3 Stack SDK Prototype
Rupert  Beatty

Rupert Beatty

1667811060

Rxtodo: iOS Todo Application using RxSwift and ReactorKit

RxTodo

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 😛)

Features

  • Using ReactorKit
  • Using RxDataSources
  • Observing model create/update/delete across the view controllers
  • Navigating between view controllers
  • Immutable models
  • Testing with RxExpect

Architecture

reactorkit

Visit ReactorKit for detail.

Requirements

  • iOS 8+
  • Swift 3
  • CocoaPods

Screenshots

rxtodo

Contribution

Discussion and pull requests are welcomed 💖

Download Details:

Author: Devxoul
Source Code: https://github.com/devxoul/RxTodo 
License: MIT license

#swift #ios #todo #rxswift #reactorkit 

Rxtodo: iOS Todo Application using RxSwift and ReactorKit
Reid  Rohan

Reid Rohan

1659460320

Todomvc-imba: Imba TodoMVC Example

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.

Resources

Support

Let us know if you discover anything worth sharing.

Running

  1. Run npm install
  2. Open index.html in your browser to see it in action!

Development

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.

Credit

Created by Sindre Aarsaether

Original article source at: https://github.com/somebee/todomvc-imba  

#javascript #todo #mvc 

Todomvc-imba: Imba TodoMVC Example
Nat  Grady

Nat Grady

1658012940

Ao: Elegant Microsoft to-Do Desktop App

Ao

Elegant Microsoft To-Do desktop app

Ao

Description

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.

Highlights

  • Black, Dark & Sepia Themes
  • Compact & Auto-Night Modes
  • Local & Global Customizable Keyboard Shortcuts
  • List Navigation
  • Scalable Interface
  • Update Notifications
  • Cross Platform

Install

Github Releases

Head to the releases page and download the appropriate installer for your system.

Snapcraft

Ubuntu Linux users can directly install through Snapcraft snap install ao

Homebrew

Macos users can directly install through Homebrew Cask brew cask install ao

Note

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.

Features

Visit the project homepage to view all features in detail.

  • Auto Night Mode - Press Cmd/Ctrl Alt N to allow Tusk to adjust to your environment.
  • Black Theme - Activate it by pressing Cmd/Ctrl B
  • Compact Mode - Downsize the window to enter the mode.
  • Custom Shortcut Keys - Navigate to ~/.ao.json or press Cmd/Ctrl . to modify any shortcut key. To reset delete ~/.ao.json & restart the app.
  • Dark Theme -Activate it by pressing Cmd/Ctrl H
  • Global Shortcut Keys - Enable them by using the File > Enable Global Shortcut Keys option.
  • List Navigation - Navigate your lists by pressing Cmd/Ctrl Tab / Cmd/Ctrl Shift Tab or jump directly to one by using Cmd/Ctrl 1 - 9
  • Scalable Interface - Adjust the zooming factor by pressing Cmd/Ctrl Shift = or Cmd/Ctrl -.
  • Sepia Theme - Activate it by pressing Cmd/Ctrl G.
  • Update Notifications - Customize the app's update checking frequency.

Keyboard Shortcuts

Local Shortcut Keys

40+ local keyboard shortcuts. Toggle anything in a flash.

View all the available local keyboard shortcuts.
 

DescriptionKeys
Activate Auto Night ModeCmd/Ctrl Alt N
Add Due DateCmd/Ctrl Shift T
Add Todo to My DayCmd/Ctrl K
Complete TodoCmd/Ctrl Shift N
Delete ListCmd/Ctrl Shift D
Delete TodoCmd/Ctrl D
Edit Shortcut KeysCmd/Ctrl .
Hide Completed TodosCmd/Ctrl Shift H
Jump to ListCmd/Ctrl 1 - 9
Make Text LargerCmd/Ctrl Shift =
Make Text SmallerCmd/Ctrl -
Navigate to Next ListCmd/Ctrl Tab
New ListCmd/Ctrl L
New TodoCmd/Ctrl N
Rename ListCmd/Ctrl Y
Rename TodoCmd/Ctrl T
Reset Zoom LevelCmd/Ctrl 0
Return to TodosEsc
Search TodosCmd/Ctrl F
Set Always on TopCmd/Ctrl Shift P
Set ReminderCmd/Ctrl Shift E
Sign outCmd/Ctrl Alt Q
Toggle Black ThemeCmd/Ctrl B
Toggle Dark ThemeCmd/Ctrl H
Toggle ImportantCmd/Ctrl I
Toggle My DayCmd/Ctrl M
Toggle PlannedCmd/Ctrl P
Toggle Sepia ThemeCmd/Ctrl G
Toggle SettingsCmd/Ctrl ,
Toggle SidebarCmd/Ctrl O
Toggle TasksCmd/Ctrl J
Toggle Window MenuAlt


 

Global Shortcut Keys

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.
 

DescriptionGlobal Shortcut
Create New TodoCmd/Ctrl Alt C
Search TodosCmd/Ctrl Alt F
Toggle Ao WindowCmd/Ctrl Alt A


 

Development

For more info on how to contribute to the project, please read the contributing guidelines.

  • Fork the repository and clone it to your machine
  • Navigate to your local fork cd ao
  • Install the project dependencies npm install or yarn install
  • Run Ao on dev mode npm start or yarn start
  • Lint code for errors npm test or yarn test
  • Build binaries and installers npm run release or yarn release

Related Apps

  • Tusk - Refined Evernote desktop app.
  • Taskbook - Tasks, boards & notes for the command-line habitat.

Team

Disclaimer

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

#electron #microsoft #app #todo 

Ao: Elegant Microsoft to-Do Desktop App
Lawrence  Lesch

Lawrence Lesch

1657967040

Level-todo: Todo List in Leveldb

level-todo

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

#javascript #leveldb #todo #node 

Level-todo: Todo List in Leveldb
Duyen Hoang

Duyen Hoang

1657029720

Cách thiết lập Django với htmx và Tailwind CSS

Trong hướng dẫn này, bạn sẽ học cách thiết lập Django với htmxTailwind 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

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 aformcá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 pphầ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 và nhược điểm

Ưu điểm :

  1. Năng suất của nhà phát triển : Bạn có thể xây dựng giao diện người dùng hiện đại mà không cần chạm vào JavaScript. Để biết thêm về điều này, hãy xem An SPA Alternative .
  2. Đóng gói một cú đấm : Bản thân thư viện nhỏ (~ 10k min.gz'd), không phụ thuộc và có thể mở rộng .

Nhược điểm :

  1. Sự trưởng thành của thư viện : Vì thư viện còn khá mới nên việc triển khai tài liệu và ví dụ rất thưa thớt.
  2. Kích thước dữ liệu được truyền : Thông thường, các khung công tác SPA (như React và Vue) hoạt động bằng cách truyền dữ liệu qua lại giữa máy khách và máy chủ ở định dạng JSON. Dữ liệu nhận được sau đó sẽ được hiển thị bởi máy khách. Mặt khác, htmx nhận HTML được kết xuất từ ​​máy chủ và nó thay thế phần tử đích bằng phản hồi. HTML ở định dạng được hiển thị thường lớn hơn về kích thước so với phản hồi JSON.

CSS Tailwind

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ư BootstrapBulma 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 và nhược điểm

Ưu điểm :

  1. Khả năng tùy chỉnh cao : Mặc dù Tailwind đi kèm với các lớp được tạo sẵn, nhưng chúng có thể được ghi đè bằng cách sử dụng tệp tailwind.config.js .
  2. Tối ưu hóa : Bạn có thể định cấu hình Tailwind để tối ưu hóa đầu ra CSS bằng cách chỉ tải các lớp thực sự được sử dụng.
  3. Chế độ tối : Thật dễ dàng để thực hiện chế độ tối - ví dụ <div class="bg-white dark:bg-black">:.

Nhược điểm :

  1. Thành phần : Tailwind không cung cấp bất kỳ thành phần chính thức nào được tạo sẵn như nút, thẻ, thanh điều hướng, v.v. Các thành phần phải được tạo ra từ đầu. Có một số tài nguyên do cộng đồng điều khiển cho các thành phần như Thành phần CSS Tailwind và Hộp công cụ Tailwind , có thể kể tên một số tài nguyên. Ngoài ra còn có một thư viện thành phần mạnh mẽ, mặc dù phải trả phí, của các nhà sản xuất Tailwind có tên Tailwind UI .
  2. CSS nội tuyến : Điều này kết hợp nội dung và thiết kế, điều này làm tăng kích thước trang và làm lộn xộn HTML.

Máy nén Django

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:

  1. Biên dịch SassLESS sang CSS stylesheet
  2. Kết hợp và giảm thiểu nhiều tệp CSS và JavaScript xuống thành một tệp duy nhất cho mỗi tệp
  3. Tạo các gói nội dung để sử dụng trong các mẫu của bạn

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!

Thiết lập dự án

Để 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_APPSdanh 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 TEMPLATEScà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ú:

  1. COMPRESS_ROOT xác định vị trí tuyệt đối mà từ đó các tệp được nén được đọc và các tệp nén được ghi vào.
  2. COMPRESS_ENABLED boolean để xác định xem quá trình nén có xảy ra hay không. Nó mặc định là giá trị ngược lại của DEBUG.
  3. STATICFILES_FINDERS phải bao gồm công cụ tìm tệp của Django Compressor khi 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ả basecomponentscác utilitieslớ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.

Ví dụ đơn giản

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.urlsvà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ú:

  1. {% load compress %}nhập tất cả các thẻ cần thiết để hoạt động với Django Compressor.
  2. {% load static %}tải các tệp tĩnh vào mẫu.
  3. {% 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.

Ví dụ về Tìm kiếm Trực tiế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, searchtì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"
>
  1. Đầu vào sẽ gửi một yêu cầu POST đến /searchđiểm cuối.
  2. Yêu cầu được kích hoạt thông qua sự kiện keyup với độ trễ 250 mili giây. Vì vậy, nếu một sự kiện keyup mới được nhập trước 250 mili giây trôi qua sau lần keyup cuối cùng, yêu cầu sẽ không được kích hoạt.
  3. Phản hồi HTML từ yêu cầu sau đó được hiển thị trong #todo-resultsphần tử.
  4. Chúng tôi cũng có một chỉ báo, một phần tử tải xuất hiện sau khi yêu cầu được gửi và biến mất sau khi phản hồi trở lại.

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 runservervà điều hướng đến http: // localhost: 8000 một lần nữa để kiểm tra.

thử nghiệm

Sự kết luận

Trong hướng dẫn này, chúng tôi đã xem xét cách:

  • Thiết lập Django Compressor và Tailwind CSS
  • Xây dựng ứng dụng tìm kiếm trực tiếp bằng Django, Tailwind CSS và htmx

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-WebSocketsHTML 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

#django #htmx #tailwindcss 

Cách thiết lập Django với htmx và Tailwind CSS

Как настроить Django с помощью htmx и Tailwind CSS

В этом руководстве вы узнаете, как настроить Django с помощью htmx и Tailwind CSS . Цель как htmx, так и Tailwind — упростить современную веб-разработку, чтобы вы могли проектировать и обеспечивать интерактивность, не отказываясь от удобства и простоты HTML. Мы также рассмотрим, как использовать Django Compressor для объединения и минимизации статических ресурсов в приложении Django.

htmx

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.

Плюсы и минусы

Плюсы :

  1. Продуктивность разработчиков : вы можете создавать современные пользовательские интерфейсы, не затрагивая JavaScript. Чтобы узнать больше об этом, ознакомьтесь с альтернативой SPA .
  2. Упаковывает удар : сама библиотека небольшая (~ 10 000 мин. gz'd), свободная от зависимостей и расширяемая .

Минусы :

  1. Зрелость библиотеки : Поскольку библиотека довольно новая, документация и примеры реализации скудны.
  2. Размер передаваемых данных . Как правило, фреймворки SPA (например, React и Vue) работают, передавая данные между клиентом и сервером в формате JSON. Полученные данные затем обрабатываются клиентом. htmx, с другой стороны, получает обработанный HTML-код с сервера и заменяет целевой элемент ответом. HTML-код в отображаемом формате обычно больше по размеру, чем ответ JSON.

Попутный ветер CSS

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. Сравните результаты.

Плюсы и минусы

Плюсы :

  1. Широкие возможности настройки : хотя Tailwind поставляется с готовыми классами, их можно перезаписать с помощью файла tailwind.config.js .
  2. Оптимизация : вы можете настроить Tailwind для оптимизации вывода CSS, загружая только те классы, которые фактически используются.
  3. Темный режим : реализовать темный режим несложно — например, <div class="bg-white dark:bg-black">.

Минусы :

  1. Компоненты : Tailwind не предоставляет каких-либо официальных готовых компонентов, таких как кнопки, карточки, панели навигации и т. д. Компоненты должны быть созданы с нуля. Существует несколько управляемых сообществом ресурсов для таких компонентов, как Tailwind CSS Components и Tailwind Toolbox , и это лишь некоторые из них. Также есть мощная, хотя и платная, библиотека компонентов от создателей Tailwind под названием Tailwind UI .
  2. Встроенный CSS : он объединяет контент и дизайн, что увеличивает размер страницы и загромождает HTML.

Джанго Компрессор

Django Compressor — это расширение, предназначенное для управления (сжатия/кэширования) статическими ресурсами в приложении Django. С его помощью вы создаете простой конвейер активов для:

  1. Компиляция Sass и LESS в таблицы стилей CSS
  2. Объединение и минимизация нескольких файлов CSS и JavaScript в один файл для каждого
  3. Создание пакетов ресурсов для использования в ваших шаблонах

Итак, давайте посмотрим, как работать с каждым из вышеперечисленных инструментов в 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',)

Заметки:

  1. COMPRESS_ROOT определяет абсолютное местоположение, откуда считываются сжимаемые файлы и куда записываются сжатые файлы.
  2. COMPRESS_ENABLED логическое значение, определяющее, будет ли происходить сжатие. По умолчанию используется значение, противоположное DEBUG.
  3. STATICFILES_FINDERS должен включать средство поиска файлов Django Compressor при 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>

Заметки:

  1. {% load compress %}импортирует все необходимые теги для работы с Django Compressor.
  2. {% load static %}загружает статические файлы в шаблон.
  3. {% 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"
>
  1. Вход отправляет запрос POST в /searchконечную точку.
  2. Запрос запускается через событие keyup с задержкой 250 мс. Таким образом, если новое событие нажатия клавиши вводится до истечения 250 мс после последнего нажатия клавиши, запрос не запускается.
  3. Затем HTML-ответ на запрос отображается в #todo-resultsэлементе.
  4. У нас также есть индикатор, элемент загрузки, который появляется после отправки запроса и исчезает после возврата ответа.

Добавьте файл 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 , чтобы проверить его.

демо

Вывод

В этом уроке мы рассмотрели, как:

  • Настройте Django Compressor и Tailwind CSS
  • Создайте приложение для поиска в реальном времени, используя Django, Tailwind CSS и htmx.

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

#django #htmx #tailwindcss 

Как настроить Django с помощью htmx и Tailwind CSS
Noelia  Graham

Noelia Graham

1657007820

Comment configurer Django avec htmx et Tailwind CSS

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

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 aet 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-modeterminaison. La réponse est ensuite ajoutée à l' pélément avec une idsortie of.

Pour plus d'exemples, consultez la page Exemples d'interface utilisateur de la documentation officielle htmx.

Avantages et inconvénients

Avantages :

  1. Productivité des développeurs : Vous pouvez créer des interfaces utilisateur modernes sans toucher à JavaScript. Pour en savoir plus, consultez An SPA Alternative .
  2. Emballe un coup de poing : la bibliothèque elle-même est petite (~ 10k min.gz'd), sans dépendance et extensible .

Inconvénients :

  1. Maturité de la bibliothèque : Étant donné que la bibliothèque est assez récente, la documentation et les exemples d'implémentation sont rares.
  2. Taille des données transférées : Typiquement, les frameworks SPA (comme React et Vue) fonctionnent en faisant passer des données entre le client et le serveur au format JSON. Les données reçues sont ensuite restituées par le client. htmx, d'autre part, reçoit le rendu HTML du serveur et remplace l'élément cible par la réponse. Le HTML au format rendu est généralement plus grand en termes de taille qu'une réponse JSON.

CSS vent arrière

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 et inconvénients

Avantages :

  1. Hautement personnalisable : bien que Tailwind soit fourni avec des classes prédéfinies, elles peuvent être écrasées à l'aide du fichier tailwind.config.js .
  2. Optimisation : vous pouvez configurer Tailwind pour optimiser la sortie CSS en ne chargeant que les classes réellement utilisées.
  3. Mode sombre : il est facile d'implémenter le mode sombre - par exemple, <div class="bg-white dark:bg-black">.

Inconvénients :

  1. Composants : Tailwind ne fournit aucun composant pré-construit officiel comme des boutons, des cartes, des barres de navigation, etc. Les composants doivent être créés à partir de zéro. Il existe quelques ressources communautaires pour des composants tels que Tailwind CSS Components et Tailwind Toolbox , pour n'en nommer que quelques-uns. Il existe également une bibliothèque de composants puissante, bien que payante, créée par les créateurs de Tailwind, appelée Tailwind UI .
  2. Le CSS est en ligne : Cela couple contenu et design, ce qui augmente la taille de la page et encombre le HTML.

Compresseur Django

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 :

  1. Compiler Sass et LESS en feuilles de style CSS
  2. Combiner et réduire plusieurs fichiers CSS et JavaScript jusqu'à un seul fichier pour chacun
  3. Création d'assets bundles à utiliser dans vos modèles

Sur ce, regardons comment travailler avec chacun des outils ci-dessus dans Django !

Configuration du projet

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 todosapplication :

(venv)$ django-admin startproject config .
(venv)$ python manage.py startapp todos

Ajoutez les applications à la INSTALLED_APPSliste 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 TEMPLATESparamè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:

  1. COMPRESS_ROOT définit l'emplacement absolu à partir duquel les fichiers à compresser sont lus et les fichiers compressés sont écrits.
  2. COMPRESS_ENABLED booléen pour déterminer si la compression va se produire. Il prend par défaut la valeur opposée de DEBUG.
  3. STATICFILES_FINDERS doit inclure l'outil de recherche de fichiers de Django Compressor lorsqu'il django.contrib.staticfilesest 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, componentset utilitiesde 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.

Exemple simple

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:

  1. {% load compress %}importe toutes les balises requises pour fonctionner avec Django Compressor.
  2. {% load static %}charge les fichiers statiques dans le modèle.
  3. {% 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-100est 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.

Exemple de recherche en direct

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 jsbalise, 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"
>
  1. L'entrée envoie une demande POST au point de /searchterminaison.
  2. La demande est déclenchée via un événement keyup avec un délai de 250 ms. Ainsi, si un nouvel événement de keyup est entré avant que 250 ms ne se soient écoulés après le dernier keyup, la demande n'est pas déclenchée.
  3. La réponse HTML de la requête est alors affichée dans l' #todo-resultsélément.
  4. Nous avons également un indicateur, un élément de chargement qui apparaît après l'envoi de la requête et disparaît après le retour de la réponse.

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 runserverde et accédez à nouveau à http://localhost:8000 pour la tester.

démo

Conclusion

Dans ce tutoriel, nous avons vu comment :

  • Configurer Django Compressor et Tailwind CSS
  • Créez une application de recherche en direct à l'aide de Django, Tailwind CSS et htmx

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

#django #htmx #tailwindcss 

Comment configurer Django avec htmx et Tailwind CSS

Cómo configurar Django con htmx y Tailwind CSS

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.

htmlx

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 ay 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-modepunto final. Luego, la respuesta se agrega al pelemento con un idde salida.

Para obtener más ejemplos, consulte la página de ejemplos de interfaz de usuario de los documentos oficiales de htmx.

Pros y contras

Ventajas :

  1. Productividad del desarrollador : puede crear interfaces de usuario modernas sin tocar JavaScript. Para obtener más información sobre esto, consulte Una alternativa de SPA .
  2. Tiene un gran impacto: la biblioteca en sí es pequeña (~10k min.gz'd ), libre de dependencias y extensible .

Contras :

  1. Madurez de la biblioteca: dado que la biblioteca es bastante nueva, la documentación y las implementaciones de ejemplo son escasas.
  2. Tamaño de los datos transferidos : por lo general, los marcos SPA (como React y Vue) funcionan pasando datos entre el cliente y el servidor en formato JSON. Los datos recibidos luego son procesados ​​por el cliente. htmx, por otro lado, recibe el HTML renderizado del servidor y reemplaza el elemento de destino con la respuesta. El HTML en formato renderizado suele ser más grande en términos de tamaño que una respuesta JSON.

CSS viento de cola

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.

Pros y contras

Ventajas :

  1. Altamente personalizable : aunque Tailwind viene con clases prediseñadas, se pueden sobrescribir con el archivo tailwind.config.js .
  2. Optimización : puede configurar Tailwind para optimizar la salida de CSS cargando solo las clases que realmente se utilizan.
  3. Modo oscuro : es fácil implementar el modo oscuro , por ejemplo, <div class="bg-white dark:bg-black">.

Contras :

  1. Componentes : Tailwind no proporciona ningún componente prediseñado oficial como botones, tarjetas, barras de navegación, etc. Los componentes tienen que ser creados desde cero. Hay algunos recursos impulsados ​​por la comunidad para componentes como Tailwind CSS Components y Tailwind Toolbox , por nombrar algunos. También hay una biblioteca de componentes poderosa, aunque paga, de los creadores de Tailwind llamada Tailwind UI .
  2. CSS está en línea : esto combina contenido y diseño, lo que aumenta el tamaño de la página y desordena el HTML.

Compresor Django

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:

  1. Compilación de hojas de estilo Sass y LESS a CSS
  2. Combinar y minimizar varios archivos CSS y JavaScript en un solo archivo para cada uno
  3. Creación de paquetes de activos para usar en sus plantillas

Con eso, ¡veamos cómo trabajar con cada una de las herramientas anteriores en Django!

Configuración del proyecto

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 todosaplicación:

(venv)$ django-admin startproject config .
(venv)$ python manage.py startapp todos

Agregue las aplicaciones a la INSTALLED_APPSlista 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 TEMPLATESconfiguració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:

  1. COMPRESS_ROOT define la ubicación absoluta desde donde se leen los archivos que se van a comprimir y donde se escriben los archivos comprimidos.
  2. COMPRESS_ENABLED booleano para determinar si se realizará la compresión. Por defecto es el valor opuesto de DEBUG.
  3. STATICFILES_FINDERS debe incluir el buscador de archivos de Django Compressor cuando django.contrib.staticfilesse 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, componentsy utilitiesde 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.

Ejemplo sencillo

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.urlsa 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:

  1. {% load compress %}importa todas las etiquetas requeridas para trabajar con Django Compressor.
  2. {% load static %}carga archivos estáticos en la plantilla.
  3. {% 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-100se 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.

Ejemplo de búsqueda en vivo

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 jsetiqueta, 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"
>
  1. La entrada envía una solicitud POST al /searchpunto final.
  2. La solicitud se activa a través de un evento keyup con un retraso de 250 ms. Por lo tanto, si se ingresa un nuevo evento de activación antes de que hayan transcurrido 250 ms después de la última activación, la solicitud no se activa.
  3. La respuesta HTML de la solicitud se muestra en el #todo-resultselemento.
  4. También tenemos un indicador, un elemento de carga que aparece después de que se envía la solicitud y desaparece después de que regresa la respuesta.

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 runservery navegue hasta http://localhost:8000 nuevamente para probarla.

manifestación

Conclusión

En este tutorial, vimos cómo:

  • Configurar Django Compressor y Tailwind CSS
  • Cree una aplicación de búsqueda en vivo usando Django, Tailwind CSS y htmx

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

#django #htmx #tailwindcss 

Cómo configurar Django con htmx y Tailwind CSS
Neil  Morgan

Neil Morgan

1656989520

Como Configurar o Django com htmx e Tailwind CSS

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

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 ade 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-modeterminal. A resposta é então anexada ao pelemento com uma idsaída.

Para obter mais exemplos, confira a página de exemplos de interface do usuário dos documentos htmx oficiais.

Prós e contras

Prós :

  1. Produtividade do desenvolvedor : você pode criar interfaces de usuário modernas sem tocar em JavaScript. Para saber mais sobre isso, confira An SPA Alternative .
  2. Um soco : A biblioteca em si é pequena (~10k min.gz'd), livre de dependências e extensível .

Contras :

  1. Maturidade da biblioteca: Como a biblioteca é bastante nova, a documentação e as implementações de exemplo são escassas.
  2. Tamanho dos dados transferidos : Normalmente, as estruturas SPA (como React e Vue) funcionam passando dados entre o cliente e o servidor no formato JSON. Os dados recebidos são então processados ​​pelo cliente. htmx, por outro lado, recebe o HTML renderizado do servidor e substitui o elemento de destino pela resposta. O HTML no formato renderizado geralmente é maior em termos de tamanho do que uma resposta JSON.

Tailwind CSS

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 e contras

Prós :

  1. Altamente personalizável : embora o Tailwind venha com classes pré-criadas, elas podem ser substituídas usando o arquivo tailwind.config.js .
  2. Otimização : Você pode configurar o Tailwind para otimizar a saída CSS carregando apenas as classes que são realmente usadas.
  3. Modo escuro : é fácil implementar o modo escuro - por exemplo, <div class="bg-white dark:bg-black">.

Contras :

  1. Componentes : O Tailwind não fornece nenhum componente oficial pré-construído, como botões, cartões, barras de navegação e assim por diante. Os componentes devem ser criados do zero. Existem alguns recursos orientados pela comunidade para componentes como Tailwind CSS Components e Tailwind Toolbox , para citar alguns. Há também uma biblioteca de componentes poderosa, embora paga, dos criadores do Tailwind chamada Tailwind UI .
  2. CSS é inline : combina conteúdo e design, o que aumenta o tamanho da página e desordena o HTML.

Compressor Django

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:

  1. Compilando Sass e LESS para folhas de estilo CSS
  2. Combinando e minificando vários arquivos CSS e JavaScript em um único arquivo para cada
  3. Criando pacotes de ativos para uso em seus modelos

Com isso, vamos ver como trabalhar com cada uma das ferramentas acima no Django!

Configuração do projeto

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 todosaplicativo:

(venv)$ django-admin startproject config .
(venv)$ python manage.py startapp todos

Adicione os aplicativos à INSTALLED_APPSlista 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 TEMPLATESconfiguraçõ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:

  1. COMPRESS_ROOT define o local absoluto de onde os arquivos a serem compactados são lidos e os arquivos compactados são gravados.
  2. COMPRESS_ENABLED booleano para determinar se a compactação ocorrerá. O padrão é o valor oposto de DEBUG.
  3. STATICFILES_FINDERS deve incluir o localizador de arquivos do Django Compressor quando django.contrib.staticfilesinstalado.

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, componentse utilitiesdo 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.

Exemplo Simples

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.urlsa 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:

  1. {% load compress %}importa todas as tags necessárias para trabalhar com o Django Compressor.
  2. {% load static %}carrega arquivos estáticos no modelo.
  3. {% 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.

Exemplo de pesquisa ao vivo

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 jstag, 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"
>
  1. A entrada envia uma solicitação POST para o /searchterminal.
  2. A solicitação é acionada por meio de um evento keyup com um atraso de 250ms. Portanto, se um novo evento de keyup for inserido antes de decorridos 250ms após a última keyup, a solicitação não será acionada.
  3. A resposta HTML da solicitação é exibida no #todo-resultselemento.
  4. Também temos um indicador, um elemento de carregamento que aparece depois que a solicitação é enviada e desaparece depois que a resposta volta.

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 runservere navegue até http://localhost:8000 novamente para testá-lo.

demonstração

Conclusão

Neste tutorial, vimos como:

  • Configure o Django Compressor e o Tailwind CSS
  • Crie um aplicativo de pesquisa ao vivo usando Django, Tailwind CSS e htmx

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

#django #htmx #tailwindcss 

Como Configurar o Django com htmx e Tailwind CSS
Duyen Hoang

Duyen Hoang

1656892380

Cách Thiết Lập Flask Với htmx Và Tailwind CSS

Trong hướng dẫn này, bạn sẽ học cách thiết lập Flask với htmxTailwind 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

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 aformcá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 pphầ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 và nhược điểm

Ưu điểm :

  1. Năng suất của nhà phát triển : Bạn có thể xây dựng giao diện người dùng hiện đại mà không cần chạm vào JavaScript. Để biết thêm về điều này, hãy xem An SPA Alternative .
  2. Đóng gói một cú đấm : Bản thân thư viện nhỏ (~ 10k min.gz'd), không phụ thuộc và có thể mở rộng .

Nhược điểm :

  1. Sự trưởng thành của thư viện : Vì thư viện còn khá mới nên việc triển khai tài liệu và ví dụ rất thưa thớt.
  2. Kích thước dữ liệu được truyền : Thông thường, các khung công tác SPA (như React và Vue) hoạt động bằng cách truyền dữ liệu qua lại giữa máy khách và máy chủ ở định dạng JSON. Dữ liệu nhận được sau đó sẽ được hiển thị bởi máy khách. Mặt khác, htmx nhận HTML được kết xuất từ ​​máy chủ và nó thay thế phần tử đích bằng phản hồi. HTML ở định dạng được hiển thị thường lớn hơn về kích thước so với phản hồi JSON.

CSS Tailwind

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ư BootstrapBulma 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 và nhược điểm

Ưu điểm :

  1. Khả năng tùy chỉnh cao : Mặc dù Tailwind đi kèm với các lớp được tạo sẵn, nhưng chúng có thể được ghi đè bằng cách sử dụng tệp tailwind.config.js .
  2. Tối ưu hóa : Bạn có thể định cấu hình Tailwind để tối ưu hóa đầu ra CSS bằng cách chỉ tải các lớp thực sự được sử dụng.
  3. Chế độ tối : Thật dễ dàng để thực hiện chế độ tối - ví dụ <div class="bg-white dark:bg-black">:.

Nhược điểm :

  1. Thành phần : Tailwind không cung cấp bất kỳ thành phần chính thức nào được tạo sẵn như nút, thẻ, thanh điều hướng, v.v. Các thành phần phải được tạo ra từ đầu. Có một số tài nguyên do cộng đồng điều khiển cho các thành phần như Thành phần CSS Tailwind và Hộp công cụ Tailwind , có thể kể tên một số tài nguyên. Ngoài ra còn có một thư viện thành phần mạnh mẽ, mặc dù phải trả phí của các nhà sản xuất Tailwind có tên Tailwind UI .
  2. CSS nội tuyến : Điều này kết hợp nội dung và thiết kế, điều này làm tăng kích thước trang và làm lộn xộn HTML.

Flask-Assets

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:

  1. Biên dịch SassLESS sang CSS stylesheet
  2. Kết hợp và giảm thiểu nhiều tệp CSS và JavaScript xuống thành một tệp duy nhất cho mỗi tệp
  3. Tạo các gói nội dung để sử dụng trong các mẫu của bạn

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!

Thiết lập dự án

Để 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óiMôi trường , chúng tôi đã tạo một Environmentnộ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ả basecomponentscác utilitieslớ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.

Ví dụ đơn giản

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{{ 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.pyvà đ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.

Ví dụ về Tìm kiếm Trực tiế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"
>
  1. Đầu vào sẽ gửi một yêu cầu POST đến /searchđiểm cuối.
  2. Yêu cầu được kích hoạt thông qua sự kiện keyup với độ trễ 250 mili giây. Vì vậy, nếu một sự kiện keyup mới được nhập trước 250 mili giây trôi qua sau lần keyup cuối cùng, yêu cầu sẽ không được kích hoạt.
  3. Phản hồi HTML từ yêu cầu sau đó được hiển thị trong #todo-resultsphần tử.
  4. Chúng tôi cũng có một chỉ báo, một phần tử tải xuất hiện sau khi yêu cầu được gửi và biến mất sau khi phản hồi trở lại.

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 /searchcuố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.pyvà điều hướng đến http: // localhost: 5000 một lần nữa để kiểm tra:

thử nghiệm

Sự kết luận

Trong hướng dẫn này, chúng tôi đã xem xét cách:

  • Thiết lập Flask-Assets, htmx và Tailwind CSS
  • Xây dựng ứng dụng tìm kiếm trực tiếp bằng Flask, Tailwind CSS và htmx

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-WebSocketsHTML 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

#flask #htmx #tailwindcss 

Cách Thiết Lập Flask Với htmx Và Tailwind CSS

Как настроить Flask с помощью htmx и Tailwind CSS

В этом руководстве вы узнаете, как настроить Flask с помощью htmx и Tailwind CSS . Цель как htmx, так и Tailwind — упростить современную веб-разработку, чтобы вы могли проектировать и обеспечивать интерактивность, не отказываясь от удобства и простоты HTML. Мы также рассмотрим, как использовать Flask-Assets для объединения и минимизации статических ресурсов в приложении Flask.

htmx

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.

Плюсы и минусы

Плюсы :

  1. Продуктивность разработчиков : вы можете создавать современные пользовательские интерфейсы, не затрагивая JavaScript. Чтобы узнать больше об этом, ознакомьтесь с альтернативой SPA .
  2. Упаковывает удар : сама библиотека небольшая (~ 10 000 мин. gz'd), свободная от зависимостей и расширяемая .

Минусы :

  1. Зрелость библиотеки : Поскольку библиотека довольно новая, документация и примеры реализации скудны.
  2. Размер передаваемых данных . Как правило, фреймворки SPA (например, React и Vue) работают, передавая данные между клиентом и сервером в формате JSON. Полученные данные затем обрабатываются клиентом. htmx, с другой стороны, получает отрендеренный HTML-код с сервера и заменяет целевой элемент ответом. HTML-код в отображаемом формате обычно больше по размеру, чем ответ JSON.

Попутный ветер CSS

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. Сравните результаты.

Плюсы и минусы

Плюсы :

  1. Широкие возможности настройки : хотя Tailwind поставляется с готовыми классами, их можно перезаписать с помощью файла tailwind.config.js .
  2. Оптимизация : вы можете настроить Tailwind для оптимизации вывода CSS, загружая только те классы, которые фактически используются.
  3. Темный режим : реализовать темный режим несложно — например, <div class="bg-white dark:bg-black">.

Минусы :

  1. Компоненты : Tailwind не предоставляет каких-либо официальных готовых компонентов, таких как кнопки, карточки, панели навигации и т. д. Компоненты должны быть созданы с нуля. Существует несколько управляемых сообществом ресурсов для таких компонентов, как Tailwind CSS Components и Tailwind Toolbox , и это лишь некоторые из них. Также есть мощная, хотя и платная, библиотека компонентов от создателей Tailwind под названием Tailwind UI .
  2. Встроенный CSS : он объединяет контент и дизайн, что увеличивает размер страницы и загромождает HTML.

Flask-активы

Flask-Assets — это расширение, предназначенное для управления статическими активами в приложении Flask. С его помощью вы создаете простой конвейер активов для:

  1. Компиляция Sass и LESS в таблицы стилей CSS
  2. Объединение и минимизация нескольких файлов CSS и JavaScript в один файл для каждого
  3. Создание пакетов ресурсов для использования в ваших шаблонах

Итак, давайте посмотрим, как работать с каждым из вышеперечисленных инструментов во 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"
>
  1. Вход отправляет запрос POST в /searchконечную точку.
  2. Запрос запускается через событие keyup с задержкой 250 мс. Таким образом, если новое событие нажатия клавиши вводится до истечения 250 мс после последнего нажатия клавиши, запрос не запускается.
  3. Затем HTML-ответ на запрос отображается в #todo-resultsэлементе.
  4. У нас также есть индикатор, элемент загрузки, который появляется после отправки запроса и исчезает после возврата ответа.

Добавьте файл 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 , чтобы проверить его:

демо

Вывод

В этом уроке мы рассмотрели, как:

  • Настройте Flask-Assets, htmx и Tailwind CSS
  • Создайте приложение для поиска в реальном времени, используя Flask, Tailwind CSS и htmx.

htmx может отображать элементы без перезагрузки страницы. Самое главное, вы можете добиться этого без написания JavaScript. Хотя это уменьшает объем работы, необходимой на стороне клиента, данные, отправляемые с сервера, могут быть выше, поскольку он отправляет обработанный HTML.

Подобные частичные HTML-шаблоны были популярны в начале 2000-х годов. htmx предлагает современный подход к этому подходу. В целом, обслуживание частичных шаблонов снова становится популярным из-за сложности таких фреймворков, как React и Vue. Вы также можете добавить WebSockets для внесения изменений в реальном времени. Этот же подход используется в знаменитом Phoenix LiveView . Вы можете узнать больше о HTML поверх WebSockets в статье Будущее веб-программного обеспечения — это HTML поверх WebSockets и HTML поверх WebSockets .

Библиотека еще молода, но будущее у нее очень светлое.

Источник:  https://testdriven.io

#flask #htmx #tailwindcss 

Как настроить Flask с помощью htmx и Tailwind CSS
Shayna  Lowe

Shayna Lowe

1656880980

Comment Configurer Flask Avec htmx et Tailwind CSS

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

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 aet 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-modeterminaison. La réponse est ensuite ajoutée à l' pélément avec une idsortie of.

Pour plus d'exemples, consultez la page Exemples d'interface utilisateur de la documentation officielle htmx.

Avantages et inconvénients

Avantages :

  1. Productivité des développeurs : Vous pouvez créer des interfaces utilisateur modernes sans toucher à JavaScript. Pour en savoir plus, consultez An SPA Alternative .
  2. Emballe un coup de poing : la bibliothèque elle-même est petite (~ 10k min.gz'd), sans dépendance et extensible .

Inconvénients :

  1. Maturité de la bibliothèque : Étant donné que la bibliothèque est assez récente, la documentation et les exemples d'implémentation sont rares.
  2. Taille des données transférées : Typiquement, les frameworks SPA (comme React et Vue) fonctionnent en faisant passer des données entre le client et le serveur au format JSON. Les données reçues sont ensuite restituées par le client. htmx, d'autre part, reçoit le rendu HTML du serveur et remplace l'élément cible par la réponse. Le HTML au format rendu est généralement plus grand en termes de taille qu'une réponse JSON.

CSS vent arrière

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 et inconvénients

Avantages :

  1. Hautement personnalisable : bien que Tailwind soit fourni avec des classes prédéfinies, elles peuvent être écrasées à l'aide du fichier tailwind.config.js .
  2. Optimisation : vous pouvez configurer Tailwind pour optimiser la sortie CSS en ne chargeant que les classes réellement utilisées.
  3. Mode sombre : il est facile d'implémenter le mode sombre - par exemple, <div class="bg-white dark:bg-black">.

Inconvénients :

  1. Composants : Tailwind ne fournit aucun composant pré-construit officiel comme des boutons, des cartes, des barres de navigation, etc. Les composants doivent être créés à partir de zéro. Il existe quelques ressources communautaires pour des composants tels que Tailwind CSS Components et Tailwind Toolbox , pour n'en nommer que quelques-uns. Il existe également une bibliothèque de composants puissante, bien que payante, créée par les créateurs de Tailwind, appelée Tailwind UI .
  2. Le CSS est en ligne : Cela couple contenu et design, ce qui augmente la taille de la page et encombre le HTML.

Flask-Assets

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 :

  1. Compiler Sass et LESS en feuilles de style CSS
  2. Combiner et réduire plusieurs fichiers CSS et JavaScript en un seul fichier pour chacun
  3. Création d'assets bundles à utiliser dans vos modèles

Sur ce, regardons comment travailler avec chacun des outils ci-dessus dans Flask !

Configuration du projet

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 Environmentfichier 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, componentset utilitiesde 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.

Exemple simple

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.pyet 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.

Exemple de recherche en direct

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"
>
  1. L'entrée envoie une demande POST au point de /searchterminaison.
  2. La demande est déclenchée via un événement keyup avec un délai de 250 ms. Ainsi, si un nouvel événement de keyup est entré avant que 250 ms ne se soient écoulés après le dernier keyup, la demande n'est pas déclenchée.
  3. La réponse HTML de la requête est alors affichée dans l' #todo-resultsélément.
  4. Nous avons également un indicateur, un élément de chargement qui apparaît après l'envoi de la requête et disparaît après le retour de la réponse.

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 /searchpoint 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.pyde et accédez à nouveau à http://localhost:5000 pour la tester :

démo

Conclusion

Dans ce tutoriel, nous avons vu comment :

  • Configurer Flask-Assets, htmx et Tailwind CSS
  • Créez une application de recherche en direct à l'aide de Flask, Tailwind CSS et htmx

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

#flask #htmx #tailwindcss 

Comment Configurer Flask Avec htmx et Tailwind CSS

Cómo Configurar Flask Con htmx y Tailwind CSS

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.

htmlx

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 ay 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-modepunto final. Luego, la respuesta se agrega al pelemento con un idde salida.

Para obtener más ejemplos, consulte la página de ejemplos de interfaz de usuario de los documentos oficiales de htmx.

Pros y contras

Ventajas :

  1. Productividad del desarrollador : puede crear interfaces de usuario modernas sin tocar JavaScript. Para obtener más información sobre esto, consulte Una alternativa de SPA .
  2. Tiene un gran impacto: la biblioteca en sí es pequeña (~10k min.gz'd ), libre de dependencias y ampliable .

Contras :

  1. Madurez de la biblioteca: dado que la biblioteca es bastante nueva, la documentación y las implementaciones de ejemplo son escasas.
  2. Tamaño de los datos transferidos : por lo general, los marcos SPA (como React y Vue) funcionan pasando datos entre el cliente y el servidor en formato JSON. Los datos recibidos luego son procesados ​​por el cliente. htmx, por otro lado, recibe el HTML renderizado del servidor y reemplaza el elemento de destino con la respuesta. El HTML en formato renderizado suele ser más grande en términos de tamaño que una respuesta JSON.

CSS viento de cola

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.

Pros y contras

Ventajas :

  1. Altamente personalizable : aunque Tailwind viene con clases prediseñadas, se pueden sobrescribir con el archivo tailwind.config.js .
  2. Optimización : puede configurar Tailwind para optimizar la salida de CSS cargando solo las clases que realmente se utilizan.
  3. Modo oscuro : es fácil implementar el modo oscuro , por ejemplo, <div class="bg-white dark:bg-black">.

Contras :

  1. Componentes : Tailwind no proporciona ningún componente prediseñado oficial como botones, tarjetas, barras de navegación, etc. Los componentes tienen que ser creados desde cero. Hay algunos recursos impulsados ​​por la comunidad para componentes como Tailwind CSS Components y Tailwind Toolbox , por nombrar algunos. También hay una biblioteca de componentes poderosa, aunque paga, de los creadores de Tailwind llamada Tailwind UI .
  2. CSS está en línea : esto combina contenido y diseño, lo que aumenta el tamaño de la página y desordena el HTML.

Flask-Activos

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:

  1. Compilación de hojas de estilo Sass y LESS a CSS
  2. Combinar y minimizar varios archivos CSS y JavaScript en un solo archivo para cada uno
  3. Creación de paquetes de activos para usar en sus plantillas

Con eso, ¡veamos cómo trabajar con cada una de las herramientas anteriores en Flask!

Configuración del proyecto

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 Environmenty 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, componentsy utilitiesde 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.

Ejemplo sencillo

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-100de , 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.pyde 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.

Ejemplo de búsqueda en vivo

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"
>
  1. La entrada envía una solicitud POST al /searchpunto final.
  2. La solicitud se activa a través de un evento keyup con un retraso de 250 ms. Por lo tanto, si se ingresa un nuevo evento de activación antes de que hayan transcurrido 250 ms después de la última activación, la solicitud no se activa.
  3. La respuesta HTML de la solicitud se muestra en el #todo-resultselemento.
  4. También tenemos un indicador, un elemento de carga que aparece después de que se envía la solicitud y desaparece después de que regresa la respuesta.

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 /searchpunto 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.pyy navegue hasta http://localhost:5000 nuevamente para probarla:

manifestación

Conclusión

En este tutorial, vimos cómo:

  • Configurar Flask-Assets, htmx y Tailwind CSS
  • Cree una aplicación de búsqueda en vivo usando Flask, Tailwind CSS y htmx

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

#flask #htmx #tailwindcss 

Cómo Configurar Flask Con htmx y Tailwind CSS

Como Configurar o Flask Com htmx e Tailwind CSS

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

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 ade 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-modeterminal. A resposta é então anexada ao pelemento com uma idsaída.

Para obter mais exemplos, confira a página de exemplos de interface do usuário dos documentos htmx oficiais.

Prós e contras

Prós :

  1. Produtividade do desenvolvedor : você pode criar interfaces de usuário modernas sem tocar em JavaScript. Para saber mais sobre isso, confira An SPA Alternative .
  2. Um soco : A biblioteca em si é pequena (~10k min.gz'd), livre de dependências e extensível .

Contras :

  1. Maturidade da biblioteca: Como a biblioteca é bastante nova, a documentação e as implementações de exemplo são escassas.
  2. Tamanho dos dados transferidos : Normalmente, as estruturas SPA (como React e Vue) funcionam passando dados entre o cliente e o servidor no formato JSON. Os dados recebidos são então processados ​​pelo cliente. htmx, por outro lado, recebe o HTML renderizado do servidor e substitui o elemento de destino pela resposta. O HTML no formato renderizado geralmente é maior em termos de tamanho do que uma resposta JSON.

Tailwind CSS

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 e contras

Prós :

  1. Altamente personalizável : embora o Tailwind venha com classes pré-criadas, elas podem ser substituídas usando o arquivo tailwind.config.js .
  2. Otimização : Você pode configurar o Tailwind para otimizar a saída CSS carregando apenas as classes que são realmente usadas.
  3. Modo escuro : é fácil implementar o modo escuro - por exemplo, <div class="bg-white dark:bg-black">.

Contras :

  1. Componentes : O Tailwind não fornece nenhum componente oficial pré-construído, como botões, cartões, barras de navegação e assim por diante. Os componentes devem ser criados do zero. Existem alguns recursos orientados pela comunidade para componentes como Tailwind CSS Components e Tailwind Toolbox , para citar alguns. Há também uma biblioteca de componentes poderosa, embora paga, dos criadores do Tailwind chamada Tailwind UI .
  2. CSS é inline : combina conteúdo e design, o que aumenta o tamanho da página e desordena o HTML.

Frasco-Ativos

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:

  1. Compilando Sass e LESS para folhas de estilo CSS
  2. Combinando e minificando vários arquivos CSS e JavaScript em um único arquivo para cada
  3. Criando pacotes de ativos para uso em seus modelos

Com isso, vamos ver como trabalhar com cada uma das ferramentas acima no Flask!

Configuração do projeto

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 Environmente 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, componentse utilitiesdo 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.

Exemplo Simples

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.pye 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.

Exemplo de pesquisa ao vivo

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"
>
  1. A entrada envia uma solicitação POST para o /searchterminal.
  2. A solicitação é acionada por meio de um evento keyup com um atraso de 250ms. Portanto, se um novo evento de keyup for inserido antes de decorridos 250ms após a última keyup, a solicitação não será acionada.
  3. A resposta HTML da solicitação é exibida no #todo-resultselemento.
  4. Também temos um indicador, um elemento de carregamento que aparece depois que a solicitação é enviada e desaparece depois que a resposta volta.

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 /searchterminal 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.pye navegue até http://localhost:5000 novamente para testá-lo:

demonstração

Conclusão

Neste tutorial, vimos como:

  • Configurar Flask-Assets, htmx e Tailwind CSS
  • Crie um aplicativo de pesquisa ao vivo usando Flask, Tailwind CSS e htmx

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

#flask #htmx #tailwindcss 

Como Configurar o Flask Com htmx e Tailwind CSS