ComponentsOverviewPage.vala 9.1 KB

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