<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: wszgrcy</title>
    <description>The latest articles on DEV Community by wszgrcy (@wszgrcy).</description>
    <link>https://dev.to/wszgrcy</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F112684%2F3eef5078-2356-4af6-9522-8c8b7de2e418.jpeg</url>
      <title>DEV Community: wszgrcy</title>
      <link>https://dev.to/wszgrcy</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/wszgrcy"/>
    <language>en</language>
    <item>
      <title>Piying-View: write forms with a schema, field types inferred end to end</title>
      <dc:creator>wszgrcy</dc:creator>
      <pubDate>Mon, 21 Sep 2026 07:43:37 +0000</pubDate>
      <link>https://dev.to/wszgrcy/piying-view-write-forms-with-a-schema-field-types-inferred-end-to-end-68l</link>
      <guid>https://dev.to/wszgrcy/piying-view-write-forms-with-a-schema-field-types-inferred-end-to-end-68l</guid>
      <description>&lt;h2&gt;
  
  
  What is Piying-View
&lt;/h2&gt;

&lt;p&gt;Piying-View is an open-source, strongly typed TypeScript form library for the frontend.&lt;/p&gt;

&lt;p&gt;You write one &lt;strong&gt;schema&lt;/strong&gt; with &lt;a href="https://valibot.dev/" rel="noopener noreferrer"&gt;Valibot&lt;/a&gt; (what the data looks like, what validation rules it has), and Piying-View turns it into a complete working form:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All controls rendered automatically&lt;/li&gt;
&lt;li&gt;Two-way data binding handled for you&lt;/li&gt;
&lt;li&gt;Validation and error display handled for you&lt;/li&gt;
&lt;li&gt;Automatically reacts to data changes: hide / disable / couple fields&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can use it with &lt;strong&gt;Angular, Vue 3, Vue 2, React, Svelte, Solid&lt;/strong&gt;. The same schema and business logic moves as-is — switching frameworks only means rewriting the registered components.&lt;/p&gt;

&lt;h3&gt;
  
  
  Capabilities at a glance
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capability&lt;/th&gt;
&lt;th&gt;How&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Define the structure&lt;/td&gt;
&lt;td&gt;Valibot schema: &lt;code&gt;object&lt;/code&gt; / &lt;code&gt;array&lt;/code&gt; / &lt;code&gt;record&lt;/code&gt; / &lt;code&gt;tuple&lt;/code&gt; / &lt;code&gt;union&lt;/code&gt; / &lt;code&gt;intersect&lt;/code&gt; / &lt;code&gt;map&lt;/code&gt; / &lt;code&gt;set&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Choose the control&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;setComponent('input')&lt;/code&gt;, or set global defaults with &lt;code&gt;fieldGlobalConfig&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pass props to a component&lt;/td&gt;
&lt;td&gt;&lt;code&gt;inputs&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Handle component events&lt;/td&gt;
&lt;td&gt;&lt;code&gt;outputs&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Two-way binding&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;models&lt;/code&gt; (fully supported in Angular)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Native DOM events&lt;/td&gt;
&lt;td&gt;&lt;code&gt;events&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTML attributes / ARIA / &lt;code&gt;data-*&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;attributes&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Arbitrary custom metadata&lt;/td&gt;
&lt;td&gt;&lt;code&gt;props&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Styling&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;class.top&lt;/code&gt; / &lt;code&gt;class.bottom&lt;/code&gt; / &lt;code&gt;class.component&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Outer wrappers (label, error message, grid)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;wrappers&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conditional hide / disable&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;hideWhen&lt;/code&gt; / &lt;code&gt;disableWhen&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Value coupling&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;valueChange&lt;/code&gt; / &lt;code&gt;outputChange&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Validation&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;formConfig.validators&lt;/code&gt; / &lt;code&gt;asyncValidators&lt;/code&gt;, or write Valibot validation directly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Value transformation&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;formConfig.transformer&lt;/code&gt; (toView / toModel), &lt;code&gt;pipe&lt;/code&gt; (RxJS), &lt;code&gt;v.transform&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Layout adjustment&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;layout({ priority })&lt;/code&gt; for ordering, &lt;code&gt;layout({ keyPath })&lt;/code&gt; to move a field&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Service injection&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;providers&lt;/code&gt; (Angular uses &lt;code&gt;inject()&lt;/code&gt;, other frameworks use static-injector)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lifecycle&lt;/td&gt;
&lt;td&gt;&lt;code&gt;hooks&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Treat a nested object as one control&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;asControl()&lt;/code&gt; / &lt;code&gt;asVirtualGroup()&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JSON Schema&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;jsonSchemaToValibot()&lt;/code&gt; converts it automatically&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Two ways to use it
&lt;/h3&gt;

&lt;p&gt;They can be nested freely:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Automatic mode&lt;/strong&gt;: &lt;code&gt;&amp;lt;piying-view [schema]="..."&amp;gt;&lt;/code&gt; — the library renders the whole component tree for you.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manual mode&lt;/strong&gt;: call &lt;code&gt;convertToField()&lt;/code&gt; yourself to get a &lt;code&gt;field&lt;/code&gt;, then bind it with a directive wherever you want to put it.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  End to end strong typing and code completion
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Latest feature update&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Without type hints
&lt;/h3&gt;

&lt;p&gt;Actions have always worked, but they were loose about "the key you write" and "the field in your callback":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;valibot&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setComponent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@piying/view-angular-core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="nf"&gt;setComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Enter a name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;maxLength&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;patchAsync&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;address&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;city&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt; &lt;span class="c1"&gt;// Does this path even exist? You can't tell from the types&lt;/span&gt;
      &lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;control&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// any&lt;/span&gt;
      &lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// any&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;inputs&lt;/code&gt; is typed as &lt;code&gt;Record&amp;lt;string, any&amp;gt;&lt;/code&gt;: keys are free-form, value types are not checked&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;field&lt;/code&gt; in callbacks is a generic shape: &lt;code&gt;get()&lt;/code&gt; cannot resolve an accurate type, &lt;code&gt;.value&lt;/code&gt; is &lt;code&gt;any&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;A typo like &lt;code&gt;palceholder&lt;/code&gt;, or using a &lt;code&gt;number&lt;/code&gt; where a &lt;code&gt;string&lt;/code&gt; belongs, only shows up at runtime&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  With type hints
&lt;/h3&gt;

&lt;p&gt;Types are not an interface bolted on afterwards — they are inferred all the way down from the schema:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Autocompleted paths&lt;/strong&gt;: your editor completes paths from the schema structure and flags wrong ones&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Typed callbacks&lt;/strong&gt;: &lt;code&gt;field&lt;/code&gt; is the exact field type for that path, not &lt;code&gt;any&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Typed values&lt;/strong&gt;: &lt;code&gt;field.form.control.value&lt;/code&gt; is the output type inferred from the schema; same for &lt;code&gt;parent&lt;/code&gt; / &lt;code&gt;root&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;typedFieldPipe&lt;/code&gt; — attach metadata to a schema by path
&lt;/h3&gt;

&lt;p&gt;Adds actions (metadata) to an already defined schema and returns a new schema. It is written &lt;strong&gt;by path&lt;/strong&gt;, and paths are derived from the schema structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;valibot&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;typedFieldPipe&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@piying/view-core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;city&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="na"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;})),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;merged&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;typedFieldPipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="c1"&gt;// Paths are derived from the schema; the editor flags a wrong one&lt;/span&gt;
  &lt;span class="nf"&gt;d&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;address&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;city&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;patchAsync&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="c1"&gt;// The field in the callback == builder.get(['address', 'city'])&lt;/span&gt;
          &lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;control&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// string&lt;/span&gt;
          &lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// { city: string; zip: string }&lt;/span&gt;
          &lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// { name; age; price; address; tags } — all exact&lt;/span&gt;

          &lt;span class="c1"&gt;// Query other fields and you still get exact types&lt;/span&gt;
          &lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;..&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zip&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;control&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// string&lt;/span&gt;
          &lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;age&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;control&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// number&lt;/span&gt;
          &lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tags&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;count&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;control&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// number&lt;/span&gt;

          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ok&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Coupling is just as precise — whatever fields you listen to, the callback types exactly those positions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;d&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;address&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;city&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;valueChange&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;list&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;..&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zip&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;age&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// [string, string, number] — exact per position, not any&lt;/span&gt;
        &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;listenFields&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// the matching field per position, each with .get() and .form.control&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;hideWhen&lt;/code&gt; / &lt;code&gt;disableWhen&lt;/code&gt; / &lt;code&gt;outputChange&lt;/code&gt; / &lt;code&gt;class.asyncTop&lt;/code&gt; / &lt;code&gt;outputs.mergeAsync&lt;/code&gt; … every action with a &lt;code&gt;field&lt;/code&gt; callback uses the same set of precise types. Official Valibot actions (&lt;code&gt;v.minLength&lt;/code&gt; / &lt;code&gt;v.check&lt;/code&gt; / &lt;code&gt;v.transform&lt;/code&gt;) can be mixed in directly, and their callback parameters stay precise too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Framework agnostic&lt;/strong&gt;: import from &lt;code&gt;@piying/view-angular-core&lt;/code&gt; in Angular, from &lt;code&gt;@piying/view-core&lt;/code&gt; in Vue / React / Svelte / Solid — the syntax is identical.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;typedFieldComponentPipe&lt;/code&gt; — component props are narrowed too
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F37x69yymtt8cueoisgjd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F37x69yymtt8cueoisgjd.png" alt=" " width="799" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;typedFieldPipe&lt;/code&gt; knows nothing about components: the keys of &lt;code&gt;inputs&lt;/code&gt; / &lt;code&gt;outputs&lt;/code&gt; are free-form.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;typedFieldComponentPipe&lt;/code&gt; takes &lt;strong&gt;one extra argument: the "component"&lt;/strong&gt;, and locks the keys and value types of &lt;code&gt;inputs&lt;/code&gt; / &lt;code&gt;outputs&lt;/code&gt; / &lt;code&gt;events&lt;/code&gt; / &lt;code&gt;attributes&lt;/code&gt; to the properties that component actually has:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Autocompletion only lists the props / events the component &lt;strong&gt;really has&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;A wrong prop name or a wrong prop value type is flagged by the editor&lt;/li&gt;
&lt;li&gt;The entry also automatically carries &lt;code&gt;setComponent(component)&lt;/code&gt;, so &lt;strong&gt;the type used for validation = the component actually rendered&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;typedComponent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;typedFieldComponentPipe&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@piying/view-angular&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;typeDefine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;typedComponent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// @Input() placeholder?: string; @Input() precision: number&lt;/span&gt;
    &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AmountInputComponent&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="c1"&gt;// @Output() change: EventEmitter&amp;lt;string[]&amp;gt;&lt;/span&gt;
    &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TagPickerComponent&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;merged&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;typedFieldComponentPipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;typeDefine&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="nf"&gt;d&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;price&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;amount&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;patch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Enter an amount&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;patch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;123&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="c1"&gt;// editor error: wrong value type&lt;/span&gt;
    &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;patch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;palceholder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="c1"&gt;// editor error: no such prop&lt;/span&gt;

    &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;patchAsync&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;precision&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;control&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;]),&lt;/span&gt;

  &lt;span class="nf"&gt;d&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tags&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tags&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;outputs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;change&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;outputChange&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;([{&lt;/span&gt; &lt;span class="na"&gt;list&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;change&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}]))]),&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Usage is the same across frameworks; only "how component props map to inputs / outputs" follows each framework's own component model:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Framework&lt;/th&gt;
&lt;th&gt;Mapping rule&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Angular&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;input()&lt;/code&gt; / &lt;code&gt;output()&lt;/code&gt; / &lt;code&gt;model()&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;The only framework exposing &lt;code&gt;models&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vue 3&lt;/td&gt;
&lt;td&gt;Split from &lt;code&gt;$props&lt;/code&gt;, emit names drop the &lt;code&gt;on&lt;/code&gt; prefix&lt;/td&gt;
&lt;td&gt;Supports &lt;code&gt;markRaw&lt;/code&gt; / lazy loading&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;React&lt;/td&gt;
&lt;td&gt;A function prop is an output, names kept as-is&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Solid&lt;/td&gt;
&lt;td&gt;Same shape as React, built-in keys include &lt;code&gt;classList&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Svelte 5&lt;/td&gt;
&lt;td&gt;A function prop is an output, Snippets are not counted&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In the component slot you can pass a key registered in &lt;code&gt;types&lt;/code&gt;, or a component class directly (including lazy loading). You are not locked out when a component cannot be inferred: &lt;code&gt;inputs&lt;/code&gt; / &lt;code&gt;outputs&lt;/code&gt; fall back to plain key / value — keys are simply no longer validated, everything else keeps working.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;typedFieldPipe&lt;/code&gt; is &lt;strong&gt;general purpose&lt;/strong&gt; and works with any frontend framework;&lt;br&gt;
&lt;code&gt;typedFieldComponentPipe&lt;/code&gt; is &lt;strong&gt;framework specific&lt;/strong&gt;, depending on which framework you use.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;convertToField&lt;/code&gt; — manual conversion is equally precise
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frosgxrohw50cidlk7k2f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frosgxrohw50cidlk7k2f.png" alt=" " width="800" height="342"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every framework provides &lt;code&gt;convertToField&lt;/code&gt;. After converting a schema into a &lt;code&gt;field&lt;/code&gt;, you can still call &lt;code&gt;field.get(xxx)&lt;/code&gt; and get equally accurate types:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;convertToField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;address&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zip&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;control&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// string&lt;/span&gt;
&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tags&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;count&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;control&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// number&lt;/span&gt;
&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;control&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// the output type of the whole schema&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Manual binding — the path is remembered by the type
&lt;/h3&gt;

&lt;p&gt;In manual mode, the path you bind is remembered by the type as well. In Angular that is &lt;code&gt;PiyingFieldControlBindDirective&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;[formControl]=&lt;/span&gt;&lt;span class="s"&gt;"root()"&lt;/span&gt; &lt;span class="na"&gt;[path]=&lt;/span&gt;&lt;span class="s"&gt;"['address', 'city']"&lt;/span&gt; &lt;span class="na"&gt;#city&lt;/span&gt;&lt;span class="err"&gt;="&lt;/span&gt;&lt;span class="na"&gt;formControl&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;city&lt;/code&gt; template reference variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;city&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;field$&lt;/span&gt;&lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// the field type for the path ['address', 'city']&lt;/span&gt;
&lt;span class="nx"&gt;city&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fieldControl$&lt;/span&gt;&lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// its control, .value is string&lt;/span&gt;
&lt;span class="nx"&gt;city&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;summaryList$&lt;/span&gt;&lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// validation error summary&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;[path]&lt;/code&gt; is autocompleted too — it feeds every valid path of the root schema straight to your editor, the very same set you get from &lt;code&gt;typedFieldPipe&lt;/code&gt;. Other frameworks have differently shaped binding directives, with the same capability.&lt;/p&gt;




&lt;h2&gt;
  
  
  Who is responsible for what
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;You write&lt;/th&gt;
&lt;th&gt;What you get&lt;/th&gt;
&lt;th&gt;Scope&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;typedFieldPipe(schema, d =&amp;gt; [...])&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Path autocompletion + precise &lt;code&gt;field&lt;/code&gt; in callbacks + precise &lt;code&gt;field.get()&lt;/code&gt; + precise &lt;code&gt;.value&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;All frameworks&lt;/strong&gt; (general)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;typedFieldComponentPipe(schema, cfg, d =&amp;gt; [...])&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Everything above + &lt;code&gt;inputs&lt;/code&gt; / &lt;code&gt;outputs&lt;/code&gt; / &lt;code&gt;events&lt;/code&gt; / &lt;code&gt;attributes&lt;/code&gt; narrowed to the real component&lt;/td&gt;
&lt;td&gt;One per framework&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;convertToField(...)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The converted &lt;code&gt;field&lt;/code&gt;, with precise &lt;code&gt;get()&lt;/code&gt; / &lt;code&gt;.value&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;One per framework&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;[formControl]&lt;/code&gt; / &lt;code&gt;[path]&lt;/code&gt; manual binding&lt;/td&gt;
&lt;td&gt;The bound path is remembered by the type, template reference variables are strongly typed&lt;/td&gt;
&lt;td&gt;Matching directive per framework&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;p&gt;If you are tired of "renaming one form field means chasing changes through the template, the validation and the types" — this was built for you.&lt;/p&gt;

&lt;p&gt;📖 Docs: &lt;a href="https://piying-org.github.io/piying-view/" rel="noopener noreferrer"&gt;https://piying-org.github.io/piying-view/&lt;/a&gt; (with an online Playground — edit it and run it right there)&lt;/p&gt;

&lt;p&gt;📦 Repo: &lt;a href="https://github.com/piying-org/piying-view" rel="noopener noreferrer"&gt;https://github.com/piying-org/piying-view&lt;/a&gt;&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>opensource</category>
      <category>softwaredevelopment</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Capacitor GeckoView: Say Goodbye to WebView Kernel Fragmentation</title>
      <dc:creator>wszgrcy</dc:creator>
      <pubDate>Sun, 06 Sep 2026 01:25:25 +0000</pubDate>
      <link>https://dev.to/wszgrcy/capacitor-geckoview-say-goodbye-to-webview-kernel-fragmentation-5ce</link>
      <guid>https://dev.to/wszgrcy/capacitor-geckoview-say-goodbye-to-webview-kernel-fragmentation-5ce</guid>
      <description>&lt;h3&gt;
  
  
  Why This Project Exists
&lt;/h3&gt;

&lt;p&gt;Capacitor is a highly popular cross-platform mobile development framework today. Its biggest strength is &lt;strong&gt;"one set of web code, reused across multiple platforms"&lt;/strong&gt; — it runs frontend code through the built-in WebView on Android, allowing developers to deliver native apps using only HTML/JS/CSS.&lt;/p&gt;

&lt;p&gt;However, there is a long-overlooked problem here: &lt;strong&gt;the kernel version of Android's built-in WebView is not unified&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Different vendors, different system versions, and even different models come with vastly different system WebView kernels. Older devices may have WebView stuck at Chrome versions from years ago, while newer devices run the latest versions. This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The new APIs you confidently use during development may directly throw errors on users' older devices.&lt;/li&gt;
&lt;li&gt;To support legacy kernels, you have to write piles of polyfills and stick to outdated syntax.&lt;/li&gt;
&lt;li&gt;"Same code, inconsistent behavior" becomes the norm, and testing costs remain high.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  The Solution: Introducing GeckoView
&lt;/h3&gt;

&lt;p&gt;To make the kernel always controllable and always up‑to‑date, I introduced &lt;strong&gt;GeckoView&lt;/strong&gt; — the browser engine behind Firefox.&lt;/p&gt;

&lt;p&gt;As an independent engine that can be packaged with your app, GeckoView's greatest advantage is that &lt;strong&gt;the engine version is determined by you&lt;/strong&gt;. Whatever kernel version you bundle with the app is what runs on users' devices, completely breaking the dependency on the system WebView.&lt;/p&gt;




&lt;h3&gt;
  
  
  Advantages
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Unified and up‑to‑date kernel version&lt;/strong&gt;: No matter what device or system version the user has, they all run the same Firefox engine, ensuring fully consistent behavior.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No more compatibility worries during development&lt;/strong&gt;: You can safely use the latest HTML/JS/CSS features without writing hacks for old WebViews. The development experience is almost identical to modern web development.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Same usage as vanilla Capacitor&lt;/strong&gt;: Build commands and development workflows are exactly the same as native Capacitor, making the learning curve minimal and migration smooth.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Disadvantages
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Increased package size&lt;/strong&gt;: Bundling a complete browser engine makes the installation package larger than the default solution. However, after minimal trimming, &lt;strong&gt;it is now compressed to about 120MB for the arm64 architecture&lt;/strong&gt; — much smaller than the default 200MB, with further optimization possible.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Plugin Compatibility
&lt;/h3&gt;

&lt;p&gt;Since most Capacitor native plugins are written for the system WebView, some plugins are &lt;strong&gt;not 100% compatible&lt;/strong&gt; with GeckoView. Common reasons include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Plugins rely on WebView‑specific features or interfaces.&lt;/li&gt;
&lt;li&gt;Gradle version mismatches (this project uses Gradle 9, while some plugins are written for Gradle 8 and need manual upgrades).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To address this, I have prepared a &lt;a href="https://github.com/wszgrcy/capacitor-geckoview-plugins/blob/main/docs/webview-to-geckoview-%E8%BF%81%E7%A7%BB%E6%8C%87%E5%8D%97.md" rel="noopener noreferrer"&gt;Migration Guide&lt;/a&gt; to help you quickly adapt existing plugins to run on GeckoView.&lt;/p&gt;




&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Content&lt;/th&gt;
&lt;th&gt;Link&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Main open‑source project&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/wszgrcy/capacitor-geckoview" rel="noopener noreferrer"&gt;https://github.com/wszgrcy/capacitor-geckoview&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Starter template (ready to use)&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/wszgrcy/capacitor-geckoview-starter" rel="noopener noreferrer"&gt;https://github.com/wszgrcy/capacitor-geckoview-starter&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Plugin migration guide&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/wszgrcy/capacitor-geckoview-plugins/blob/main/docs/webview-to-geckoview-%E8%BF%81%E7%A7%BB%E6%8C%87%E5%8D%97.md" rel="noopener noreferrer"&gt;https://github.com/wszgrcy/capacitor-geckoview-plugins/blob/main/docs/webview-to-geckoview-%E8%BF%81%E7%A7%BB%E6%8C%87%E5%8D%97.md&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  Feedback &amp;amp; Discussion
&lt;/h3&gt;

&lt;p&gt;If you encounter any issues while using this project, feel free to send an email to &lt;strong&gt;&lt;a href="mailto:wszgrcy@gmail.com"&gt;wszgrcy@gmail.com&lt;/a&gt;&lt;/strong&gt;. I will do my best to help.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I hope this project helps you break free from WebView kernel fragmentation, so you can focus your energy on building your business logic with peace of mind.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>android</category>
      <category>html</category>
    </item>
    <item>
      <title>Angular Selectorless Support</title>
      <dc:creator>wszgrcy</dc:creator>
      <pubDate>Sun, 19 Jul 2026 13:10:14 +0000</pubDate>
      <link>https://dev.to/wszgrcy/angular-selectorless-support-1gai</link>
      <guid>https://dev.to/wszgrcy/angular-selectorless-support-1gai</guid>
      <description>&lt;h2&gt;
  
  
  Preface
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Recently, while implementing the Angular adapter for opentui, the special mechanism of opentui made it necessary to rewrite the renderer. This led to the discovery that virtual nodes can reduce the rendering of custom component tags. Therefore, I attempted to extract this approach for standalone use in the browser.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Install &lt;code&gt;@cyia/ngx-common/service&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Configuration
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ApplicationConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;provideBrowserGlobalErrorListeners&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;RendererFactory2&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;provideRouter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/router&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;routes&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./app.routes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DomRendererFactory2&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@cyia/ngx-common/service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;appConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ApplicationConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nf"&gt;provideBrowserGlobalErrorListeners&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nf"&gt;provideRouter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="c1"&gt;// Add this configuration&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RendererFactory2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;useClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DomRendererFactory2&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Implementing styles inside components is forbidden, because styles require an outermost element with attributes to define the scope, while selectorless components have no outermost element.&lt;/li&gt;
&lt;li&gt;Elements must be manually marked as selectorless because the implementation operates at the application layer, and element rendering is decoupled from component binding, so manual specification is required.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;selectorlessExcludeTag&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@cyia/ngx-common/service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nf"&gt;selectorlessExcludeTag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app-t1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Directives that bind to and manipulate the DOM cannot be implemented. Normal directive binding still works, but if a directive internally performs operations that involve obtaining an &lt;code&gt;ElementRef&lt;/code&gt;, they cannot be realized.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Source Code
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/wszgrcy/cyia-ngx-common" rel="noopener noreferrer"&gt;cyia-ngx-common&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>angular</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Writing TUI in the Terminal with Angular: Introducing @cyia/opentui-angular</title>
      <dc:creator>wszgrcy</dc:creator>
      <pubDate>Sun, 19 Jul 2026 10:07:26 +0000</pubDate>
      <link>https://dev.to/wszgrcy/writing-tui-in-the-terminal-with-angular-introducing-cyiaopentui-angular-2no9</link>
      <guid>https://dev.to/wszgrcy/writing-tui-in-the-terminal-with-angular-introducing-cyiaopentui-angular-2no9</guid>
      <description>&lt;h2&gt;
  
  
  Foreword
&lt;/h2&gt;

&lt;p&gt;Terminal applications (TUI, Text User Interface) have always had a unique charm — lightweight, fast, and cross-platform. From &lt;code&gt;htop&lt;/code&gt; to &lt;code&gt;vim&lt;/code&gt;, from the interactive wizard of &lt;code&gt;npm init&lt;/code&gt; to database management tools, excellent terminal interfaces can make command-line workflows more efficient.&lt;/p&gt;

&lt;p&gt;However, developing a terminal application usually requires dealing directly with low-level rendering details: calculating character buffers, handling ANSI escape sequences, listening for terminal resize events... These tedious tasks often overshadow the business logic itself.&lt;/p&gt;

&lt;p&gt;Recently, an Angular adapter for the &lt;a href="https://github.com/anomalyco/opentui" rel="noopener noreferrer"&gt;OpenTUI&lt;/a&gt; project has emerged — &lt;code&gt;@cyia/opentui-angular&lt;/code&gt;. It allows developers familiar with Angular to build terminal interfaces using the patterns they already know.&lt;/p&gt;

&lt;p&gt;OpenTUI itself provides a comprehensive component system (&lt;code&gt;text&lt;/code&gt;, &lt;code&gt;box&lt;/code&gt;, &lt;code&gt;input&lt;/code&gt;, &lt;code&gt;scrollbox&lt;/code&gt;, &lt;code&gt;code&lt;/code&gt;, &lt;code&gt;diff&lt;/code&gt;, etc.) and a set of hooks APIs (keyboard events, focus management, paste handling, timeline animations, etc.), but its design is deeply coupled to React — it relies on &lt;code&gt;react-server-dom-flight&lt;/code&gt; to drive the rendering loop.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Positioning of the Angular Adapter
&lt;/h2&gt;

&lt;p&gt;What &lt;code&gt;@cyia/opentui-angular&lt;/code&gt; does is clear: &lt;strong&gt;retain the rendering capabilities of OpenTUI core, while replacing the React rendering engine with Angular's component system&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Specifically, this adapter accomplishes the following:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Custom Platform Layer
&lt;/h3&gt;

&lt;p&gt;Angular's &lt;code&gt;ɵinternalCreateApplication&lt;/code&gt; accepts a &lt;code&gt;platformProviders&lt;/code&gt; parameter, allowing developers full control over the underlying DOM adaptation. The OpenTUI adapter implements a &lt;code&gt;TDomAdapter&lt;/code&gt; — since terminal applications don't need a real browser DOM, this adapter provides no-op implementations or stub functions for most DOM APIs, and takes over &lt;code&gt;RendererFactory2&lt;/code&gt; and &lt;code&gt;Sanitizer&lt;/code&gt;, bridging them to OpenTUI's renderer.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Component Mapping
&lt;/h3&gt;

&lt;p&gt;OpenTUI core exports a large number of Renderable classes (such as &lt;code&gt;BoxRenderable&lt;/code&gt;, &lt;code&gt;TextRenderable&lt;/code&gt;, &lt;code&gt;InputRenderable&lt;/code&gt;, etc.), which are essentially the "HTML tags" of the terminal world. The adapter registers these Renderables as Angular custom elements, and with the &lt;code&gt;NO_ERRORS_SCHEMA&lt;/code&gt; configuration, developers can directly use tags like &lt;code&gt;&amp;lt;box&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;text&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; in their templates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app-root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;schemas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;NO_ERRORS_SCHEMA&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
    &amp;lt;box&amp;gt;
      &amp;lt;text [content]="message()" fg="cyan" /&amp;gt;
    &amp;lt;/box&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;App&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello from Angular + OpenTUI&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Angular-ifying Hooks
&lt;/h3&gt;

&lt;p&gt;OpenTUI's native hooks (like &lt;code&gt;useRenderer&lt;/code&gt;, &lt;code&gt;useKeyboard&lt;/code&gt;) depend on React's context mechanism. The adapter rewrites these hooks to use Angular's &lt;code&gt;inject()&lt;/code&gt;, &lt;code&gt;effect()&lt;/code&gt;, and &lt;code&gt;computed()&lt;/code&gt;, enabling them to function correctly within Angular's dependency injection system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;useKeyboard&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;escape&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Slot System Adaptation
&lt;/h3&gt;

&lt;p&gt;OpenTUI's plugin system allows external components to be inserted into the render tree through a SlotRegistry mechanism. The adapter implements &lt;code&gt;createAngularSlotRegistry&lt;/code&gt;, bridging Angular's &lt;code&gt;TemplateRef&lt;/code&gt;, &lt;code&gt;NgTemplateOutlet&lt;/code&gt;, and &lt;code&gt;ComponentRef&lt;/code&gt; with OpenTUI's slot protocol, so that Angular templates can be rendered into the terminal interface as plugin content.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Keymap Submodule
&lt;/h3&gt;

&lt;p&gt;The adapter also publishes a separate subpath export &lt;code&gt;@cyia/opentui-angular/keymap&lt;/code&gt;, providing an Angular Signal-based key mapping solution. With utility functions like &lt;code&gt;useKeymap&lt;/code&gt;, &lt;code&gt;useBindings&lt;/code&gt;, and &lt;code&gt;useActiveKeys&lt;/code&gt;, developers can define keyboard shortcut bindings declaratively, supporting advanced interaction patterns like leader keys and command completion.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Component Extension Capability
&lt;/h3&gt;

&lt;p&gt;OpenTUI core provides an &lt;code&gt;extend()&lt;/code&gt; API to allow custom Renderables. The adapter retains this capability — you can inherit from existing Renderables (e.g., &lt;code&gt;BoxRenderable&lt;/code&gt;), override the rendering logic, and then register them as new tags for use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ConsoleButtonRenderable&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;BoxRenderable&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nx"&gt;override&lt;/span&gt; &lt;span class="nf"&gt;renderSelf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OptimizedBuffer&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;renderSelf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// Custom button text drawing logic&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;consoleButton&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ConsoleButtonRenderable&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Included Examples
&lt;/h2&gt;

&lt;p&gt;The project itself comes with a fairly rich set of examples covering common TUI scenarios:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Basic Demo&lt;/td&gt;
&lt;td&gt;Login form, focus management, styled text (bold/italic/colors)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Counter Demo&lt;/td&gt;
&lt;td&gt;Timed re-rendering driven by Signal state updates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Animation Demo&lt;/td&gt;
&lt;td&gt;System monitoring animation driven by the Timeline API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Borders / Box&lt;/td&gt;
&lt;td&gt;Border styles and layout components&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scrollbox&lt;/td&gt;
&lt;td&gt;Scrollable content area&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Code / Line Number&lt;/td&gt;
&lt;td&gt;Code highlighting and line number display&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Diff&lt;/td&gt;
&lt;td&gt;Text diff comparison view&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hooks Demo&lt;/td&gt;
&lt;td&gt;Comprehensive demonstration of various hooks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Keymap Demo&lt;/td&gt;
&lt;td&gt;Complete keyboard shortcut system, supports leader keys, command completion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extend Demo&lt;/td&gt;
&lt;td&gt;How to extend custom Renderables&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Flush Sync&lt;/td&gt;
&lt;td&gt;Synchronous flush rendering&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Tech Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Angular 22&lt;/strong&gt; — The latest Angular version, fully adopting Signals and the new control flow syntax&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenTUI core ^0.4.x&lt;/strong&gt; — The underlying TUI rendering engine&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Quick Start
&lt;/h3&gt;

&lt;p&gt;The project provides a GitHub template that can be cloned with a single command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/wszgrcy/ngx-opentui-starter.git
&lt;span class="nb"&gt;cd &lt;/span&gt;ngx-opentui-starter
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Manual Integration
&lt;/h3&gt;

&lt;p&gt;If your project is already based on Angular, after installing the dependencies you only need a small amount of configuration at the application entry point:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;bootstrapApplication&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CliRendererToken&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@cyia/opentui-angular&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createCliRenderer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@opentui/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;renderer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createCliRenderer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;cliRenderer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nf"&gt;bootstrapApplication&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CliRendererToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;useValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cliRenderer&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then remember to add &lt;code&gt;schemas: [NO_ERRORS_SCHEMA]&lt;/code&gt; in your component — this is a necessary configuration for using custom elements like &lt;code&gt;&amp;lt;text&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;box&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who Is It For?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Angular Developers&lt;/strong&gt; — If you want to build terminal applications but don't want to learn React or a completely new framework, this adapter lets you directly apply your existing Angular knowledge.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Node.js Projects Needing a TUI&lt;/strong&gt; — Scenarios like interactive CLI tools, operations dashboards, database management interfaces, etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenTUI Users&lt;/strong&gt; — If you are already using OpenTUI but are more accustomed to Angular's development patterns, this adapter provides a migration path.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;What &lt;code&gt;@cyia/opentui-angular&lt;/code&gt; does is not complicated — it is essentially a bridging layer that exposes the rendering capabilities of OpenTUI core as Angular components and hooks. But it solves a practical problem: &lt;strong&gt;allowing Angular developers to build terminal interfaces in the most natural way&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you are looking for a solution that enables an Angular project to quickly gain TUI capabilities, or if you are simply curious about the idea of "using a virtual DOM in the terminal," this project is worth exploring.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;npm&lt;/strong&gt;: &lt;code&gt;@cyia/opentui-angular&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Source Code&lt;/strong&gt;: &lt;a href="https://github.com/wszgrcy/opentui-angular" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quick Start Template&lt;/strong&gt;: &lt;a href="https://github.com/wszgrcy/ngx-opentui-starter" rel="noopener noreferrer"&gt;ngx-opentui-starter&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>angular</category>
      <category>node</category>
      <category>terminal</category>
      <category>cli</category>
    </item>
    <item>
      <title>ngx-ink: The Angular Version of Ink, A Component-Based Revolution for Terminal UI</title>
      <dc:creator>wszgrcy</dc:creator>
      <pubDate>Sun, 28 Jun 2026 00:53:11 +0000</pubDate>
      <link>https://dev.to/wszgrcy/ngx-ink-the-angular-version-of-ink-a-component-based-revolution-for-terminal-ui-1k8i</link>
      <guid>https://dev.to/wszgrcy/ngx-ink-the-angular-version-of-ink-a-component-based-revolution-for-terminal-ui-1k8i</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Ink is already the most mature terminal UI framework in the React ecosystem. Now, Angular developers can enjoy the same power.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Background: Ink Has Proven This Path Works
&lt;/h2&gt;

&lt;p&gt;In the React ecosystem, &lt;a href="https://github.com/vadimdemedes/ink" rel="noopener noreferrer"&gt;Ink&lt;/a&gt; has successfully validated the feasibility of "building CLI interfaces with components." It is adopted by numerous well-known projects such as &lt;a href="https://github.com/anthropics/claude-code" rel="noopener noreferrer"&gt;Claude Code&lt;/a&gt;, &lt;a href="https://github.com/features/copilot/cli" rel="noopener noreferrer"&gt;GitHub Copilot CLI&lt;/a&gt;, &lt;a href="https://github.com/cloudflare/wrangler2" rel="noopener noreferrer"&gt;Cloudflare Wrangler&lt;/a&gt;, and &lt;a href="https://www.prisma.io" rel="noopener noreferrer"&gt;Prisma&lt;/a&gt;, boasting a mature API design and rich ecosystem.&lt;/p&gt;

&lt;p&gt;But there's one problem: &lt;strong&gt;Angular developers are left out.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ink is built on React, relying on React's Hooks, JSX, and functional component model. For Angular teams, this means either learning an entirely new programming paradigm or giving up the ability to enjoy componentized UI in the terminal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ngx-ink was born to solve this problem.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What is ngx-ink?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;ngx-ink&lt;/strong&gt; is the &lt;strong&gt;complete Angular migration of &lt;a href="https://github.com/vadimdemedes/ink" rel="noopener noreferrer"&gt;Ink&lt;/a&gt;&lt;/strong&gt;. It reuses Ink's core logic—including ANSI processing, Yoga Flexbox layout calculation, and rendering pipeline—but transforms the entire programming model from React to Angular.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Core Promise: API equivalence, seamless experience transition.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you know how to use it in Ink, you know how to use it in ngx-ink. The only difference is that the syntax changes from React-style to Angular-style.&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Features
&lt;/h3&gt;

&lt;h4&gt;
  
  
  🎨 Declarative Componentized UI
&lt;/h4&gt;

&lt;p&gt;Say goodbye to string concatenation. Use familiar Angular component syntax to build your interface in the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app-root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
    &amp;lt;box [style]="{ flexDirection: 'column', padding: 1 }"&amp;gt;
      &amp;lt;text [color]="'cyan'" [bold]="true"&amp;gt;🚀 My CLI Tool&amp;lt;/text&amp;gt;
      &amp;lt;text [color]="'green'"&amp;gt;Status: Running&amp;lt;/text&amp;gt;
      &amp;lt;text [color]="'yellow'"&amp;gt;Progress: {{ progress() }}%&amp;lt;/text&amp;gt;
    &amp;lt;/box&amp;gt;
  `&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppComponent&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  📐 Flexbox Terminal Layout
&lt;/h4&gt;

&lt;p&gt;Powered by Facebook's &lt;a href="https://github.com/facebook/yoga" rel="noopener noreferrer"&gt;Yoga&lt;/a&gt; engine, ngx-ink implements full Flexbox layout capabilities in the terminal. &lt;code&gt;flexDirection&lt;/code&gt;, &lt;code&gt;justifyContent&lt;/code&gt;, &lt;code&gt;alignItems&lt;/code&gt;, &lt;code&gt;margin&lt;/code&gt;, &lt;code&gt;padding&lt;/code&gt;—these properties you take for granted in web development are now all available in the terminal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;box&lt;/span&gt; &lt;span class="na"&gt;[style]=&lt;/span&gt;&lt;span class="s"&gt;"{ flexDirection: 'row', justifyContent: 'space-between' }"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;text&amp;gt;&lt;/span&gt;Left Item&lt;span class="nt"&gt;&amp;lt;/text&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;spacer&amp;gt;&amp;lt;/spacer&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;text&lt;/span&gt; &lt;span class="na"&gt;[color]=&lt;/span&gt;&lt;span class="s"&gt;"'green'"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Right Item ✓&lt;span class="nt"&gt;&amp;lt;/text&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/box&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  🎬 Responsive Real-time Updates
&lt;/h4&gt;

&lt;p&gt;Leveraging Angular Signals' reactivity, the terminal interface automatically refreshes as data changes, without manually calling update methods. Paired with the &lt;code&gt;useAnimation&lt;/code&gt; hook, easily implement spinner animations, progress bar scrolling, and other effects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;animation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useAnimation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;

&lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;spinner&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chars&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;⠋&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;⠙&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;⠹&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;⠸&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;⠼&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;⠴&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;⠦&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;⠧&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;⠇&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;⠏&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;chars&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;frame&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nx"&gt;chars&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  ⌨️ Complete Interaction Capabilities
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Keyboard Input Listening&lt;/strong&gt; — &lt;code&gt;useInput&lt;/code&gt; hook supports arrow keys, key combinations, and full key mapping&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Focus Management&lt;/strong&gt; — &lt;code&gt;useFocus&lt;/code&gt; enables Tab-based focus switching between components&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Window Size Response&lt;/strong&gt; — &lt;code&gt;useWindowSize&lt;/code&gt; automatically detects terminal size changes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Standard Stream Access&lt;/strong&gt; — &lt;code&gt;useStdin&lt;/code&gt; / &lt;code&gt;useStdout&lt;/code&gt; / &lt;code&gt;useStderr&lt;/code&gt; directly manipulate I/O streams&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  🎭 Rich Built-in Components
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BoxComponent&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Flexbox layout container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TextComponent&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Text display with style support (colors, bold, italic, underline, etc.)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NewlineComponent&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Line break control&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SpacerComponent&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Flexible blank space filling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;StaticComponent&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Static content rendering (logs, completed tasks, etc.)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TransformComponent&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Text transformation processing&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  🎨 Complete ANSI Color Support
&lt;/h4&gt;

&lt;p&gt;Built on Chalk, supporting all ANSI color names, as well as background colors, bold, italic, underline, strikethrough, reverse video, and other rich text styles. One line of code makes your terminal output delightful:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;text&lt;/span&gt; &lt;span class="na"&gt;[color]=&lt;/span&gt;&lt;span class="s"&gt;"'green'"&lt;/span&gt; &lt;span class="na"&gt;[bold]=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt; &lt;span class="na"&gt;[underline]=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  ✨ Success! All tests passed.
&lt;span class="nt"&gt;&amp;lt;/text&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  ♿ Accessibility Support
&lt;/h4&gt;

&lt;p&gt;Built-in screen reader detection and ARIA attribute support ensure your CLI tool is friendly to everyone.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Choose ngx-ink?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Full Capabilities of Ink, Expressed in Angular
&lt;/h3&gt;

&lt;p&gt;ngx-ink is not reinventing the wheel—it brings Ink's proven capabilities to Angular developers. Whatever features Ink has, ngx-ink has too:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Flexbox terminal layout (Yoga engine)&lt;/li&gt;
&lt;li&gt;✅ Complete ANSI colors and text styles&lt;/li&gt;
&lt;li&gt;✅ Responsive real-time updates&lt;/li&gt;
&lt;li&gt;✅ Keyboard input, focus management, animation-driven&lt;/li&gt;
&lt;li&gt;✅ Static rendering, Transform text transformation&lt;/li&gt;
&lt;li&gt;✅ Screen reader support and ARIA accessibility&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Whatever Ink can do, ngx-ink can do too. The only difference is that you're writing Angular code.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. React Hooks → Angular Hooks, Zero Mental Shift
&lt;/h3&gt;

&lt;p&gt;Developers familiar with Ink already know React Hooks like &lt;code&gt;useState&lt;/code&gt;, &lt;code&gt;useEffect&lt;/code&gt;, and &lt;code&gt;useInput&lt;/code&gt;. In ngx-ink, these concepts are fully preserved and adapted to Angular style:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Ink (React)&lt;/th&gt;
&lt;th&gt;ngx-ink (Angular)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;useState&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;signal()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;useEffect&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;effect()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;useInput&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;useInput()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;useWindowSize&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;useWindowSize()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;useAnimation&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;useAnimation()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JSX Component&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;@Component&lt;/code&gt; decorator&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Props&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;input()&lt;/code&gt; / &lt;code&gt;output()&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;API signatures are consistent, behaviors are consistent—you just need to convert the syntax.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Based on Ink 7.1.0, Production-Grade Reliability
&lt;/h3&gt;

&lt;p&gt;ngx-ink reuses Ink's core logic layer—ANSI processing, Yoga layout calculation, and rendering pipeline all come from Ink 7.1.0. This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Proven at Scale&lt;/strong&gt; — Ink is adopted by projects like Claude Code, GitHub Copilot CLI, and Cloudflare Wrangler&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mature Edge Case Handling&lt;/strong&gt; — Terminal resize, special characters, ANSI escape sequences, and other edge cases have been thoroughly addressed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Continuous Evolution&lt;/strong&gt; — Improvements in the Ink ecosystem naturally reflect in ngx-ink&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ngx-ink handles the Angular adaptation layer, with core rendering capabilities originating from Ink.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Native Integration with the Angular Ecosystem
&lt;/h3&gt;

&lt;p&gt;As an Angular library, ngx-ink seamlessly integrates into your Angular projects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Start using the standard &lt;code&gt;bootstrapApplication&lt;/code&gt; approach&lt;/li&gt;
&lt;li&gt;Supports Angular routing, forms, HTTP client&lt;/li&gt;
&lt;li&gt;Full dependency injection and Token mechanisms&lt;/li&gt;
&lt;li&gt;TypeScript type safety with compile-time checking&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Quick Start
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @cyia/ngx-ink
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Using the Starter Template (Recommended)
&lt;/h3&gt;

&lt;p&gt;The fastest way to get started is using the &lt;a href="https://github.com/wszgrcy/ngx-ink-starter" rel="noopener noreferrer"&gt;ngx-ink-starter&lt;/a&gt; template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/wszgrcy/ngx-ink-starter.git
&lt;span class="nb"&gt;cd &lt;/span&gt;ngx-ink-starter
npm &lt;span class="nb"&gt;install
&lt;/span&gt;npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it—your terminal app is up and running, with no build configuration required.&lt;/p&gt;

&lt;h3&gt;
  
  
  Write a Counter by Hand
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;effect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;BoxComponent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TextComponent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@cyia/ngx-ink&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app-root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;standalone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;BoxComponent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TextComponent&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
    &amp;lt;box [style]="{ padding: 2 }"&amp;gt;
      &amp;lt;text [color]="'cyan'" [bold]="true"&amp;gt;Counter: {{ counter() }}&amp;lt;/text&amp;gt;
      &amp;lt;text [color]="'green'"&amp;gt;Press any key to increment&amp;lt;/text&amp;gt;
    &amp;lt;/box&amp;gt;
  `&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;useInput&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Who Needs ngx-ink?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Angular Developers&lt;/strong&gt; — Want to build interactive CLIs in the terminal without learning React&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ink Users&lt;/strong&gt; — Projects requiring Angular tech stack, hoping to reuse Ink's API capabilities&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-Framework Teams&lt;/strong&gt; — Teams using both React and Angular, wanting CLI tools to maintain a unified component-based development experience&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Terminal Tool Enthusiasts&lt;/strong&gt; — Want to build beautiful command-line interfaces in a familiar way&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Project Information
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;npm Package&lt;/strong&gt;: &lt;code&gt;@cyia/ngx-ink&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/wszgrcy/ngx-ink" rel="noopener noreferrer"&gt;ngx-ink&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Starter Template&lt;/strong&gt;: &lt;a href="https://github.com/wszgrcy/ngx-ink-starter" rel="noopener noreferrer"&gt;ngx-ink-starter&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Angular Version&lt;/strong&gt;: Supports Angular 22+&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;License&lt;/strong&gt;: MIT&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Ink has proven the feasibility of building CLI interfaces with components. Now, &lt;strong&gt;ngx-ink&lt;/strong&gt; brings this capability to Angular developers.&lt;/p&gt;

&lt;p&gt;No need to learn new paradigms, no need to abandon familiar tools. &lt;strong&gt;What Ink has, ngx-ink has; what Angular excels at, ngx-ink excels at too.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ngx-ink&lt;/strong&gt; — The Angular form of Ink.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 If you like this project, feel free to Star and Fork, and we welcome Issues and PRs to help improve it together!&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>angular</category>
      <category>node</category>
      <category>terminal</category>
      <category>cli</category>
    </item>
    <item>
      <title>A Next-Generation Alternative to ngx-formly: piying-view</title>
      <dc:creator>wszgrcy</dc:creator>
      <pubDate>Tue, 28 Oct 2025 14:08:44 +0000</pubDate>
      <link>https://dev.to/wszgrcy/a-next-generation-alternative-to-ngx-formly-piying-view-3lo5</link>
      <guid>https://dev.to/wszgrcy/a-next-generation-alternative-to-ngx-formly-piying-view-3lo5</guid>
      <description>&lt;ul&gt;
&lt;li&gt;If you're using &lt;a href="https://formly.dev" rel="noopener noreferrer"&gt;ngx-formly&lt;/a&gt;, or looking for a more flexible, modern, and extensible form solution, then &lt;a href="https://piying-org.github.io/website/docs/client/intro" rel="noopener noreferrer"&gt;piying-view&lt;/a&gt; might be the next-generation choice worth trying.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  📌 Background
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ngx-formly&lt;/code&gt; is an excellent form abstraction library.&lt;/li&gt;
&lt;li&gt;However, as project complexity increases, I found limitations in certain areas. Therefore, inspired by the core philosophy of &lt;a href="https://formly.dev" rel="noopener noreferrer"&gt;ngx-formly&lt;/a&gt;, I've made several improvements and added more features, resulting in the creation of &lt;a href="https://piying-org.github.io/website/docs/client/intro" rel="noopener noreferrer"&gt;piying-view&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🚀 Improvements
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Reset
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ngx-formly&lt;/code&gt; only supports global reset. To reset individual form controls, you need complex custom implementations.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;piying-view&lt;/code&gt; allows any form control to be reset simply by calling its &lt;code&gt;reset&lt;/code&gt; method, including all its child controls (if any).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Input Properties
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ngx-formly&lt;/code&gt; only supports setting properties via custom &lt;code&gt;props&lt;/code&gt;, and does not support Angular’s built-in input properties.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;piying-view&lt;/code&gt; supports both &lt;code&gt;props&lt;/code&gt; and native Angular input properties (&lt;a href="https://angular.dev/api/core/input" rel="noopener noreferrer"&gt;&lt;code&gt;@Input&lt;/code&gt;&lt;/a&gt;), meaning existing components can be used directly as form controls.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Form Binding
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ngx-formly&lt;/code&gt; requires inheritance from &lt;code&gt;FieldType&lt;/code&gt;, making it incompatible with direct use of &lt;a href="https://angular.dev/api/forms/ControlValueAccessor" rel="noopener noreferrer"&gt;custom form controls&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;piying-view&lt;/code&gt; allows existing &lt;a href="https://angular.dev/api/forms/ControlValueAccessor" rel="noopener noreferrer"&gt;custom form controls&lt;/a&gt; to be used directly without modification.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Component References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ngx-formly&lt;/code&gt; only supports synchronous component loading.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;piying-view&lt;/code&gt; supports both synchronous and asynchronous component loading.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Query
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ngx-formly&lt;/code&gt; only supports querying child controls, not direct parent or root-level queries.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;piying-view&lt;/code&gt; supports querying children, parents (&lt;code&gt;..&lt;/code&gt;), root (&lt;code&gt;#&lt;/code&gt;), and named controls (&lt;code&gt;@xxx&lt;/code&gt;) via &lt;a href="https://piying-org.github.io/website/docs/client/path-query" rel="noopener noreferrer"&gt;path query&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Validators
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ngx-formly&lt;/code&gt; offers limited built-in validators, but supports custom validators.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;piying-view&lt;/code&gt; supports all &lt;a href="https://piying-org.github.io/website/docs/client/validator" rel="noopener noreferrer"&gt;validators&lt;/a&gt; from &lt;code&gt;valibot&lt;/code&gt;, includes i18n support, and also allows custom validators.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Layout
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ngx-formly&lt;/code&gt; uses the defined position as layout.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;piying-view&lt;/code&gt; fixes the position during definition, but allows dynamic layout adjustments using &lt;a href="https://piying-org.github.io/website/docs/client/action#layout" rel="noopener noreferrer"&gt;layout actions&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Input Listening
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ngx-formly&lt;/code&gt; requires dynamic property updates to be implemented in &lt;code&gt;ngOnInit&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;piying-view&lt;/code&gt; accepts &lt;code&gt;Signal&lt;/code&gt;, &lt;code&gt;Observable&lt;/code&gt;, or &lt;code&gt;Promise&lt;/code&gt; as input parameters, enabling real-time updates.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Group Disabling
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ngx-formly&lt;/code&gt; disables all controls within a group if any control in the same group is disabled.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;piying-view&lt;/code&gt; allows disabling only specific logical groups within a single group.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🔥 New Features
&lt;/h2&gt;

&lt;h3&gt;
  
  
  🎯 Directive Support
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Supports applying Angular directives directly to components.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🧩 Selectorless Mode
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Supports selectorless components — created components do not require a &lt;code&gt;selector&lt;/code&gt; definition in the template.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🔤 Strong Typing
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Definitions can be directly converted into TypeScript types, enhancing development experience and type safety.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🛠️ Custom Actions
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Allows encapsulating functionality into reusable &lt;a href="https://piying-org.github.io/website/docs/client/action" rel="noopener noreferrer"&gt;Actions&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🌐 Multi-Platform Support
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Supports not only &lt;code&gt;Angular&lt;/code&gt;, but also provides adapter layers for &lt;code&gt;Vue&lt;/code&gt;, &lt;code&gt;React&lt;/code&gt;, &lt;code&gt;Svelte&lt;/code&gt;, and &lt;code&gt;Solid&lt;/code&gt;, ideal for cross-framework projects or future migration.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🔁 Bidirectional Data Transformation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Supports &lt;code&gt;pre-processing&lt;/code&gt; (e.g., formatting) of input data and &lt;code&gt;post-processing&lt;/code&gt; (e.g., serialization) of output data — meeting complex business requirements. &lt;a href="https://piying-org.github.io/website/docs/client/form" rel="noopener noreferrer"&gt;Learn more&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  📊 Feature Comparison
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;😀 - Fully supported 🤢 - Partially supported 🥵 - Not supported&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;&lt;a href="https://piying-org.github.io/website/docs/client/intro" rel="noopener noreferrer"&gt;piying-view&lt;/a&gt;&lt;/th&gt;
&lt;th&gt;&lt;a href="https://formly.dev/" rel="noopener noreferrer"&gt;ngx-formly&lt;/a&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Wrapper&lt;/td&gt;
&lt;td&gt;😀&lt;/td&gt;
&lt;td&gt;😀&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Control Group&lt;/td&gt;
&lt;td&gt;😀&lt;/td&gt;
&lt;td&gt;😀&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Controls&lt;/td&gt;
&lt;td&gt;😀&lt;/td&gt;
&lt;td&gt;😀&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JSON Schema&lt;/td&gt;
&lt;td&gt;😀&lt;/td&gt;
&lt;td&gt;😀&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Validators&lt;/td&gt;
&lt;td&gt;😀&lt;/td&gt;
&lt;td&gt;😀&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Component Layout&lt;/td&gt;
&lt;td&gt;😀&lt;/td&gt;
&lt;td&gt;😀&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Settings Class&lt;/td&gt;
&lt;td&gt;😀&lt;/td&gt;
&lt;td&gt;😀&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Plugins&lt;/td&gt;
&lt;td&gt;😀&lt;/td&gt;
&lt;td&gt;😀&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Form Reset&lt;/td&gt;
&lt;td&gt;😀&lt;/td&gt;
&lt;td&gt;🤢&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Form Query&lt;/td&gt;
&lt;td&gt;😀&lt;/td&gt;
&lt;td&gt;🤢&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Input Properties&lt;/td&gt;
&lt;td&gt;😀&lt;/td&gt;
&lt;td&gt;🤢&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data Transformation&lt;/td&gt;
&lt;td&gt;😀&lt;/td&gt;
&lt;td&gt;🤢&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Async Components&lt;/td&gt;
&lt;td&gt;😀&lt;/td&gt;
&lt;td&gt;🥵&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Group Disabling&lt;/td&gt;
&lt;td&gt;😀&lt;/td&gt;
&lt;td&gt;🥵&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Decorators&lt;/td&gt;
&lt;td&gt;😀&lt;/td&gt;
&lt;td&gt;🥵&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Selectorless&lt;/td&gt;
&lt;td&gt;😀&lt;/td&gt;
&lt;td&gt;🥵&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-Platform&lt;/td&gt;
&lt;td&gt;😀&lt;/td&gt;
&lt;td&gt;🥵&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Strong Typing&lt;/td&gt;
&lt;td&gt;😀&lt;/td&gt;
&lt;td&gt;🥵&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pre-built Component Library&lt;/td&gt;
&lt;td&gt;🥵&lt;/td&gt;
&lt;td&gt;😀&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  🌟 Final Thoughts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://piying-org.github.io/website/docs/client/quick-start" rel="noopener noreferrer"&gt;Get Started Quickly&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;If you're interested in &lt;a href="https://piying-org.github.io/website/docs/client/intro" rel="noopener noreferrer"&gt;piying-view&lt;/a&gt;, feel free to visit the &lt;a href="https://piying-org.github.io/website/docs/client/intro" rel="noopener noreferrer"&gt;👉 Official Website&lt;/a&gt; for more documentation and examples.&lt;/li&gt;
&lt;li&gt;Have feedback or suggestions? We welcome your &lt;a href="https://github.com/piying-org/piying-view/issues" rel="noopener noreferrer"&gt;📩 contributions&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/piying-org/piying-view" rel="noopener noreferrer"&gt;📦 Source Code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>angular</category>
      <category>webdev</category>
      <category>programming</category>
      <category>typescript</category>
    </item>
    <item>
      <title>UnOfficial Implementation of Angular's Selectorless Components</title>
      <dc:creator>wszgrcy</dc:creator>
      <pubDate>Sat, 18 Oct 2025 03:17:58 +0000</pubDate>
      <link>https://dev.to/wszgrcy/unofficial-implementation-of-angulars-selectorless-components-1o2i</link>
      <guid>https://dev.to/wszgrcy/unofficial-implementation-of-angulars-selectorless-components-1o2i</guid>
      <description>&lt;ul&gt;
&lt;li&gt;As is well known, when defining components in Angular, a custom tag must be generated. In some cases, this can be inconvenient when using CSS layout.&lt;/li&gt;
&lt;li&gt;Although the official team has begun to consider implementing selectorless components, it is still in the planning stage, and it's uncertain how long it will take to be realized.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Principle
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;As is well known, structural directives can dynamically insert templates.&lt;/li&gt;
&lt;li&gt;Template content can be customized and can also use all properties and methods within the component.&lt;/li&gt;
&lt;li&gt;Therefore, it's sufficient to turn the component into a template to achieve this.&lt;/li&gt;
&lt;li&gt;The method is also simple, just wrap a &lt;code&gt;ng-template&lt;/code&gt; around the component's html&lt;/li&gt;
&lt;li&gt;Now that we have the template, we only need to consider how to use it.&lt;/li&gt;
&lt;li&gt;This is also simple: dynamically create a component, which returns the component instance. Through the instance, we can get the template reference.&lt;/li&gt;
&lt;li&gt;Since we have the template reference, we can use &lt;code&gt;viewContainerRef&lt;/code&gt; to insert it.&lt;/li&gt;
&lt;li&gt;As the inserted content is a template, the component is only created but not actually inserted into the DOM with a selector.&lt;/li&gt;
&lt;li&gt;This achieves the selectorless component.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The following code is only for demonstrating the principle. For more detailed information, please refer to the &lt;a href="https://github.com/wszgrcy/cyia-ngx-common/blob/master/lib/src/directive/selectorless-outlet.ts" rel="noopener noreferrer"&gt;repository&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Directive
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Create component reference, existing only in memory&lt;/span&gt;
&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_componentRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;selectlessOutlet&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;
&lt;span class="c1"&gt;// Get the instantiated component, note that the component needs a templateRef to save the template reference&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;instance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_componentRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;templateRef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Signal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TemplateRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="c1"&gt;// Insert the template in a structural directive (similar to ngIf ngFor)&lt;/span&gt;
&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;viewContainerRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createEmbeddedView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;templateRef&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="c1"&gt;// Bind lifecycle&lt;/span&gt;
&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;appRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attachView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_componentRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hostView&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_componentRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;changeDetectorRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detectChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Selectorless component part
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ng-template&lt;/span&gt; &lt;span class="na"&gt;#templateRef&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; 123 &lt;span class="nt"&gt;&amp;lt;/ng-template&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;InsertComponent&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;OnInit&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;templateRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;viewChild&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;required&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;templateRef&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Usage part
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ng-container&lt;/span&gt; &lt;span class="na"&gt;*selectlessOutlet=&lt;/span&gt;&lt;span class="s"&gt;"InsertComponent;inputs:{}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;/ng-container&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Of course, this method has some limitations.&lt;/li&gt;
&lt;li&gt;The implementation of &lt;code&gt;ng-content&lt;/code&gt; will be very complex; it needs to use &lt;code&gt;ng-template&lt;/code&gt; references to replace input variables.&lt;/li&gt;
&lt;li&gt;Some directives may be restricted, because components implemented in this way are essentially a shell and cannot access the real element.
&amp;gt; Because at this point, the &lt;code&gt;ElementRef&lt;/code&gt; gets an element that is not in the DOM.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Faster Usage?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://piying-org.github.io/website/docs/client/intro" rel="noopener noreferrer"&gt;Piying View&lt;/a&gt; has already supported custom selectorless component definitions and can switch them conveniently.
&amp;gt; Piying can be considered as the Pro plus version of &lt;code&gt;ngx-formly&lt;/code&gt;, with similar concepts to &lt;code&gt;ngx-formly&lt;/code&gt;, but with clearer logic, safer, supports strong typing, multi-framework, and more features.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final Words
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;If you have other questions, feel free to contact via email &lt;code&gt;wszgrcy@gmail.com&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/wszgrcy/cyia-ngx-common" rel="noopener noreferrer"&gt;Selectorless code repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/piying-org/piying-view" rel="noopener noreferrer"&gt;Piying View code repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.bilibili.com/video/BV1bXW7z5EZn/?vd_source=e12a10b2c48383fa34d7ec0e25a880c7" rel="noopener noreferrer"&gt;Demo video&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>angular</category>
      <category>frontend</category>
      <category>javascript</category>
      <category>typescript</category>
    </item>
    <item>
      <title>I've open-sourced a form library — define metadata once, and it can be rendered anywhere.</title>
      <dc:creator>wszgrcy</dc:creator>
      <pubDate>Fri, 08 Aug 2025 04:14:04 +0000</pubDate>
      <link>https://dev.to/wszgrcy/ive-open-sourced-a-form-library-define-metadata-once-and-it-can-be-rendered-anywhere-15bo</link>
      <guid>https://dev.to/wszgrcy/ive-open-sourced-a-form-library-define-metadata-once-and-it-can-be-rendered-anywhere-15bo</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwd68dlj0opglsw7gzo3b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwd68dlj0opglsw7gzo3b.png" alt=" " width="800" height="767"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;After reviewing all major form libraries on the market, I identified a critical flaw: they require &lt;strong&gt;repeated definitions&lt;/strong&gt; to build a form.&lt;/li&gt;
&lt;li&gt;For example, consider the following (pseudo-code):
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Test&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useForm&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Test&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;defaultValues&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;default&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Field&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"firstName"&lt;/span&gt;
  &lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;As we all know, the more times you define something, the higher the chance of introducing bugs.&lt;/li&gt;
&lt;li&gt;In the above scenario, if we want to rename &lt;code&gt;firstName&lt;/code&gt; to &lt;code&gt;name&lt;/code&gt;, we’d need to update it in &lt;strong&gt;at least three places&lt;/strong&gt; — significantly increasing code fragility.&lt;/li&gt;
&lt;li&gt;That’s why I created &lt;strong&gt;&lt;a href="https://piying-org.github.io/website/docs/client/intro" rel="noopener noreferrer"&gt;Piying&lt;/a&gt;&lt;/strong&gt; — a form library that achieves &lt;strong&gt;all the above functionality with just one definition&lt;/strong&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;default&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How Does Piying Achieve This?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;First, thanks to &lt;a href="https://valibot.dev/guides/mental-model/" rel="noopener noreferrer"&gt;valibot&lt;/a&gt;, the code above is simply a schema definition.&lt;/li&gt;
&lt;li&gt;Piying implements a &lt;strong&gt;traversal engine&lt;/strong&gt; that collects metadata from the schema.&lt;/li&gt;
&lt;li&gt;This metadata is then transformed into &lt;strong&gt;components and form configurations&lt;/strong&gt;, enabling full compatibility across any frontend framework.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  But What About Layout Flexibility?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;While the schema definition is fixed, Piying supports &lt;strong&gt;dynamic layout manipulation&lt;/strong&gt; via the &lt;code&gt;layout&lt;/code&gt; method.&lt;/li&gt;
&lt;li&gt;You can move any field into a container schema that supports nesting — meaning &lt;strong&gt;you can freely rearrange the UI layout without changing the schema&lt;/strong&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;intersect&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
  &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({}),&lt;/span&gt; &lt;span class="nf"&gt;setAlias&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;scope1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
  &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;key1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;test1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;value1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;keyPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@scope1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;})),&lt;/span&gt;
      &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;This effectively &lt;strong&gt;decouples definition from visual positioning&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Regarding field order in &lt;code&gt;object&lt;/code&gt;, refer to the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in#description" rel="noopener noreferrer"&gt;MDN documentation&lt;/a&gt; for details on JavaScript’s object property iteration order.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to Achieve Advanced Layouts?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Sometimes, you need more than just field rendering — think labels, validation hints, tooltips, etc.&lt;/li&gt;
&lt;li&gt;These can be achieved using &lt;strong&gt;wrappers&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;k2-label&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;setWrappers&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;label&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;If you want to customize the styling of a group of fields, you can define a custom component directly:
&amp;gt; While wrappers can be used for field groups, direct component customization is often more convenient.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;k1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;k1-label&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;setWrappers&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;label&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])),&lt;/span&gt;
    &lt;span class="na"&gt;k2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;k2-label&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;minValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;setWrappers&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;label&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;validator&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])),&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="nf"&gt;setComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fieldset&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How to Customize Wrappers or Components?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The code examples above don’t specify a particular frontend framework — because they’re &lt;strong&gt;framework-agnostic&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;However, &lt;strong&gt;wrapper and component definitions&lt;/strong&gt; are framework-specific.&lt;/li&gt;
&lt;li&gt;You can find setup instructions for your preferred framework in the &lt;a href="https://piying-org.github.io/website/docs/client/quick-start" rel="noopener noreferrer"&gt;Quick Start Guide&lt;/a&gt;.
Currently supported: &lt;strong&gt;React&lt;/strong&gt;, &lt;strong&gt;Vue&lt;/strong&gt;, and &lt;strong&gt;Angular&lt;/strong&gt;.
If you need support for another framework, feel free to &lt;a href="https://github.com/piying-org/piying-view/issues" rel="noopener noreferrer"&gt;open an issue&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Is It Ready for Production?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Piying Form&lt;/strong&gt; has been used internally for over half a year before public open-source release.&lt;/li&gt;
&lt;li&gt;Most features have undergone rigorous testing.
Code coverage exceeds &lt;strong&gt;95%&lt;/strong&gt;, making it production-ready.&lt;/li&gt;
&lt;li&gt;We provide extensive &lt;a href="https://piying-org.github.io/website/docs/client/component-use" rel="noopener noreferrer"&gt;use case demos&lt;/a&gt; and equivalent implementations of popular libraries to ease migration:

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://piying-org.github.io/website/docs/client/ngx-formly-migrate" rel="noopener noreferrer"&gt;ngx-formly Migration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://piying-org.github.io/vee-validate-migration/" rel="noopener noreferrer"&gt;vee-validate Migration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://piying-org.github.io/formik-migration/" rel="noopener noreferrer"&gt;Formik Migration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://piying-org.github.io/website/docs/client/react-hook-form-migration" rel="noopener noreferrer"&gt;React Hook Form Migration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://piying-org.github.io/react-tanstack-migration/" rel="noopener noreferrer"&gt;React TanStack Migration&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Project Repository
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/piying-org/piying-view" rel="noopener noreferrer"&gt;piying-view&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Contact Me
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;If you have any feedback, suggestions, or questions, feel free to reach out: &lt;code&gt;wszgrcy@gmail.com&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>angular</category>
      <category>vue</category>
      <category>react</category>
      <category>frontend</category>
    </item>
    <item>
      <title>General Form Solution --- Piying</title>
      <dc:creator>wszgrcy</dc:creator>
      <pubDate>Tue, 29 Jul 2025 14:29:02 +0000</pubDate>
      <link>https://dev.to/wszgrcy/general-form-solution-piying-2i2</link>
      <guid>https://dev.to/wszgrcy/general-form-solution-piying-2i2</guid>
      <description>&lt;ul&gt;
&lt;li&gt;Piying provides a unified form development experience across different frameworks&lt;/li&gt;
&lt;li&gt;It allows developers to focus more on logic and layout, freeing them from trivial tasks&lt;/li&gt;
&lt;li&gt;Piying's configuration is more semantic, reducing cognitive load for developers, and allows for custom encapsulation&lt;/li&gt;
&lt;li&gt;Piying is fully compatible with all &lt;code&gt;valibot&lt;/code&gt; Actions, allowing for perfect utilization of its validation and transformation mechanisms&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Documentation
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A sensory understanding may help you experience the charm of Piying faster. The website has implemented a large number of examples, making it easy for you to quickly understand Piying.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://piying-org.github.io/website/" rel="noopener noreferrer"&gt;https://piying-org.github.io/website/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Current Support
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Angular&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Vue&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;React&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Use Piying?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;If you meet the following conditions, Piying is suitable for you&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reduce the cost of writing and maintaining code&lt;/li&gt;
&lt;li&gt;Type-safe implementation of forms&lt;/li&gt;
&lt;li&gt;Separation of view and logic&lt;/li&gt;
&lt;li&gt;Reduce dependency on underlying frameworks to make the code more generic&lt;/li&gt;
&lt;li&gt;More convenient validation mechanism&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;If you meet the following conditions, you can try Piying&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There are some similar logic between pages, or some code looks similar&lt;/li&gt;
&lt;li&gt;You hope that future code can be migrated across different frameworks&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What Problems Does Piying Solve?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Piying has fully covered most of the common form functions, so the following problems are solved by Piying compared to mainstream libraries.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Type Safety
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Most forms on the market do not have types, and after designing the form, you often need to write a type again to constrain the input and output&lt;/li&gt;
&lt;li&gt;By defining with &lt;code&gt;valibot&lt;/code&gt;, you can naturally obtain the type and get the most basic type validation, ensuring that the data type is always correct in any situation&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Unified Frontend and Backend Types and Validation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;When both the frontend and backend are developed using JavaScript, a common problem is that the backend's type is already defined, but the backend's interface often only has &lt;code&gt;interface&lt;/code&gt;/&lt;code&gt;type&lt;/code&gt;, and even worse, there may be no type at all, only telling you which fields are present&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;valibot&lt;/code&gt; can provide both types and validation&lt;/li&gt;
&lt;li&gt;With the help of Piying's parsing, the relevant validation can be perfectly reflected on the form, making the checks on the frontend and backend the same, reducing maintenance costs and improving data security&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  General Definition Structure
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Components in different frameworks are implemented differently, so they need to be implemented separately&lt;/li&gt;
&lt;li&gt;However, after registration, the logic part is completely independent of any content in the view, making future migration and maintenance easier&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Layout Mobility
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Most forms do not have type safety, and even with metadata definitions, they cannot be moved arbitrarily and can only be displayed according to the defined order&lt;/li&gt;
&lt;li&gt;Piying successfully solves this problem, making the definition and view not coupled, allowing for arbitrary display and adjustment
&amp;gt; This way, you can customize the form according to your design requirements rather than being limited in display&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Logical OR/AND
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Most forms only implement three controls: &lt;code&gt;Group&lt;/code&gt;, &lt;code&gt;Array&lt;/code&gt;, and &lt;code&gt;Control&lt;/code&gt;, but lack logical OR/AND, making some logic implementations more complex&lt;/li&gt;
&lt;li&gt;For example, &lt;code&gt;anyOf&lt;/code&gt; and &lt;code&gt;oneOf&lt;/code&gt; in JsonSchema can be easily handled with the corresponding &lt;code&gt;FieldLogicGroup&lt;/code&gt; using &lt;code&gt;v.intersect&lt;/code&gt; and &lt;code&gt;v.union&lt;/code&gt; for dynamic switching&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Model-View/View-Model Separation of Form Controls
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Most form controls have equivalent input and output, meaning that the control inputs &lt;code&gt;a&lt;/code&gt; =&amp;gt; emits output &lt;code&gt;a&lt;/code&gt;, and inputs &lt;code&gt;a&lt;/code&gt; =&amp;gt; writes &lt;code&gt;a&lt;/code&gt; into the control&lt;/li&gt;
&lt;li&gt;Piying allows processing in these two stages separately, enabling more complex business logic&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Test Coverage
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;✔ Browser application bundle generation complete.
29 07 2025 22:17:37.889:INFO &lt;span class="o"&gt;[&lt;/span&gt;karma-server]: Karma v6.4.4 server started at http://localhost:9876/
29 07 2025 22:17:37.891:INFO &lt;span class="o"&gt;[&lt;/span&gt;launcher]: Launching browsers Chrome with concurrency unlimited
29 07 2025 22:17:37.910:INFO &lt;span class="o"&gt;[&lt;/span&gt;launcher]: Starting browser Chrome
29 07 2025 22:17:38.530:INFO &lt;span class="o"&gt;[&lt;/span&gt;Chrome 138.0.0.0 &lt;span class="o"&gt;(&lt;/span&gt;Windows 10&lt;span class="o"&gt;)]&lt;/span&gt;: Connected on socket VqecE5a27WYED9lwAAAB with &lt;span class="nb"&gt;id &lt;/span&gt;10611162
Chrome 138.0.0.0 &lt;span class="o"&gt;(&lt;/span&gt;Windows 10&lt;span class="o"&gt;)&lt;/span&gt;: Executed 452 of 454 &lt;span class="o"&gt;(&lt;/span&gt;skipped 2&lt;span class="o"&gt;)&lt;/span&gt; SUCCESS &lt;span class="o"&gt;(&lt;/span&gt;0.944 secs / 0.854 secs&lt;span class="o"&gt;)&lt;/span&gt;
TOTAL: 452 SUCCESS

&lt;span class="o"&gt;===============================&lt;/span&gt; Coverage summary &lt;span class="o"&gt;===============================&lt;/span&gt;
Statements   : 90.44% &lt;span class="o"&gt;(&lt;/span&gt; 1315/1454 &lt;span class="o"&gt;)&lt;/span&gt;
Branches     : 82.19% &lt;span class="o"&gt;(&lt;/span&gt; 457/556 &lt;span class="o"&gt;)&lt;/span&gt;
Functions    : 86.16% &lt;span class="o"&gt;(&lt;/span&gt; 411/477 &lt;span class="o"&gt;)&lt;/span&gt;
Lines        : 90.69% &lt;span class="o"&gt;(&lt;/span&gt; 1258/1387 &lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;================================================================================&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  About Open Source
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Currently, due to the large number of repositories, some code needs to be organized, so it will take about a week; please follow &lt;a href="https://piying-org.github.io/website/" rel="noopener noreferrer"&gt;the website&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;If you have any opinions or suggestions, please feel free to contact me at &lt;code&gt;wszgrcy@gmail.com&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>angular</category>
      <category>vue</category>
      <category>react</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Now you can use dynamic directive in Angular</title>
      <dc:creator>wszgrcy</dc:creator>
      <pubDate>Fri, 28 Feb 2025 14:22:46 +0000</pubDate>
      <link>https://dev.to/wszgrcy/now-you-can-use-dynamic-directive-in-angular-4mb0</link>
      <guid>https://dev.to/wszgrcy/now-you-can-use-dynamic-directive-in-angular-4mb0</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/wszgrcy/dynamic-component-define" rel="noopener noreferrer"&gt;wszgrcy/dynamic-component-define&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;npm i @cyia/dynamic-component-define
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createDynamicComponentDefine&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@cyia/dynamic-component-define&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;envInjector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;EnvironmentInjector&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;continerRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;viewChild&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ViewContainerRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ViewContainerRef&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;continerRef&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ViewContainerRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;classInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;inputValue2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hello&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nf"&gt;ngOnInit&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;define&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createDynamicComponentDefine&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;C1Component&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ClassDirective&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classInput&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ClickDirective&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;outputs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;clientEvent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;define&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;environmentInjector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;envInjector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;continerRef&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createEmbeddedView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;templateRef&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nf"&gt;changeClass&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;inputValue2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;changedClass&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>angular</category>
      <category>frontend</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Using dependency injection in nodejs environment</title>
      <dc:creator>wszgrcy</dc:creator>
      <pubDate>Tue, 30 Apr 2024 02:12:29 +0000</pubDate>
      <link>https://dev.to/wszgrcy/using-dependency-injection-in-node-environment-k1e</link>
      <guid>https://dev.to/wszgrcy/using-dependency-injection-in-node-environment-k1e</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Angular dependency injection standalone version&lt;/li&gt;
&lt;li&gt;The usage method is completely consistent with Angular's dependency injection&lt;/li&gt;
&lt;li&gt;No transformer required&lt;/li&gt;
&lt;li&gt;0 dependencies&lt;/li&gt;
&lt;li&gt;Remove Decorator
&amp;gt; &lt;code&gt;@Injectable()&lt;/code&gt;=&amp;gt;&lt;code&gt;static injectOptions={}&lt;/code&gt;
&amp;gt; &lt;code&gt;@Inject() xx&lt;/code&gt;=&amp;gt;&lt;code&gt;xx=inject()&lt;/code&gt;
&amp;gt; &lt;code&gt;@Optional()&lt;/code&gt;=&amp;gt;&lt;code&gt;xx=inject(token,{optional:true})&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;JS&lt;/code&gt;/&lt;code&gt;TS&lt;/code&gt; Support&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Source
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Angular 17.3.6&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;npm i static-injector&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Create a first level dependency injector with &lt;code&gt;Injector.create&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Injector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;inject&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;static-injector&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Main&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;child&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Child&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Child&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;output&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hello world&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;injector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Injector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Child&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;instance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;injector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;child&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;output&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Different from &lt;code&gt;injection-js&lt;/code&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;injection-js&lt;/code&gt; belongs to dynamic dependency injection and is a version used before Angular5. Currently no longer updated&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The two are basically interchangeable (the details need to be adjusted)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Support the use of &lt;code&gt;inject&lt;/code&gt; during construction&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  No Decorator
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The original use of &lt;code&gt;@Injectable()&lt;/code&gt; to pass parameters has been changed to &lt;code&gt;static injectOptions={}&lt;/code&gt;. If there are no parameters, there is no need to set them&lt;/li&gt;
&lt;li&gt;Originally, &lt;code&gt;@Optional&lt;/code&gt;, &lt;code&gt;@SkipSelf&lt;/code&gt;, &lt;code&gt;@Self&lt;/code&gt;, please use the second pass parameter of &lt;code&gt;inject&lt;/code&gt; instead&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Test
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Partially conducted unit testing to ensure that most functions are functioning properly&lt;/li&gt;
&lt;li&gt;Because most of the code itself is extracted from Angular, stability is definitely guaranteed&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Sync
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Currently, the synchronization logic has been refactored and modified using &lt;code&gt;@code-recycle/cli&lt;/code&gt; to ensure consistency with the official version of &lt;code&gt;angular&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Examples
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/wszgrcy/static-injector/tree/main/test/import"&gt;https://github.com/wszgrcy/static-injector/tree/main/test/import&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Contact me
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;If you have any technical issues, please  contact me for assistance &lt;code&gt;wszgrcy@gmail.com&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>node</category>
      <category>angular</category>
      <category>web</category>
    </item>
    <item>
      <title>Angular Event Modifier</title>
      <dc:creator>wszgrcy</dc:creator>
      <pubDate>Sun, 21 Apr 2024 09:38:01 +0000</pubDate>
      <link>https://dev.to/wszgrcy/angular-event-modifier-5c3</link>
      <guid>https://dev.to/wszgrcy/angular-event-modifier-5c3</guid>
      <description>&lt;h2&gt;
  
  
  Source Code
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;version &lt;code&gt;17.3.5&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;commit &lt;code&gt;cc57d4c4998b4e38f940afdf358af37185028072&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  EventManagerPlugin
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;For the official, it does provide an event management solution that allows for different monitoring based on any event name&lt;/li&gt;
&lt;li&gt;However, it is only based on the event name to listen to DOM elements and handle their listening&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The official does not provide a solution for processing component outputs
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;According to &lt;a href="https://github.com/angular/angular/blob/17.3.5/packages/core/src/render3/instructions/listener.ts#L212"&gt;source code&lt;/a&gt; We know that the &lt;code&gt;outputs&lt;/code&gt; of all components are actually subscribed directly, which means there is no chance to intercept them&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Is the component output actually being monitored twice?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;As mentioned above, all components are subscribed directly. However, when a component is subscribed directly, it also performs event listening, which means that &lt;code&gt;EventManagerPlugin&lt;/code&gt; can also listen. After all, custom components are also DOM elements&lt;/li&gt;
&lt;li&gt;But here's the problem. Even if you can handle this listening by creating an event manager, it only handles event listening, and subscriptions have nothing to do with it. In normal development, you will never call the DOM element of a component to do a &lt;code&gt;dispatchEvent&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Another issue is that the event manager relies on &lt;a href="https://github.com/angular/angular/blob/17.3.5/packages/platform-browser/src/dom/events/event_manager.ts#L108"&gt;source code&lt;/a&gt;,&lt;code&gt;addEventListener&lt;/code&gt; interface obtains an element and event name. How to find the corresponding component through the element?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Magic modification moment, showcasing skills begins
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;We found that there is an attribute called &lt;code&gt;__ngContext__&lt;/code&gt; on the element, and the number on this attribute is actually the LView ID&lt;/li&gt;
&lt;li&gt;We use publicly available and non-public methods &lt;code&gt;ɵgetLContext&lt;/code&gt; Get its LContext. Then read it to LView&lt;/li&gt;
&lt;li&gt;On LView, there are many key attributes, and component instances are one of them &lt;a href="https://github.com/angular/angular/blob/17.3.5/packages/core/src/render3/interfaces/view.ts#L185"&gt;Index position&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Once we find the corresponding element of the component, we can hook up the original event emission and let it only go through our own listening channel. This part is relatively simple, just look at the &lt;a href="https://github.com/wszgrcy/cyia-ngx-common/blob/master/lib/src/event/event.service.ts#L109-L121"&gt;source code&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Handling event modifiers
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;There are two types of event modifiers, one is &lt;code&gt;guard&lt;/code&gt; that returns true to prohibit, and the other is &lt;code&gt;map&lt;/code&gt;, which is used for converting data&lt;/li&gt;
&lt;li&gt;The code comes with some 'guard' type modifiers. From &lt;a href="https://github.com/vuejs/core/blob/v3.4.23/packages/runtime-dom/src/directives/vOn.ts#L14"&gt;Vue source code&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Regarding keyboard type event modifiers, fully call the official ng &lt;code&gt;ɵKeyEventsPlugin&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Use
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/wszgrcy/cyia-ngx-common/blob/master/lib/src/event/event.service.ts"&gt;source code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;npm i @cyia/ngx-common&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app module&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;EVENT_MODIFIER_OPTIONS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;EventModifiersPlugin&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@cyia/ngx-common/event&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nl"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;EVENT_MANAGER_PLUGINS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;useClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;EventModifiersPlugin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;multi&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;DOCUMENT&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;EVENT_MODIFIER_OPTIONS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;useValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;modifiers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// custom event modifier&lt;/span&gt;
            &lt;span class="na"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`prefix:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;componentOutput&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;(click)=&lt;/span&gt;&lt;span class="s"&gt;"clicked('divClicked')"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;(click.stop.once)=&lt;/span&gt;&lt;span class="s"&gt;"clicked('btnClicked')"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;点击&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;app-a&lt;/span&gt; &lt;span class="na"&gt;(output2.prefix.once)=&lt;/span&gt;&lt;span class="s"&gt;"output($event)"&lt;/span&gt; &lt;span class="na"&gt;(output2)=&lt;/span&gt;&lt;span class="s"&gt;"output($event)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/app-a&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Contact me
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;If you have any technical issues, please  contact me for assistance &lt;code&gt;wszgrcy@gmail.com&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>angular</category>
      <category>typescript</category>
      <category>frontend</category>
    </item>
  </channel>
</rss>
