ComponentsOverviewPage.vala 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. using Spry;
  2. using Inversion;
  3. /**
  4. * ComponentsOverviewPage - Introduction to Spry Components
  5. *
  6. * This page provides a comprehensive overview of what Spry components are,
  7. * their lifecycle, key methods, and basic usage patterns.
  8. */
  9. public class ComponentsOverviewPage : PageComponent {
  10. public override string title { get { return "Components Overview - Spry"; } }
  11. public const string ROUTE = "/components/overview";
  12. private ComponentFactory factory = inject<ComponentFactory>();
  13. public override string markup { get {
  14. return """
  15. <div sid="page" class="doc-content">
  16. <h1>Components Overview</h1>
  17. <section class="doc-section">
  18. <h2>What are Spry Components?</h2>
  19. <p>
  20. Spry components are the building blocks of your web application. They combine
  21. HTML templates with behavior logic in a single, cohesive Vala class. Each component
  22. is self-contained with its own markup, state management, and action handling.
  23. </p>
  24. <p>
  25. Unlike traditional web frameworks where templates and logic are separated,
  26. Spry components embrace a component-based architecture where everything related
  27. to a UI element lives in one place.
  28. </p>
  29. </section>
  30. <section class="doc-section">
  31. <h2>Component Lifecycle</h2>
  32. <p>
  33. Understanding the component lifecycle is crucial for building effective Spry applications.
  34. The template DOM is <strong>fresh on every request</strong>, meaning state is not
  35. persisted between requests.
  36. </p>
  37. <div class="lifecycle-diagram">
  38. <div class="lifecycle-step">
  39. <div class="step-number">1</div>
  40. <div class="step-content">
  41. <h4>Component Creation</h4>
  42. <p>Component is instantiated via ComponentFactory</p>
  43. </div>
  44. </div>
  45. <div class="lifecycle-arrow">↓</div>
  46. <div class="lifecycle-step">
  47. <div class="step-number">2</div>
  48. <div class="step-content">
  49. <h4>prepare_once()</h4>
  50. <p>One-time initialization (called only before first prepare)</p>
  51. </div>
  52. </div>
  53. <div class="lifecycle-arrow">↓</div>
  54. <div class="lifecycle-step">
  55. <div class="step-number">3</div>
  56. <div class="step-content">
  57. <h4>prepare()</h4>
  58. <p>Fetch data from stores, populate template elements</p>
  59. </div>
  60. </div>
  61. <div class="lifecycle-arrow">↓</div>
  62. <div class="lifecycle-step">
  63. <div class="step-number">4</div>
  64. <div class="step-content">
  65. <h4>handle_action()</h4>
  66. <p>Handle user interactions (if action triggered)</p>
  67. </div>
  68. </div>
  69. <div class="lifecycle-arrow">↓</div>
  70. <div class="lifecycle-step">
  71. <div class="step-number">5</div>
  72. <div class="step-content">
  73. <h4>Serialization</h4>
  74. <p>Template is rendered to HTML and sent to client</p>
  75. </div>
  76. </div>
  77. </div>
  78. </section>
  79. <section class="doc-section">
  80. <h2>Key Methods</h2>
  81. <p>
  82. Every component inherits from the base <code>Component</code> class and can
  83. override these key methods:
  84. </p>
  85. <table class="doc-table">
  86. <thead>
  87. <tr>
  88. <th>Method</th>
  89. <th>Description</th>
  90. </tr>
  91. </thead>
  92. <tbody>
  93. <tr>
  94. <td><code>markup</code></td>
  95. <td>Property returning the HTML template string with Spry attributes</td>
  96. </tr>
  97. <tr>
  98. <td><code>prepare()</code></td>
  99. <td>Called before serialization. Fetch data and populate template here.</td>
  100. </tr>
  101. <tr>
  102. <td><code>prepare_once()</code></td>
  103. <td>Called only once before the first prepare(). For one-time initialization.</td>
  104. </tr>
  105. <tr>
  106. <td><code>handle_action()</code></td>
  107. <td>Called when HTMX triggers an action. Modify state here.</td>
  108. </tr>
  109. <tr>
  110. <td><code>continuation()</code></td>
  111. <td>For real-time updates via Server-Sent Events (SSE).</td>
  112. </tr>
  113. </tbody>
  114. </table>
  115. </section>
  116. <section class="doc-section">
  117. <h2>Basic Component Example</h2>
  118. <p>
  119. Here's a simple counter component demonstrating the core concepts:
  120. </p>
  121. <spry-component name="CodeBlockComponent" sid="example-code"/>
  122. </section>
  123. <section class="doc-section">
  124. <h2>Important: Template State is Not Persisted</h2>
  125. <div class="warning-box">
  126. <p>
  127. <strong>⚠️ Critical Concept:</strong> The template DOM is fresh on every request.
  128. Any state set via setters (like <code>title</code>, <code>completed</code>) is
  129. NOT available in <code>handle_action()</code>.
  130. </p>
  131. <p>
  132. Always use <code>prepare()</code> to fetch data from stores and set up the template.
  133. To pass data to actions, use one of these approaches:
  134. </p>
  135. </div>
  136. <div class="info-box">
  137. <h4>Option 1: spry-context (Recommended for Properties)</h4>
  138. <p>
  139. Use <code>&lt;spry-context property="name"/&gt;</code> to automatically preserve
  140. property values across requests.
  141. </p>
  142. <ul>
  143. <li>Properties are encrypted and included in action URLs automatically</li>
  144. <li>Values are restored to properties before <code>handle_action()</code> is called</li>
  145. <li>Example: <code>&lt;spry-context property="item_id"/&gt;</code></li>
  146. </ul>
  147. </div>
  148. <div class="info-box">
  149. <h4>Option 2: hx-vals (Manual Query Parameters)</h4>
  150. <p>
  151. Use the <code>hx-vals</code> attribute to pass data as query parameters.
  152. </p>
  153. <ul>
  154. <li>Values are included in the request as query parameters</li>
  155. <li>Must be read manually via <code>http_context.request.query_params.get_any_or_default("key")</code></li>
  156. <li>Example: <code>hx-vals='{"id": "123"}'</code></li>
  157. </ul>
  158. </div>
  159. </section>
  160. <section class="doc-section">
  161. <h2>Next Steps</h2>
  162. <div class="nav-cards">
  163. <a href="/components/template-syntax" class="nav-card">
  164. <h3>Template Syntax →</h3>
  165. <p>Learn about Spry's special HTML attributes</p>
  166. </a>
  167. <a href="/components/actions" class="nav-card">
  168. <h3>Actions →</h3>
  169. <p>Handle user interactions with actions</p>
  170. </a>
  171. <a href="/components/outlets" class="nav-card">
  172. <h3>Outlets →</h3>
  173. <p>Compose components with outlets</p>
  174. </a>
  175. </div>
  176. </section>
  177. </div>
  178. """;
  179. }}
  180. public override async void prepare() throws Error {
  181. var code = get_component_child<CodeBlockComponent>("example-code");
  182. code.language = "Vala";
  183. code.code = "using Spry;\n\n" +
  184. "public class CounterComponent : Component {\n" +
  185. " private CounterStore store = inject<CounterStore>();\n\n" +
  186. " public override string markup { get {\n" +
  187. " return \"\"\"\n" +
  188. " <div sid=\"counter\" class=\"counter\">\n" +
  189. " <span sid=\"count\"></span>\n" +
  190. " <button sid=\"inc\" spry-action=\":Increment\"\n" +
  191. " spry-target=\"counter\">+</button>\n" +
  192. " <button sid=\"dec\" spry-action=\":Decrement\"\n" +
  193. " spry-target=\"counter\">-</button>\n" +
  194. " </div>\n" +
  195. " \"\"\";\n" +
  196. " }}\n\n" +
  197. " public override void prepare() throws Error {\n" +
  198. " this[\"count\"].text_content = store.count.to_string();\n" +
  199. " }\n\n" +
  200. " public async override void handle_action(string action) throws Error {\n" +
  201. " switch (action) {\n" +
  202. " case \"Increment\":\n" +
  203. " store.count++;\n" +
  204. " break;\n" +
  205. " case \"Decrement\":\n" +
  206. " store.count--;\n" +
  207. " break;\n" +
  208. " }\n" +
  209. " // prepare() is called automatically after handle_action()\n" +
  210. " }\n" +
  211. "}";
  212. }
  213. }