Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 22 additions & 17 deletions crates/oxc_angular_compiler/src/directive/property_decorators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1812,19 +1812,21 @@ pub(crate) fn extract_host_bindings_in<'a>(
continue;
}

let Some(decorator) = find_decorator_by_name(decorators, "HostBinding", consts) else {
continue;
};

let Some(class_property_name) = property_name else {
continue;
};

// Extract the host property name from the decorator argument
let host_property_name =
extract_host_binding_name(decorator).unwrap_or_else(|| class_property_name.clone());
// Every @HostBinding of the member is a binding: ngtsc compiles
// `@HostBinding('class.a') @HostBinding('b') get x()` to two.
for decorator in
decorators.iter().filter(|d| angular_member_decorator(d, consts) == Some("HostBinding"))
Comment thread
Brooooooklyn marked this conversation as resolved.
{
// Extract the host property name from the decorator argument
let host_property_name =
extract_host_binding_name(decorator).unwrap_or_else(|| class_property_name.clone());

bindings.push((host_property_name, class_property_name));
bindings.push((host_property_name, class_property_name.clone()));
}
}

bindings
Expand Down Expand Up @@ -1904,21 +1906,24 @@ pub(crate) fn extract_host_listeners_in<'a>(
continue;
}

let Some(decorator) = find_decorator_by_name(decorators, "HostListener", consts) else {
continue;
};

let Some(method_name) = property_name else {
continue;
};

let (event_name, args) = parse_host_listener_config(allocator, decorator);
// Every @HostListener of the member is a listener: ngtsc compiles
// `@HostListener('click') @HostListener('pointerdown') m() {}` to two.
for decorator in decorators
.iter()
.filter(|d| angular_member_decorator(d, consts) == Some("HostListener"))
{
let (event_name, args) = parse_host_listener_config(allocator, decorator);

let Some(event_name) = event_name else {
continue;
};
let Some(event_name) = event_name else {
continue;
};

listeners.push((event_name, method_name, args));
listeners.push((event_name, method_name.clone(), args));
Comment thread
Brooooooklyn marked this conversation as resolved.
}
}

listeners
Expand Down
44 changes: 44 additions & 0 deletions crates/oxc_angular_compiler/tests/stacked_host_decorators_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//! A member can carry several `@HostListener`s or `@HostBinding`s
//! (`@HostListener('click') @HostListener('keydown.enter') activate() {}`); each
//! one is a host listener or binding, as in ngtsc. The expected code is
//! `@angular/compiler-cli` 21.0.1's `hostBindings` for the same class.

use oxc_allocator::Allocator;
use oxc_angular_compiler::{TransformOptions, transform_angular_file};

#[test]
fn every_stacked_host_listener_and_binding_is_compiled() {
let source = r"
import { Component, HostBinding, HostListener } from '@angular/core';
@Component({ selector: 'c-cmp', template: '' })
export class CCmp {
@HostListener('click') @HostListener('keydown.enter') activate() {}
@HostListener('focus', ['$event']) @HostListener('blur', ['$event']) onFocus(e: Event) {}
@HostBinding('class.on') @HostBinding('attr.aria-pressed') on = true;
}
";
let allocator = Allocator::default();
let result = transform_angular_file(
&allocator,
"test.ts",
source,
Some(&TransformOptions::default()),
None,
);
assert!(!result.has_errors(), "Should not have errors: {:?}", result.diagnostics);
let code: String = result.code.chars().filter(|c| !c.is_whitespace()).collect();

let listeners = concat!(
r#"i0.ɵɵlistener("click",functionCCmp_click_HostBindingHandler(){returnctx.activate();})"#,
r#"("keydown.enter",functionCCmp_keydown_enter_HostBindingHandler(){returnctx.activate();})"#,
r#"("focus",functionCCmp_focus_HostBindingHandler($event){returnctx.onFocus($event);})"#,
r#"("blur",functionCCmp_blur_HostBindingHandler($event){returnctx.onFocus($event);});"#,
);
assert!(code.contains(listeners), "Expected the four listeners in:\n{}", result.code);
assert!(
code.contains(r#"i0.ɵɵattribute("aria-pressed",ctx.on);i0.ɵɵclassProp("on",ctx.on);"#),
"Expected both bindings in:\n{}",
result.code
);
assert!(code.contains("hostVars:3"), "Got:\n{}", result.code);
}
Loading