For those who come across this in the future, I'd like to shed some light on why its a linting error, and why or why not you should use the host
property.
So, there are a couple ways to set properties and listen to events on the host component. The host
property in the component decorator or with @HostBinding
(for properties) and @HostListener
(for events).
When we use the host
property, we can use syntax that's exactly like it is in templates, []
and ()
and direct properties like class
. This is great because you don't have to import anything and when you look at it, you'll pretty much know what will happen. Now, when you get into some more complex scenarios, like setting aria properties your logic in these properties get complicated. E.g:
@Component({
selector: 'my-component',
host: {
'[attr.aria-expanded]': 'expanded'
}
})
export class MyComponent {
expanded: boolean = false
}
Here we can see that the expanded property is used to set the aria-expanded
attribute on the host. Using any tooling, whether it be IDE's, TypeScript, LanguageExtensions, we would not be able to see the two are linked.
This causes a problem when you do refactoring, and you missed the logic that are in those strings. And when that happens, it's a real pain.
So to get around that, you would use the @HostBinding
decorator.
@Component({
selector: 'my-component'
})
export class MyComponent {
@HostBinding('attr.aria-expanded')
expanded: boolean = false
}
Now you can change the name of the property to what ever you want and everyone is happy.
That is until you get to properties that could affect multiple host element attributes, or that actually has some kind of logic to it.
HostBinding
@Component({
selector: 'my-component'
})
export class MyComponent {
@HostBinding('attr.aria-expanded')
@HostBinding('class.expanded')
expanded: boolean = false
}
Some people don't like the multiple @HostBindings
on a property. And that could be changed to:
host
@Component({
selector: 'my-component',
host: {
'[attr.aria-expanded]': 'expanded',
'[class.expanded]': 'expanded',
}
})
export class MyComponent {
expanded: boolean = false
}
And properties that actually has logic:
HostBinding
@Component({
selector: 'my-component'
})
export class MyComponent {
@HostBinding('attr.aria-expanded')
@HostBinding('class.expanded')
get expanded(): boolean {
// Don't actually do this, this is just an example for Hostbinding vs. host
return this._expanded ? true : null
}
// create a setter here as well.
private _expanded: boolean = false
}
vs
host
@Component({
selector: 'my-component',
host: {
'[attr.aria-expanded]': 'expanded ? true : null',
'[class.expanded]': 'expanded',
}
})
export class MyComponent {
expanded: boolean = false
}
So, now that we know what each does, we can talk about why host
properties are flagged by the linter by default.
When using the host
properties, there's really no checks to actually see if you spelled the property correctly. When you build Angular with AoT (which is usually for production) you'll most likely get the errors, and then fix it. It's just quicker to get feedback in your editor when using @HostBinding
, rather than wait for a long build progress (depending on how big your application is, actually).
So, because of that almost unknown (to today's compilers) string values, using host
properties is flagged by default.
Maybe in the future when AoT could be used in development (I think with the Ivy renderer?), we could get those compiler errors. But in the mean time, we're not there yet.