Angular: Detect model() signal outputs (type inference + compodoc autodocs + runtime binding)#34833
Conversation
Adds Layer A native detection of Angular model() signal outputs in the @storybook/angular public type inference. Refs #34831 - Adds AngularModelSignal / AngularHasModelSignal / ModelSignal aliases mirroring the existing InputSignal/OutputEmitterRef conditional style. - Adds TransformModelSignalType<T>: maps each ModelSignal<E> field to E and synthesizes an intersection member `${prop}Change`: (e: E) => void (the xChange member is compiler-synthesized, never a real keyof T member). - Pins TransformComponentType composition with TransformModelSignalType as the INNERMOST wrapper (do not reorder): the synthesized `${prop}Change` is (e:E)=>void so it passes the outer Input/Output/Event transforms unchanged, and since ModelSignal extends InputSignal the model value field is idempotently re-collapsed by the outer TransformInputSignalType (no double-transform divergence). - Adds public-types.test-d.ts asserting the FINAL composed TransformComponentType<C> (composed, NOT TransformModelSignalType in isolation) for color/colorChange + model.required() + full no-regression coverage (input(), transform input(), output(), EventEmitter, @input, @output) resolving simultaneously in one type. Documented limitation (for the AC-X3 changelog): aliased model(prop, { alias: 'a' }) produces aChange at runtime, but Layer A can only synthesize ${propName}Change because TypeScript cannot observe the runtime alias. Runtime detection (Layer C) handles the alias via the resolved binding name on ɵcmp. model.required() is fully covered. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extends getComponentInputsOutputs with an additive dual detection path so a
model() field surfaces BOTH its `color` input and the compiler-synthesized
`colorChange` output, without altering @Input/@Output/input()/output()/
EventEmitter results from the decorator path.
Strategy (Probe C: esbuild/JIT strips signal AOT metadata in the unit-test
harness, so `ecmp` I/O maps are empty for signal members / `signals===false`):
- Primary: read the Angular component def via ɵgetComponentDef; ɵcmp keys the
I/O maps by template name -> propName (verified empirically), so aliased
model(x,{alias}) and model.required() resolve to their real binding names.
- Fallback: synthesize from the component instance brand (writable+subscribable
signal => model() input + ${name}Change output) for non-AOT/JIT classes.
All 3 consumers verified (no edits needed): computesTemplateFromComponent and
computesTemplateSourceFromComponent emit [color]+(colorChange) via the existing
pure I/O builders; StorybookWrapperComponent filter-inversion confirmed -- the
model input now reaches the instance through the template Input binding
(initial render + live storyProps$ updates) instead of the dropped
getNonInputsOutputsProps direct-assignment path.
Adds a NEW factory-free test block covering @Input/@output, input(), output(),
EventEmitter, model(), model.required(), aliased model(), plus a throws-if-called
ComponentFactoryResolver guard proving zero factory invocation in model()
detection. L50-212 kept commented with a tracked TODO(angular-22).
Refs #34831
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…er B) Refs #34831 Angular `model()` two-way binding signals were not surfaced in compodoc autodocs. compodoc (verified against the captured v1.2.1 output) emits a `model()` member as an IDENTICAL entry — same bare name, no decorators/ jsdoctags, ModelSignal<T> wrapper erased — in BOTH `inputsClass` AND `outputsClass`. Plain @Input/input() only land in inputsClass; plain @Output/output()/EventEmitter only in outputsClass. The reliable, version-tolerant discriminator is therefore a property whose name appears in BOTH arrays of the same component (the both-arrays discriminator); compodoc emits no model() marker, so compodoc-types.ts Property is unchanged. - extractArgTypesFromData now detects model props via the both-arrays discriminator, suppresses compodoc's spurious bare-name outputsClass duplicate (model surfaces as an input control), and synthesizes a `${name}Change` output (action: '${name}Change') reusing the per-item output shape. - Deterministic angularFilterNonInputControls re-surface branch: the synthesized `${name}Change` output + companion input are present with the flag OFF, and still re-surfaced with the flag ON despite iteration being restricted to ['inputsClass']. - New __testfixtures__/doc-model fixture (mirrors doc-button) with the captured compodoc JSON; angular-properties.test.ts now asserts the synthesized colorChange/showTextChange rows for both filter states. - doc-button EventEmitter fixture regression-guarded (untouched, still green). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Stacked commit 4 of 4 (cross-cutting) for native @storybook/angular model() signal output detection. Refs: #34831 - AC-X2a: ControlsAndActions story for a model() ColorPickerComponent (color = model<string>('#345F92')) asserting colorChange appears as an Action (fires on emit) and color is a Control/arg. Mirrors the existing Angular signal/ story precedent (template/stories_angular-cli-*/signal/, cli/page.stories.ts play pattern) exactly. - AC-X2b: TwoWayRoundTrip story whose play runs the exact sequence: (1) initial render -> initial args.color reached the instance; (2) live storyProps$ arg change via useArgs().updateArgs -> new value reaches the instance (the StorybookWrapperComponent L125->L131 live path AC-C3c flags highest-risk); (3) in-component colorChange emission round-trips back to args.color (positive two-way [(color)]); (4) action received colorChange. - AC-X3: CHANGELOG.prerelease.md entry under 10.5.0-alpha.0 describing native model() support (type inference + compodoc autodocs + runtime binding/actions) and that the hand-written Args workaround is no longer required. Explicitly documents the KNOWN LIMITATION: aliased model(prop,{alias}) - the type layer (Layer A) can only synthesize ${propName}Change, not the runtime alias; runtime (Layer C) resolves the alias via the Angular component def at AOT. model.required() fully covered. AC-X1 full verification (real output): - yarn nx run-many -t check: angular:check -> "No type errors" (Layers A/B/C clean). Single failing task nextjs-vite:check (TS2451 storybookNextJsPlugin redeclaration) is PRE-EXISTING and unrelated - nextjs-vite is not touched by any of the 4 stacked commits; zero NEW errors from A/B/C/X. - yarn nx run-many -t compile: exit 0 (42 projects). - yarn lint: exit 0 (clean). - yarn fmt:write: applied (4451 files; only the new story files + changelog modified, no unrelated changes). - Targeted Angular vitest from repo root (NgComponentAnalyzer, ComputesTemplateFromComponent, docs/angular-properties, compodoc): 14 files / 137 tests passed, no type errors, exit 0 - zero regression from Layers A/B/C. Story-execution environment constraint (honest, FALLBACK - not a faked green): the storybook-ui Vitest project (cd code && yarn storybook:vitest) is the React-based internal Storybook; its include globs cover only core/** and addons/*/** - frameworks/angular/template/** is not included, so `yarn vitest run --project storybook-ui color-picker.stories` exits 1 with "No test files found". The @storybook/angular unit harness has no AOT Vite plugin and does not glob *.stories.ts. No angular-cli sandbox exists locally. The model() play stories therefore run only in a generated Angular sandbox in CI - the exact established validation path for the pre-existing template/stories_angular-cli-*/signal/ precedent. The stories are authored correctly as real CI/sandbox-validated deliverables. Full detail recorded in .omc/plans/probe-results-angular-model-signal-outputs.md (STEP 4 story-execution environment section). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…s, comments, limitations) comment-only repoint to committed compodoc-input.json fixture; document same-name @Input/@output discriminator false-positive (compodoc.ts + changelog); synthesized ${name}Change output no longer inherits misleading input defaultValue/type summary (Docs-table only, detection logic unchanged); reference #34831. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThis PR adds end-to-end Angular ChangesAngular model() Signal Support
🎯 3 (Moderate) | ⏱️ ~25 minutes Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (2)
code/frameworks/angular/template/stories_angular-cli-default-ts/model-signal/color-picker.stories.ts (1)
6-6: ⚡ Quick winAdd explicit
.tsfile extension to component import.The coding guidelines recommend explicit file extensions for relative TypeScript module imports. As per coding guidelines: "For TypeScript source in the repo, prefer explicit file extensions for relative code imports and exports such as
./foo.tsor./bar.tsxwhen the target is another TS/JS module".♻️ Suggested fix
-import ColorPickerComponent from './color-picker.component'; +import ColorPickerComponent from './color-picker.component.ts';🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@code/frameworks/angular/template/stories_angular-cli-default-ts/model-signal/color-picker.stories.ts` at line 6, Update the relative import to include the explicit TypeScript extension: change the import that references ColorPickerComponent from './color-picker.component' to reference './color-picker.component.ts' so the module import for ColorPickerComponent follows the repo guideline of using explicit .ts extensions for TypeScript source files.code/frameworks/angular/template/stories_angular-cli-prerelease/model-signal/color-picker.stories.ts (1)
6-6: ⚡ Quick winAdd explicit
.tsfile extension to component import.The coding guidelines recommend explicit file extensions for relative TypeScript module imports. As per coding guidelines: "For TypeScript source in the repo, prefer explicit file extensions for relative code imports and exports such as
./foo.tsor./bar.tsxwhen the target is another TS/JS module".♻️ Suggested fix
-import ColorPickerComponent from './color-picker.component'; +import ColorPickerComponent from './color-picker.component.ts';🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@code/frameworks/angular/template/stories_angular-cli-prerelease/model-signal/color-picker.stories.ts` at line 6, The import of ColorPickerComponent should use an explicit TypeScript extension; update the import statement that references color-picker.component to import from './color-picker.component.ts' (i.e., change the module specifier used when importing ColorPickerComponent) so it follows the repo guideline for explicit .ts extensions on relative TypeScript imports.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In
`@code/frameworks/angular/template/stories_angular-cli-default-ts/model-signal/color-picker.stories.ts`:
- Line 6: Update the relative import to include the explicit TypeScript
extension: change the import that references ColorPickerComponent from
'./color-picker.component' to reference './color-picker.component.ts' so the
module import for ColorPickerComponent follows the repo guideline of using
explicit .ts extensions for TypeScript source files.
In
`@code/frameworks/angular/template/stories_angular-cli-prerelease/model-signal/color-picker.stories.ts`:
- Line 6: The import of ColorPickerComponent should use an explicit TypeScript
extension; update the import statement that references color-picker.component to
import from './color-picker.component.ts' (i.e., change the module specifier
used when importing ColorPickerComponent) so it follows the repo guideline for
explicit .ts extensions on relative TypeScript imports.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 9fc35a57-8e44-4c09-864f-1e8ab9050c39
📒 Files selected for processing (21)
CHANGELOG.prerelease.mdcode/frameworks/angular/src/client/angular-beta/utils/NgComponentAnalyzer.test.tscode/frameworks/angular/src/client/angular-beta/utils/NgComponentAnalyzer.tscode/frameworks/angular/src/client/compodoc.tscode/frameworks/angular/src/client/docs/__testfixtures__/doc-model/argtypes-filtered.snapshotcode/frameworks/angular/src/client/docs/__testfixtures__/doc-model/argtypes.snapshotcode/frameworks/angular/src/client/docs/__testfixtures__/doc-model/compodoc-input.jsoncode/frameworks/angular/src/client/docs/__testfixtures__/doc-model/compodoc-posix.snapshotcode/frameworks/angular/src/client/docs/__testfixtures__/doc-model/compodoc-undefined.snapshotcode/frameworks/angular/src/client/docs/__testfixtures__/doc-model/compodoc-windows.snapshotcode/frameworks/angular/src/client/docs/__testfixtures__/doc-model/input.tscode/frameworks/angular/src/client/docs/__testfixtures__/doc-model/tsconfig.jsoncode/frameworks/angular/src/client/docs/angular-properties.test.tscode/frameworks/angular/src/client/public-types.test-d.tscode/frameworks/angular/src/client/public-types.tscode/frameworks/angular/template/stories_angular-cli-default-ts/model-signal/color-picker.component.tscode/frameworks/angular/template/stories_angular-cli-default-ts/model-signal/color-picker.csscode/frameworks/angular/template/stories_angular-cli-default-ts/model-signal/color-picker.stories.tscode/frameworks/angular/template/stories_angular-cli-prerelease/model-signal/color-picker.component.tscode/frameworks/angular/template/stories_angular-cli-prerelease/model-signal/color-picker.csscode/frameworks/angular/template/stories_angular-cli-prerelease/model-signal/color-picker.stories.ts
Package BenchmarksCommit: No significant changes detected, all good. 👏 |
…ic ɵcmp only Address review feedback (Karpathy + Pragmatic): - NgComponentAnalyzer: drop the new-instance fallback (runInInjectionContext + new component()) and its orphaned imports. Signal I/O is now read purely statically from the compiled component definition (ɵcmp), so the analysis path never executes user constructor code. Verified the fallback was test-harness-only (ComputesTemplateFromComponent/compodoc/angular-properties unaffected). - Tests: assert the production ɵcmp reader via a synthetic ɵcmp mirroring the AOT shape (now also covers aliased model() resolution). Drop the over-engineered throws-if-called resolveComponentFactory guard; restore pre-existing imports. - Strip internal investigation jargon (Probe/AC/R6/Lxxx) from comments in NgComponentAnalyzer, compodoc, public-types, and the model-signal stories. - CHANGELOG: collapse to the repo one-line convention. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
There was a problem hiding this comment.
🧹 Nitpick comments (1)
code/frameworks/angular/src/client/angular-beta/utils/NgComponentAnalyzer.test.ts (1)
219-310: ⚡ Quick winAdd one mixed decorator+signal case here.
The new suite validates each source independently, but the production change is the merge between decorator-derived metadata and
ɵcmp. A single component that combines@Input/@Outputwithinput()/output()/model()would lock down the dedupe path and catch duplicate/alias ordering regressions inaddSignalInputsOutputs().🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@code/frameworks/angular/src/client/angular-beta/utils/NgComponentAnalyzer.test.ts` around lines 219 - 310, Add a new test in the same suite that creates a single component combining decorator-based members and a synthetic ɵcmp (use the withCmp helper) to exercise getComponentInputsOutputs and the merge/dedupe path in addSignalInputsOutputs; create a class FooComponent with `@Input/`@Output (e.g., input, inputWithBindingPropertyName, output, outputWithBindingPropertyName) and then attach a ɵcmp that includes signal-style inputs/outputs (arrays and strings) including names that would collide or alias (e.g., same templateName or synthesized model change names) and assert the final inputs and outputs include all unique props with the decorator values taking precedence where intended and no duplicates (use sortByPropName/containsEqual assertions similar to the existing cases).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In
`@code/frameworks/angular/src/client/angular-beta/utils/NgComponentAnalyzer.test.ts`:
- Around line 219-310: Add a new test in the same suite that creates a single
component combining decorator-based members and a synthetic ɵcmp (use the
withCmp helper) to exercise getComponentInputsOutputs and the merge/dedupe path
in addSignalInputsOutputs; create a class FooComponent with `@Input/`@Output
(e.g., input, inputWithBindingPropertyName, output,
outputWithBindingPropertyName) and then attach a ɵcmp that includes signal-style
inputs/outputs (arrays and strings) including names that would collide or alias
(e.g., same templateName or synthesized model change names) and assert the final
inputs and outputs include all unique props with the decorator values taking
precedence where intended and no duplicates (use sortByPropName/containsEqual
assertions similar to the existing cases).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 12febf9b-6d96-4d64-a6e7-036e7a83e34f
📒 Files selected for processing (7)
CHANGELOG.prerelease.mdcode/frameworks/angular/src/client/angular-beta/utils/NgComponentAnalyzer.test.tscode/frameworks/angular/src/client/angular-beta/utils/NgComponentAnalyzer.tscode/frameworks/angular/src/client/compodoc.tscode/frameworks/angular/src/client/public-types.tscode/frameworks/angular/template/stories_angular-cli-default-ts/model-signal/color-picker.stories.tscode/frameworks/angular/template/stories_angular-cli-prerelease/model-signal/color-picker.stories.ts
✅ Files skipped from review due to trivial changes (1)
- CHANGELOG.prerelease.md
🚧 Files skipped from review as they are similar to previous changes (4)
- code/frameworks/angular/src/client/compodoc.ts
- code/frameworks/angular/template/stories_angular-cli-prerelease/model-signal/color-picker.stories.ts
- code/frameworks/angular/src/client/public-types.ts
- code/frameworks/angular/template/stories_angular-cli-default-ts/model-signal/color-picker.stories.ts
Cut comments that restated the code or repeated the rationale across call site and JSDoc; kept the load-bearing ones (ɵcmp shape, type-composition order, compodoc both-arrays heuristic + its limitation). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Remove the CHANGELOG.prerelease.md entry (reverts to next). - public-types.test-d.ts: drop "Layer A"/"AC-X3" transcript prose, tighten the transform-input comment. - public-types.ts: condense the TransformComponentType JSDoc to the load-bearing do-NOT-reorder constraint. - compodoc.ts: broaden the model() false-positive note (inherited / accessor splits) and document the aliased-model() autodocs gap. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@code/frameworks/angular/src/client/public-types.ts`:
- Around line 111-117: TransformModelSignalType is incorrectly unwrapping model
values in the first mapping (it turns ModelSignal<E> into E), causing valid
model-like types (e.g., EventEmitter<number>, OutputEmitterRef<string>) to be
mistaken for handlers; change the first mapped part to preserve the original
member type T[K] (do not unwrap ModelSignal) and keep only the second mapped
part that synthesizes the `${K}Change` handler when T[K] extends
ModelSignal<infer E>, typing that handler as (e: E) => void so that later types
like TransformEventType / TransformOutputSignalType can correctly unwrap model
fields.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 6fc3613e-bcfb-45d2-b247-26bd94ba1f46
📒 Files selected for processing (3)
code/frameworks/angular/src/client/compodoc.tscode/frameworks/angular/src/client/public-types.test-d.tscode/frameworks/angular/src/client/public-types.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- code/frameworks/angular/src/client/compodoc.ts
`useArgs()` is a preview hook and throws when called in a `play`
function ("hooks can only be called inside decorators and story
functions"), which crashed the TwoWayRoundTrip story and failed
Chromatic with 1 component error.
Update args from `play` via the channel (UPDATE_STORY_ARGS +
STORY_RENDERED), matching the existing core/svelte precedent, and
tag the story `!vitest` since live arg updates disrupt the runner.
The valid render-side useArgs() write-back is unchanged.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Valentin Palkovic <dev@valentinpalkovic.dev>
Co-authored-by: Valentin Palkovic <dev@valentinpalkovic.dev>
Sidnioulz
left a comment
There was a problem hiding this comment.
I don't know Angular enough to validate the actual interfacing between Angular and Storybook, but the code looks sound to me. Manual testing passed.
|
@claude, fix CI and apply the requested changes. |
|
@copilot fix CI and apply the requested changes. |
Done in a6d1715. I applied the requested review changes, checked CI status via GitHub Actions checks, and re-ran local targeted Angular validation. The remaining Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…r-model-signal-outputs Angular: Detect model() signal outputs (type inference + compodoc autodocs + runtime binding) (cherry picked from commit de29a47)
What
Adds native
@storybook/angulardetection of the output that Angular'smodel()signal generates. Forcolor = model<string>(), Angular creates inputcolorand outputcolorChange(input name +Change). Storybook previously detected none of it, in three independent layers. This fixes all three.Closes #34831
Why
Reported via customer feedback (Omnissa) migrating to signal-based inputs/outputs. The component author had to hand-maintain a separate
Argsinterface passed toMeta— and that workaround only silenced the type error; autodocs and runtime actions stayed broken.How — 3 layers (stacked, independently-revertable commits)
public-types.ts): newTransformModelSignalType<T>composed innermost inTransformComponentTypeso amodel()field yields bothcolor: Tand a synthesized${K}Change: (e: T) => voidkey.Meta<C>now acceptscolorChangeinargTypeswith noTS2353.compodoc.ts): compodoc emits amodel()prop in bothinputsClassandoutputsClass(same bare name, no marker). Detection uses that both-arrays signal to synthesize a${name}Changeoutput argType deterministically under bothangularFilterNonInputControlsstates.NgComponentAnalyzer.ts)Testing
yarn nx check angular✅,yarn nx compile angular✅,yarn lint✅ (0 new errors)NgComponentAnalyzer.test.ts,ComputesTemplateFromComponent.test.ts(109),docs/angular-properties.test.ts+compodoc.test.ts. A new non-factoryNgComponentAnalyzertest block (the existing signal-I/O block stays commented behind a trackedTODO(angular-22)for the unrelatedComponentFactoryResolverremoval) includes a throws-if-called spy proving themodel()path never touchesresolveComponentFactory.model-signalplaystories follow the existingsignal/precedent and are validated in the Angular sandbox / test-runner in CI (the internal Storybook is React-based; Angularplaystories are not runnable in the local unit harness — same as the existingsignal/stories).Reviewed by automated architecture, code-quality, and security passes (all approved; findings addressed in the final commit).
Manual testing
yarn task sandbox --template angular-cli/default-ts --start-from auto, then start it.model-signal/ColorPickerstories.colorControl and confirm the rendered value updates; click Set green and confirmcolorChangefires in the Actions panel — no hand-writtenArgsinterface or manualargTypes.colorControl and confirm it reaches the component; click Set green and confirm the emission writes back to thecolorarg (two-way[(color)]).coloris listed as an input andcolorChangeas an output.Known limitations (documented in code)
model(prop, { alias }): the type layer can only synthesize${propName}Change(TypeScript cannot see the runtime alias); the runtime layer resolves the alias correctly viaɵcmpat AOT.model.required()is fully covered.@Input() x+@Output() xpair would be misclassified as amodel(). Inherent to detecting via an external/unpinned tool that emits nomodel()marker.Caveat for reviewers
nx run-many -t checksurfaces a pre-existing, unrelatednextjs-vite:checkTS2451(duplicatestorybookNextJsPlugindeclaration) that exists verbatim onnextand is untouched by every commit here — flagged for separate triage so it doesn't mask this PR's signal.🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
model()API for two-way binding signals.Tests