Component API
Component와 LiveComponent의 공개 멤버 전부다. 여기 없는 멤버는 밑줄이 없어도 내부이고,
마이너 릴리스에서 바뀔 수 있다(호환성 정책). 사용법은 표의 링크에 있다.
tests/test_public_api.py가 이 표와 클래스를 대조한다. 밑줄 없는 멤버를 새로 만들면 여기 적거나
아래 "내부" 목록에 넣어야 테스트가 통과한다.
이름#
클래스 선언의 키워드 두 개가 컴포넌트의 이름을 정한다. class XCounter(Component)는 클래스명 XCounter로
등록되고 템플릿이 {% component "XCounter" %}, "myapp:XCounter", FQN으로 찾는다. name="..."을 주면
클래스명 대신 그 이름으로 등록된다.
public=False면 등록하지 않는다. 템플릿과 join이 이 클래스를 찾지 못한다. 테스트의 컴포넌트나 공통 베이스가
흔히 이렇게 선언한다. 그래도 로그·계측·서명 상태가 쓸 이름은 있어야 하고, 그 이름은 아래 순서로 정한다.
name="..."을 줬으면 그 이름- 등록된 클래스를 상속했으면 그 클래스의 이름
- 둘 다 아니면 자기 클래스명. 등록되지 않은 베이스(
LiveComponent포함)의 이름은 물려받지 않는다
필드#
| 이름 | 뜻 |
|---|---|
id |
페이지 안에서 고유한 컴포넌트 id |
user |
연결의 사용자 (AnonymousUser 포함) |
session |
Django 세션의 읽기 전용 뷰 (session) |
wire |
내비게이션과 쿼리. 공개는 params, redirect_to, replace_to, push_to뿐이다 (navigation) |
uploads |
업로드 항목. 템플릿의 this.uploads.<name> (external-uploads) |
선언한 필드는 상태다. JSON으로 직렬화되어야 하고 모델 인스턴스는 pk로 서명된다. 설정은 class Meta:에
둔다(README의 Meta 표).
오버라이드하는 것#
wireview가 부르는 콜백이다. get_subscriptions()와 new() 말고는 모두 async def로 쓴다 —
sync로 쓰면 실행되지 않고 manage.py check가 wireview.W002로 알린다.
이 문서의 모든 멤버는 프레임워크의 것이라 오버라이드해도 클라이언트가 이벤트로 부를 수 없다. 클라이언트가 부를 수
있는 것은 사용자 코드가 정의한, _로 시작하지 않는 메서드뿐이다. {% on "click" "joined" %}처럼 그 밖의 이름에
바인딩하면 렌더가 AssertionError로 멈춘다.
| 메서드 | 언제 |
|---|---|
joined() |
소켓에 연결되어 첫 렌더를 보내기 전. 이미 연결된 페이지에서 부모의 렌더가 새로 그린 {% component %}는 부모 템플릿 안에서 인라인으로 그려져 나가고, 페이지가 그 요소를 받아 join할 때 돈다 — 그 join에 답하는 렌더가 joined()가 바꾼 상태를 그린다. LiveComponent는 부모의 렌더가 자기 렌더 전에 부른다 (live-component) |
leaving() |
컴포넌트가 떠날 때 (소켓이 닫힘, 페이지 이동, 부모가 뺌). joined()와 짝이다 — joined()가 돈 인스턴스만 받는다(예외로 끝났어도). 페이지가 join하기 전에 떠난 중첩 {% component %}, 부모의 렌더가 joined()를 부르기 전에 사라진 LiveComponent는 받지 않는다. 그 인스턴스의 비동기 작업은 그래도 취소된다. 그래서 joined()에서 등록하고 여기서 해제하면 짝이 맞는다 |
params_changed(params, uri) |
URL 쿼리가 바뀌었을 때 |
mutation(channel, action, instance) |
구독한 모델이 바뀌었을 때 (AUTO_BROADCAST). instance는 알림에 실려 온 값에서 복원한 것이다 — DB에서 다시 읽지 않고, 관계는 id만 있다. 같은 알림을 받는 컴포넌트마다 따로 복원하므로 고쳐도 다른 컴포넌트가 받는 값은 그대로다. 페이로드에 없는 필드(다중 테이블 상속의 부모 모델 필드, senders 매핑에 적지 않은 필드 등)는 deferred라 읽으면 DB를 조회한다 — async에서는 SynchronousOnlyOperation이므로 await instance.arefresh_from_db(fields=[...])로 불러온다. 저장하면 보통의 저장이다(모델의 save(), raw=False 시그널). 무엇을 쓰는지는 설정의 모델 알림 |
notification(channel, **kwargs) |
구독한 채널로 브로드캐스트가 왔을 때 |
handle_async(name, result) |
start_async()가 끝났을 때. result는 끝난 AsyncResult(ok/failed) (async-operations) |
handle_hook_event(hook_id, event, payload) |
클라이언트 훅이 pushEvent로 보냈을 때 (hooks) |
on_upload_complete(name, entry) |
업로드 항목 하나가 끝났을 때, 다시 렌더하기 전. 청크 업로드는 파일이 서버에 다 있을 때만 불린다 (chunked-uploads, external-uploads) |
get_subscriptions() |
구독 채널을 상태에 따라 정할 때. 기본은 Meta.subscriptions |
new(**kwargs) (classmethod) |
인스턴스를 만들 때. 페이지 렌더, join, 테스트의 mount() 모두 이것을 거친다. wire·user·session과 상태 필드를 키워드로 받고 cls(**kwargs)를 돌려줘야 한다 |
LiveComponent.update(**assigns) |
부모가 새 값을 줄 때 (live-component) |
LiveComponent.update_many(updates) (classmethod) |
부모 렌더 한 번에 값이 바뀐 같은 클래스 자식 전부를 [(component, assigns), ...]로. 기본은 각자의 update() |
부르는 것#
| 메서드 | 하는 일 |
|---|---|
skip_render() |
이번 이벤트의 렌더를 건너뛴다 |
force_render() |
바뀐 것이 없어도 렌더한다 |
await send_render() |
지금 렌더를 보낸다 (긴 작업의 중간 진행률) |
freeze() |
이후 렌더를 보내지 않는다 |
await destroy() |
컴포넌트를 페이지에서 뺀다 |
await broadcast(channel, **kwargs) |
채널로 보낸다. joined() 전이면 모았다가 보낸다 |
await push_event(event, payload=None, hook_id=None) |
클라이언트 훅으로 보낸다 |
await push_js(js) |
JS() 명령을 실행한다. 같은 핸들러의 렌더가 아직 패치되지 않았으면 그 패치 뒤에 돈다 — 렌더로 드러낸 요소를 to=로 겨냥해도 된다 |
await push_title(title) |
문서 제목을 바꾼다 |
await put_flash(flash_type, message, *, timeout=5000, dismissible=True) / await clear_flash(flash_id=None) |
플래시 (flash). clear_flash()는 모두 닫는다. id는 브라우저가 만들어 서버는 모른다 |
await focus_on(selector) |
요소에 포커스 |
await scroll_into_view(element_id, *, behavior="auto", block="start", inline="nearest") |
요소를 보이게 스크롤 |
await defer(f, *args, **kwargs) |
지금 이벤트가 끝난 뒤 f를 부른다. 호출은 클라이언트 이벤트처럼 연결을 한 바퀴 돌아 이름으로 다시 들어오므로, f는 클라이언트가 부를 수 있는 이 컴포넌트의 핸들러여야 한다. _ 헬퍼나 프레임워크 메서드를 넘기면 오류 없이 경고 로그 한 줄만 남기고 버려진다 |
await start_async(name, coro) / await cancel_async(name) |
백그라운드 작업 (async-operations) |
await assign_async(coro, *, on_error=None) |
결과를 AsyncResult 필드로 받는다 |
await stream(name, items, *, template=None, dom_id=None, limit=0) |
스트림을 채우거나 다시 채운다 (Streams API 심화 튜토리얼) |
await stream_insert(name, item, *, at=-1, template=None, dom_id=None, limit=0) / await stream_delete(name, dom_id) |
스트림 항목 |
allow_upload(name, *, accept=None, max_entries=1, max_file_size=None, chunk_size=None, auto_upload=True, external=None) |
업로드를 받는다 (chunked-uploads) |
consume_uploads(name) / await cancel_upload(name, ref) |
완료된 업로드를 꺼낸다 / 취소한다 |
attach_hook(name, stage, callback) / detach_hook(name, stage=None) |
이 인스턴스에 수명주기 훅을 단다 (lifecycle-hooks) |
await send_update(live_component_id, **assigns) |
자식 LiveComponent에 값을 준다 |
await LiveComponent.send_to_parent(event, **kwargs) |
부모의 핸들러를 부른다 |
내부#
밑줄이 없지만 공개가 아닌 것: LiveComponent.myself. Pydantic BaseModel에서 온 멤버
(model_dump 등)는 Pydantic의 것이고 wireview가 약속하지 않는다.