Shranjene vrstice, obrazec, ki doda eno, dejanja na vrsticah, ki zaključijo in izbrišejo, in seznam, ki se po vsakem zapisu znova naloži.
Ta vodnik zgradi upravljalnik opravil z dvema lastnostma na eni strani: seznamom in obrazcem. Opravila so shranjena v brskalniku, zato aplikacija ne potrebuje strežnika.
1entity Task [persistent: tasks, local] {
2 id : string!
3 title : string!
4 done : boolean = false
5 instances [
6 { id: "T-1", title: "Sketch the states", done: true },
7 { id: "T-2", title: "Write the transitions", done: false }
8 ]
9}local hrani vrstice v brskalniku obiskovalca. instances jih napolni, ko se shramba prvič ustvari.
Seznam začne v loading, ob INIT naloži podatke in se premakne v ready, ko vrstice prispejo. Nalaganje poroča prek dveh dogodkov, ki ju lastnost navede v emits:
1state loading {
2 INIT -> loading
3 (fetch Task { emit: { success: TasksLoaded, failure: TasksFailed } })
4 (render-ui main { type: skeleton, variant: list, rows: 3 })
5 TasksLoaded -> ready
6 (render-ui main { type: data-list, entity: ?data, fields: [{ name: "title", label: "Task", variant: h4 }] })
7 TasksFailed -> ready
8 (render-ui main { type: alert, variant: error, message: ?error })
9}itemActions na vsako vrstico postavijo gumbe. Dejanje vrstice pošlje svoj dogodek z { id, row }, when pa za vsako vrstico odloči, ali se gumb pokaže:
▼itemActions: [
{ label: "Complete", event: COMPLETE, variant: ghost, when: (fn row (not (object/get @row done false))) },
{ label: "Delete", event: DELETE, variant: danger }
]1COMPLETE -> loading
2 (persist update Task (object/merge ?row { done: true }))
3 (fetch Task { emit: { success: TasksLoaded, failure: TasksFailed } })
4DELETE -> loading
5 (persist delete Task ?id)
6 (fetch Task { emit: { success: TasksLoaded, failure: TasksFailed } })Druga lastnost pokaže form-section. Oddaja pošlje SAVE z vrednostmi obrazca kot ?data; lastnost vrstico shrani in odda TASK_ADDED:
SAVE -> editing
(persist create Task ?data)
(emit TASK_ADDED)Seznam posluša in se znova naloži:
▼listens {
TaskForm.TASK_ADDED -> RELOAD
}TaskList
on Task · states: 2 · transitions: 6TaskForm
on Task · states: 1 · transitions: 2Zgornji program kot avtomati stanj, eden za vsako lastnost: vsak okvir je stanje, vsaka puščica dogodek, ki ga premakne, ikone na puščici pa so učinki, ki jih ta dogodek izvede. ◇ na puščici je varovalo; z miško pojdite nanj in preberite pogoj. Kako brati AVL
1app std-orb-docex-tasks "1.0.0"
2"std-orb-docex-tasks — a task manager: stored tasks, a form that adds one, row actions that complete or delete, and a list that reloads after every write."
3orbital TasksOrbital {
4 entity Task [persistent: docex_tasks, local] {
5 id : string!
6 title : string!
7 done : boolean = false
8 instances [
9 { id: "T-1", title: "Sketch the states", done: true },
10 { id: "T-2", title: "Write the transitions", done: false },
11 { id: "T-3", title: "Run orb validate", done: false }
12 ]
13 }
14
15 trait TaskList -> Task [interaction, collection] {
16 initial: loading
17
18 state loading {
19 INIT -> loading
20 (fetch Task { emit: { success: TasksLoaded, failure: TasksFailed } })
21 (render-ui main { type: skeleton, variant: list, rows: 3 })
22 TasksLoaded -> ready
23 (emit MOVED { event: "TasksLoaded" })
24 (render-ui main { type: data-list, entity: ?data, fields: [{ name: "title", label: "Task", variant: h4 }, { name: "done", label: "Done", variant: badge, labels: { "true": "Done", "false": "Open" } }], itemActions: [{ label: "Complete", event: COMPLETE, variant: ghost, when: (fn row (not (object/get @row done false))) }, { label: "Delete", event: DELETE, variant: danger }], emptyTitle: "Nothing to do", emptyDescription: "Add a task with the form.", emptyIcon: "check-circle" })
25 TasksFailed -> ready
26 (emit MOVED { event: "TasksFailed" })
27 (render-ui main { type: alert, variant: error, message: ?error })
28 }
29
30 state ready {
31 COMPLETE -> loading
32 (emit MOVED { event: "COMPLETE" })
33 (persist update Task (object/merge ?row { done: true }))
34 (fetch Task { emit: { success: TasksLoaded, failure: TasksFailed } })
35 DELETE -> loading
36 (emit MOVED { event: "DELETE" })
37 (persist delete Task ?id)
38 (fetch Task { emit: { success: TasksLoaded, failure: TasksFailed } })
39 RELOAD -> loading
40 (emit MOVED { event: "RELOAD" })
41 (fetch Task { emit: { success: TasksLoaded, failure: TasksFailed } })
42 }
43
44 emits {
45 TasksLoaded {
46 data : [Task]
47 }
48 TasksFailed {
49 error : string
50 }
51 MOVED -> external {
52 event : string!
53 }
54 @description "Each move names the event it took, so the diagram beside the demo can follow it."
55 }
56
57 listens {
58 TaskForm.TASK_ADDED -> RELOAD
59 COMPLETE {
60 id : Task.id!
61 row : Task!
62 }
63 DELETE {
64 id : Task.id!
65 }
66 }
67 }
68
69 trait TaskForm -> Task [interaction, instance] {
70 initial: editing
71
72 state editing {
73 INIT -> editing
74 (render-ui main { type: form-section, fields: ["title"], submitEvent: SAVE, mode: create, entity: {} })
75 SAVE -> editing
76 (emit MOVED { event: "SAVE" })
77 (persist create Task ?data)
78 (emit TASK_ADDED)
79 (render-ui main { type: form-section, fields: ["title"], submitEvent: SAVE, mode: create, entity: {} })
80 }
81
82 emits {
83 TASK_ADDED
84 @description "A task was stored."
85 MOVED -> external {
86 event : string!
87 }
88 @description "Each move names the event it took, so the diagram beside the demo can follow it."
89 }
90
91 listens {
92 SAVE {
93 data : @entity!
94 }
95 }
96 }
97
98 page "/tasks" -> TaskList, TaskForm
99}Kaj se zgodi, ko se sproži COMPLETE:
(persist "update" "Task" (object/merge @payload.row { done: true }))(fetch "Task" { emit: { failure: "TasksFailed", success: "TasksLoaded" } })Kako se lastnosti pogovarjajo med seboj, po vrsti:
Dodaj opravilo, zaključi eno, izbriši eno. Znova naloži stran: opravila so še vedno tam, ker so shranjena v tvojem brskalniku.
Vzorci vmesnika gredo globlje v to, kaj zaslon lahko pokaže.