Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
OS-jacobbell
left a comment
There was a problem hiding this comment.
Everything looks good for the auto-generated component wrappers!
Some components are excluded from auto generation, e.g. standalone ion-checkbox. See core/stencil.config.ts for the full list of excluded components wrappers, and packages/angular/src for their manual implementations. The nullableBooleanAttribute will need to be applied to their inputs directly.
ShaneK
left a comment
There was a problem hiding this comment.
Looks really solid overall, and the sweep across both the generated and hand-written wrappers is thorough.
The main thing is that I think this is a breaking change. These inputs had no type at all before, so narrowing them means bindings like [disabled]="items.length" stop compiling under strictTemplates, and it's the same on Angular 18 through 22. The test cases expect ngAcceptInputType to come out unknown, which would have been harmless, but what actually ships is boolean | string | null | undefined.
The rest are smaller. A couple of boolean props are missing from the hand-written arrays so they only work in the lazy build, and I've left a question on whether the editor warning from #30822 is actually silenced, since your ticket note had that as an open item.
| directivesArrayFile: '../packages/angular/src/lazy/directives/proxies-list.ts', | ||
| excludeComponents, | ||
| outputType: 'component', | ||
| booleanAttributes: true, |
There was a problem hiding this comment.
The <ion-modal [handle]="42"> failure in the description has a flip side I don't think we've accounted for. These inputs had no type at all before, so anything that compiled and isn't boolean, any or nullable now fails too. Something like [disabled]="items.length" is the common one, and it's the same on Angular 18 through 22.
What gets emitted is ngAcceptInputType_<prop>: boolean | string | null | undefined. Upstream chose the narrow type on purpose, the helper's own comment says widening it to unknown would let any expression through, so this is intended rather than a slip. It does mean every boolean input in the library gets stricter in a minor though.
Could we check the breaking change box, or scope the claim in the description? It probably wants a changelog note either way.
| outputType: 'standalone', | ||
| // Emit each component in a separate file rather than putting them all in one large file. | ||
| esModules: true, | ||
| booleanAttributes: true, |
There was a problem hiding this comment.
Is there an ionic-docs PR for this one? I couldn't find one linked. The part consumers will get wrong is that null and undefined pass through instead of coercing to false, since that's the opposite of what Angular's own booleanAttribute does.
The "Standalone Directive" section of docs/component-guide.md could use a line too. It's where someone goes to add a hand-written wrapper and it still points at ion-checkbox and ion-toggle for boolean inputs without mentioning the transform, so the next one added won't match its neighbors.
| */ | ||
|
|
||
| /** | ||
| * Transforms a value to a boolean so that boolean properties can be set by attribute presence, |
There was a problem hiding this comment.
Did this end up confirmed? The original report was an editor warning rather than a build failure, and I'm not sure the new option silences it on its own.
From the compiler side it doesn't. Writing <ion-item button detail> compiles clean with strictTemplates on against both this branch and the base, in both entry points, on Angular 18 through 22. So whether #30822 is actually fixed comes down entirely to the language service. If it isn't silenced there, that's the second Stencil option and probably its own call.
| * reach inputs routinely from the `async` pipe before its first emission and from form control | ||
| * values: | ||
| * | ||
| * ```tsx |
There was a problem hiding this comment.
This is the only one of the three copies anyone will actually edit, and it's the shortest. It drops the notes the generated copy carries about why Angular's booleanAttribute isn't imported, and about the parameter type being what ngAcceptInputType derives from, so widening it to unknown would quietly loosen template checking everywhere. That second one especially is the kind of thing someone would undo without realizing.
Meanwhile the tsx example and the async pipe paragraph read more like PR description material. I think trading one for the other would leave this better off, but up to you!
| if (value === null || value === undefined) { | ||
| return value; | ||
| } | ||
| return typeof value === 'boolean' ? value : value !== 'false'; |
There was a problem hiding this comment.
Anything that isn't a boolean, null or undefined falls into the string branch here, so 0 and NaN come back true. Before this, Angular wrote the raw value straight through and Stencil coerced 0 to false, so [disabled]="items.length" on an empty list flips from enabled to disabled. With strictTemplates on you'd get a compile error instead, without it it's silent. Empty string, null, undefined and objects don't change, so the focusTrap === false cases in the ticket are all fine.
Coercing only strings would keep the feature and the old behavior. The catch is that the two generated copies of this helper are byte-identical to what @stencil/angular-output-target 1.5.0 ships, and the whole angular-component-lib directory gets recopied on every core build, so changing it here alone would leave the hand-written wrappers behaving differently from the generated ones. It'd have to go upstream again.
Mostly flagging it so it's a deliberate call rather than something we notice later, since changing it afterwards is its own behavior change.
| 'labelPlacement', | ||
| 'mode', | ||
| 'name', | ||
| 'value', |
There was a problem hiding this comment.
| 'value', | |
| { name: 'required', transform: nullableBooleanAttribute }, | |
| 'value', |
The required prop is a public boolean on ion-checkbox and the regenerated lazy proxies picked it up, but this array doesn't list it at all, so it binds in the lazy build and does nothing in standalone. Both ion-toggle and ion-select have the same gap. It predates this PR, but since this one is a sweep over exactly these arrays it seems worth catching here. I added all three and they compile fine with the inputs typed correctly.
The hand-written wrappers are also the part the upstream tests can't reach, so this is where drift shows up. I threw together a script modelled on verify-change-detection.js that checks each one's boolean props against core's types, and it finds exactly these three plus one on popover and nothing else. Happy to share it.
| 'value', | ||
| ]; | ||
|
|
||
| /* ProxyCmp only needs the names, and runs at runtime rather than through the Angular compiler. */ |
There was a problem hiding this comment.
This line and the comment above it are byte-identical in 16 files. The NG1010 thing in the PR body is real but it only applies to the @Component/@Directive inputs array, and ProxyCmp is a plain runtime decorator whose inputs never reaches ngtsc, so this could be a shared helper instead. Most of the wrappers already import from @ionic/angular/common or utils/proxy, so 15 of the 16 sites are a one-token change to an existing import. Only the icon wrapper would pick up a new dependency and it can keep its local .map.
One thing to watch if you do take it though. It compiles fine, but changing the line count near these classes makes TypeScript splice unrelated comments into the synthesized ngAcceptInputType members in the emitted .d.ts. Still valid TypeScript and purely cosmetic, and there's none of it on this branch today, but worth a glance at dist afterwards.
| 'reference', | ||
| 'size', | ||
| 'side', | ||
| ]; |
There was a problem hiding this comment.
| ]; | |
| { name: 'keyboardEvents', transform: nullableBooleanAttribute }, | |
| ]; |
The keyboardEvents prop is a public documented boolean on ion-popover and it's missing from this array entirely, so it's the one popover boolean this PR doesn't reach. Checked that adding it compiles and wires up properly. Pre-existing, same class of thing as the required gap over on checkbox.
While you're in here, the modal array has the mirror problem, it lists translucent and event and neither of those exists on ion-modal. I think the standalone input wrapper has a couple in the same state too.
Issue number: resolves #30822
What is the current behavior?
Boolean props have to be bound explicitly. A bare attribute resolves to the empty string, so an editor reports
TS2322: Type 'string' is not assignable to type 'boolean'on markup like this:It compiles and works at runtime, because Stencil coerces boolean attributes on the element. The problem is what the Angular compiler accepts.
What is the new behavior?
Boolean props can now be set by attribute presence:
@stencil/angular-output-targetto 1.5.0 and enables its newbooleanAttributesoption on both Angular output targets. That declares an input transform on 144 boolean props across 57 components in the lazy output, and 45 components in the standalone output.ion-modalandion-popoveralone account for 19. Without this,<ion-checkbox checked>and<ion-modal handle>would still be rejected.nullandundefinedare passed through rather than coerced tofalse, so props that treat them as a third state keep working.ion-item detail,ion-modal handleandion-content forceOverscrollall resolveundefinedto a mode-specific default.core's build.Does this introduce a breaking change?
Other information
Dev build:
9.0.4-dev.11789496656.1690cf55The dependency bump is the fix rather than an unrelated update. The generated proxies are overwritten on every
corebuild, so this could not be solved in this repo. The change was made upstream in stenciljs/output-targets#842 and released in 1.5.0.The hand-written wrappers use one mixed input array, with the plain names derived for
ProxyCmp:A helper that built the list instead fails with
NG1010: Failed to resolve @Directive.inputs to an array, since Angular has to resolve the metadata statically.common/utils/boolean-attribute.tsduplicates the copied helper for the same reasonproxy.tsduplicatesProxyCmp; refer to the TODO at the top ofproxy.ts.Verified against
packages/angular/test/apps/ng18withstrictTemplatesenabled, consuming the built package:<ion-item button detail>compiles (generated wrapper).<ion-checkbox checked indeterminate disabled>,<ion-toggle checked enableOnOffLabels>,<ion-input clearInput counter required readonly>and<ion-modal isOpen handle keepContentsMounted showBackdrop>compile (hand-written wrappers).<ion-modal [handle]="42">fails withType '42' is not assignable to type 'string | boolean | null | undefined', which shows type checking genuinely engages rather than silently passing.