@@ -3054,17 +3054,17 @@ fn test_svg_in_switch_case_with_whitespace() {
30543054
30553055#[ test]
30563056fn test_control_binding_attribute_extraction ( ) {
3057- // Test that [field ] (control binding) is extracted into the consts array.
3057+ // Test that [formField ] (control binding) is extracted into the consts array.
30583058 // Before the fix, UpdateOp::Control was not handled in attribute extraction,
3059- // causing the control binding name ("field ") to be missing from the element's
3059+ // causing the control binding name ("formField ") to be missing from the element's
30603060 // extracted attributes. This resulted in duplicate/shifted const entries.
30613061 let allocator = Allocator :: default ( ) ;
30623062 let source = r#"
30633063import { Component } from '@angular/core';
30643064
30653065@Component({
30663066 selector: 'test-comp',
3067- template: '<cu-comp [field ]="myField" [open]="isOpen"></cu-comp>',
3067+ template: '<cu-comp [formField ]="myField" [open]="isOpen"></cu-comp>',
30683068 standalone: true,
30693069})
30703070export class TestComponent {
@@ -3084,24 +3084,26 @@ export class TestComponent {
30843084 assert ! ( !result. has_errors( ) , "Should not have errors: {:?}" , result. diagnostics) ;
30853085 eprintln ! ( "OUTPUT:\n {}" , result. code) ;
30863086
3087- // The consts array should contain "field " as an extracted property binding name.
3088- // Without the fix, only "open" would appear (missing "field "), resulting in
3087+ // The consts array should contain "formField " as an extracted property binding name.
3088+ // Without the fix, only "open" would appear (missing "formField "), resulting in
30893089 // incorrect const array entries and shifted indices.
30903090 assert ! (
3091- result. code. contains( r#""field ""# ) ,
3092- "Consts should contain 'field ' from control binding extraction. Output:\n {}" ,
3091+ result. code. contains( r#""formField ""# ) ,
3092+ "Consts should contain 'formField ' from control binding extraction. Output:\n {}" ,
30933093 result. code
30943094 ) ;
30953095
3096- // Both "field " and "open" should appear in the same consts entry (same element).
3096+ // Both "formField " and "open" should appear in the same consts entry (same element).
30973097 // The property marker (3) should precede both names.
3098- // Expected: [3, "field", "open"] (property marker followed by both binding names)
3099- // Without the fix: [3, "open"] (missing "field")
3100- let has_both_in_same_const =
3101- result. code . lines ( ) . any ( |line| line. contains ( r#""field""# ) && line. contains ( r#""open""# ) ) ;
3098+ // Expected: [3, "formField", "open"] (property marker followed by both binding names)
3099+ // Without the fix: [3, "open"] (missing "formField")
3100+ let has_both_in_same_const = result
3101+ . code
3102+ . lines ( )
3103+ . any ( |line| line. contains ( r#""formField""# ) && line. contains ( r#""open""# ) ) ;
31023104 assert ! (
31033105 has_both_in_same_const,
3104- "Both 'field ' and 'open' should appear in the same consts entry. Output:\n {}" ,
3106+ "Both 'formField ' and 'open' should appear in the same consts entry. Output:\n {}" ,
31053107 result. code
31063108 ) ;
31073109}
@@ -3125,7 +3127,7 @@ fn test_pipe_slot_in_control_binding_exact_slot() {
31253127 // Element is at slot 0, pipe is at slot 1.
31263128 // The pipeBind1 call should reference slot 1, not slot 0.
31273129 let js = compile_template_to_js (
3128- r#"<cu-comp [field ]="myField$ | async"></cu-comp>"# ,
3130+ r#"<cu-comp [formField ]="myField$ | async"></cu-comp>"# ,
31293131 "TestComponent" ,
31303132 ) ;
31313133 eprintln ! ( "OUTPUT:\n {js}" ) ;
@@ -3147,7 +3149,7 @@ fn test_pipe_slot_in_control_binding_exact_slot() {
31473149#[ test]
31483150fn test_pipe_in_field_binding_with_safe_nav ( ) {
31493151 let js = compile_template_to_js (
3150- r#"<cu-comp [field ]="(settings$ | async)?.workload?.field" [title]="name | uppercase"></cu-comp>"# ,
3152+ r#"<cu-comp [formField ]="(settings$ | async)?.workload?.field" [title]="name | uppercase"></cu-comp>"# ,
31513153 "TestComponent" ,
31523154 ) ;
31533155 eprintln ! ( "OUTPUT:\n {js}" ) ;
@@ -3163,7 +3165,7 @@ fn test_pipe_in_field_binding_with_safe_nav() {
31633165#[ test]
31643166fn test_pipe_in_field_in_ngif ( ) {
31653167 let js = compile_template_to_js (
3166- r#"<div *ngIf="show"><cu-comp [field ]="(settings$ | async)?.workload?.field" [title]="name | uppercase"></cu-comp></div>"# ,
3168+ r#"<div *ngIf="show"><cu-comp [formField ]="(settings$ | async)?.workload?.field" [title]="name | uppercase"></cu-comp></div>"# ,
31673169 "TestComponent" ,
31683170 ) ;
31693171 eprintln ! ( "OUTPUT:\n {js}" ) ;
@@ -3174,7 +3176,7 @@ fn test_pipe_in_field_in_ngif() {
31743176#[ test]
31753177fn test_pipe_in_field_in_if_block ( ) {
31763178 let js = compile_template_to_js (
3177- r#"@if (show) {<cu-comp [field ]="(settings$ | async)?.workload?.field" [title]="name | uppercase"></cu-comp>}"# ,
3179+ r#"@if (show) {<cu-comp [formField ]="(settings$ | async)?.workload?.field" [title]="name | uppercase"></cu-comp>}"# ,
31783180 "TestComponent" ,
31793181 ) ;
31803182 eprintln ! ( "OUTPUT:\n {js}" ) ;
@@ -5376,3 +5378,62 @@ fn test_if_block_no_expression_skips_main_branch() {
53765378 }
53775379 assert ! ( !errors. is_empty( ) , "Should report a parse error for @if without expression" ) ;
53785380}
5381+
5382+ // ============================================================================
5383+ // Regression: @switch with @default first should preserve source order
5384+ // ============================================================================
5385+
5386+ #[ test]
5387+ fn test_switch_default_first_preserves_source_order ( ) {
5388+ // When @default appears first in source, Angular TS preserves source order:
5389+ // Case_0 = default (Other), Case_1 = case(1) (One), Case_2 = case(2) (Two)
5390+ // The conditional expression puts default's slot as the ternary fallback.
5391+ let js = compile_template_to_js (
5392+ r"@switch (value) { @default { <div>Other</div> } @case (1) { <div>One</div> } @case (2) { <div>Two</div> } }" ,
5393+ "TestComponent" ,
5394+ ) ;
5395+
5396+ // Case_0 should be the default (Other), NOT reordered
5397+ assert ! ( js. contains( "Case_0_Template" ) , "Expected Case_0_Template in output. Got:\n {}" , js) ;
5398+ let case0_start = js. find ( "Case_0_Template" ) . unwrap ( ) ;
5399+ let case0_body = & js[ case0_start..case0_start + 200 ] ;
5400+ assert ! (
5401+ case0_body. contains( "Other" ) ,
5402+ "Case_0 should render 'Other' (default in source order). Got:\n {}" ,
5403+ js
5404+ ) ;
5405+
5406+ // Conditional ternary: default slot (0) should be the fallback base
5407+ // Expected: (tmp === 1) ? 1 : (tmp === 2) ? 2 : 0
5408+ assert ! ( js. contains( "2: 0)" ) , "Ternary fallback should be slot 0 (default). Got:\n {}" , js) ;
5409+ }
5410+
5411+ // ============================================================================
5412+ // Regression: [field] should be a regular property, not a control binding
5413+ // ============================================================================
5414+
5415+ #[ test]
5416+ fn test_field_property_not_control_binding ( ) {
5417+ // [field] is a regular property binding, NOT a form control binding.
5418+ // Only [formField] should trigger control binding behavior.
5419+ // Before fix: [field] emitted controlCreate()/control() instructions.
5420+ // After fix: [field] emits regular property() instruction.
5421+ let js = compile_template_to_js ( r#"<cu-comp [field]="myField"></cu-comp>"# , "TestComponent" ) ;
5422+
5423+ // Should NOT have controlCreate
5424+ assert ! (
5425+ !js. contains( "controlCreate" ) ,
5426+ "[field] should NOT produce controlCreate. Got:\n {}" ,
5427+ js
5428+ ) ;
5429+
5430+ // Should NOT have control() call
5431+ assert ! ( !js. contains( "ɵɵcontrol(" ) , "[field] should NOT produce ɵɵcontrol(). Got:\n {}" , js) ;
5432+
5433+ // Should have regular property binding
5434+ assert ! (
5435+ js. contains( r#"ɵɵproperty("field""# ) ,
5436+ "[field] should produce regular ɵɵproperty(\" field\" , ...). Got:\n {}" ,
5437+ js
5438+ ) ;
5439+ }
0 commit comments