Coder Social home page Coder Social logo

Comments (9)

JoostK avatar JoostK commented on April 27, 2024

This is expected, as there is a difference between attr.class and class; the former is an attribute binding, whereas the second is a class property binding. The property binding applies the individual items in the array as individual classes, crucially without parsing them as they are provided as an array. Class names with spaces are invalid and are therefore not actually applied as class name, as the browser rejects setting them.

from angular.

pkozlowski-opensource avatar pkozlowski-opensource commented on April 27, 2024

I guess part of the confusion might be coming from the fact that NgClass does this splitting: https://github.com/angular/angular/blob/main/packages/common/src/directives/ng_class.ts#L184-L190

Or at least this is a difference between class bindings and NgClass and we should strive to minimize those.

from angular.

Profana92 avatar Profana92 commented on April 27, 2024

This is expected, as there is a difference between attr.class and class; the former is an attribute binding, whereas the second is a class property binding. The property binding applies the individual items in the array as individual classes, crucially without parsing them as they are provided as an array. Class names with spaces are invalid and are therefore not actually applied as class name, as the browser rejects setting them.

Not completly sure it that it a attr.class / class issue, as

@HostBinding('class')
get Classes() {
return 'p-icon-small w-full h-full';
}
same story as an array:

@HostBinding('attr.class')
get Classes() {
return ['flex bg-primary box-shadow-2xl border-2 border-accent'];
}

also works just fine. The difference is, when @HostBinding to attr.class is used together with host property its appending the classes and styles to class list and if @HostBinding to class then it overwrites them. So @HostBinding is recognizing classes in a string and host property doesn't. Keeping in mind, that host property should be a replacement for @HostBinding we can see, that implementation is different and it cannot be a replacement out of the box at it current state.

According to docs:
https://angular.dev/guide/components/host-elements#the-hostbinding-and-hostlistener-decorators

Always prefer using the host property over @HostBinding and @HostListener. These decorators exist exclusively for backwards compatibility.

If we are supposed to prefer host over @HostBinding shouldn't the function-wise implementation be the same or at least not less complete in some aspects?

from angular.

JoostK avatar JoostK commented on April 27, 2024

I am confused, as you're showing three different things: attr.class with an array, class without an array, and class with an array. The non-array form of a class binding parses the provided string into its separate class names, as separated by whitespace. The array form of class expects the individual classes in the array to be valid class names already, so they aren't individually parsed.

from angular.

Profana92 avatar Profana92 commented on April 27, 2024

I am confused, as you're showing three different things: attr.class with an array, class without an array, and class with an array. The non-array form of a class binding parses the provided string into its separate class names, as separated by whitespace. The array form of class expects the individual classes in the array to be valid class names already, so they aren't individually parsed.

It seems you're completely correct, I will try to group it to be more understandable and to present my point correctly. The basic problems are difficulties with proper conditional setting of multiple classes with host property:

This works, strings are getting interpreted properly even if more than one.

host: {
    '[class]': `'bg-primary p-10'`,
  },

This works as long as returning one class per array index:

host: {
'[class]': ['bg-primary', 'p-10'],
},

this doesn't work, as we try to return more than one:

host: {
'[class]': ['bg-primary p-10'],
},

With attr.class this applies classes without classes definition, so classes are there but without styling with result of class="bg-primary,p-10", so classes separated by comma:

host: {
'[attr.class]': ['bg-primary p-10'],
},

Meaning, there is a difference in implementation of host for class and attr.class, string and array of strings.

When we need conditional classes, which is my situation, you can also have template literal, but only one condition can be applied then, not suitable for anything more complex.

host: {
    '[class]': `size==="medium" ? "p-10 bg-primary" : ""`,
  },

or an array of conditions, but then you can return only one class per array index, which makes an array very complex as we need to check same condition for every class we want to add, if we return more than one per index then it breaks.

host: {
'[class]': ['flex', 'max-w-max', 
        orientation === 'row' && !reversed ? 'flex-row': '',
        orientation === 'row' && !reversed ? 'justify-center': '',
        orientation === 'row' && !reversed ? 'items-center': '',],
},

There is also a @HostBinding decorator, which actually also struggles to work with extensive testing, it also works as long as returs one item, it can return a string of classes, but only one string of classes, if more it just applies the last array item, both attr.class and class act similar in this manner:

  @HostBinding('attr.class')
  get Classes() {
    return ['bg-primary border-2 border-accent'];
  }
@HostBinding('class')
  get Classes() {
       return ['bg-primary border-2 border-accent'];
  }

but when more than one item in array, then only last gets applied:

  @HostBinding('class')
  get Classes() {
    return ['bg-primary border-2 border-accent', 'border-dotted'];
  }

All of the problems doesn't exist on ngClass, as we can do basically every of the above:

[ngClass]="['bg-primary border-2', 'border-accent', size==='medium' ? 'p-5 m-20':'']"

This applies all the classes, string of multiple classes, multiple array strings, conditional strings of one or more classes and they all work, which is actually best implementation.

In conclusion, host and @HostBinding doesn't seem to have the same implementation and are confusing in relation with ngClass, as i struggle to find a good way to apply multiple class to host element conditionally with both of those solutions. The same rules that apply for ngClass doesn't apply to host property and to @HostBinding. I think, that ngClass and host implementation of those things should be a bit more similar (ngClass is working perfectly and @HostBinding is legacy), because as for now it is confusing and doesn't really work well with tailwind and i struggle to find a good solution for conditional class implementation in host property. There is always an option to throw a div around everything in html file, but thats always an extra div, that doesn't present enough value.

from angular.

pkozlowski-opensource avatar pkozlowski-opensource commented on April 27, 2024

Relevant: #40623

from angular.

pkozlowski-opensource avatar pkozlowski-opensource commented on April 27, 2024

@Profana92 the points you are rising are well understood and boil down to 2 points:

  • need for providing a space-separated list of classes when using [class] bindings with an array literal;
  • aligning behaviour of the style-specific [class] binding with what is possible with ngClass

I've changed the title of this issue to better reflect the crux of the issue.

The second point is discussed in #40623.

from angular.

pkozlowski-opensource avatar pkozlowski-opensource commented on April 27, 2024

Note: the support for space-separated list of classes was not initially added in the [class] bindings due to the performance considerations.

from angular.

pkozlowski-opensource avatar pkozlowski-opensource commented on April 27, 2024

Discussed this during a team meeting and decided against supporting this feature due to its performance cost (increased code size of the framework and runtime processing cost).

from angular.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.