angular / angular.js

AngularJS - HTML enhanced for web apps!
https://angularjs.org
MIT License
58.82k stars 27.5k forks source link

Support CSS on components (backport from ng2) #15014

Open schmod opened 8 years ago

schmod commented 8 years ago

Do you want to request a feature or report a bug?

Feature.


Has there been any discussion about backporting Angular 2's component CSS support (with emulated view encapsulation) support to work with Angular 1 components?

While projects like angular-css provide similar functionality, there does not appear to be a solution for Angular 1.x applications that provides true view-encapsulation.

IMO, this is one of the most useful (and under-appreciated) features of Angular 2. This feature would be an enormous benefit to existing Angular 1.x applications, and could provide another tool for owners of legacy applications to gradually adopt Angular 2 (while improving the modularity of their existing applications).


If this has been discussed before, please feel free to close as a duplicate -- I couldn't find any previous discussion on this topic.

gkalpak commented 8 years ago

That would be a nice addition indeed. I am not sure if this is reasonably possible in Angular 1.x.

Note that Angular 2 has its own parsers for HTML and CSS (among other things), which gives it the ability to do much more advanced transformations. (And it also has offline compiling capabilities, which gives it the option to get rid of the bloat at runtime.)

Adding all that to Angular 1 would considerably increase the size of the framework and the internal complexity (as if $compile is not complicated enough already :stuck_out_tongue:).

But it is a nice feature indeed. Putting it in the backlog, but don't hold your breath :stuck_out_tongue:

wesleycho commented 8 years ago

I would think this would be doable by creating an external module that has an extensibility hook into $compile (maybe making it exclusive to components), then adding the module as a dependency. Such a module would probably need to use the same parsing mechanism from @angular/compiler though for parity & for seamless upgrading (compiling appears to be a simple matter, as seen here. I don't think HTML parsing would be necessary, since this request sounds like it refers more to the style option of the component decorator in Angular 2.

Combined with @angular/upgrade, this may be a doable task, although if one is going the upgrade path, I'm not sure how much value there is in duplicating logic here (it would increase the JS served to bootstrap the app). I get the impression that Angular 1's core feature set is mostly frozen due to the amount of resources shifted to Angular 2 & to create stability due to a greater focus on getting users to Angular 2, although I may be mistaken. In addition, there is the challenge of making sure it is in sync with whatever breaking changes happen in Angular 2, which there are some still yet to happen in the release candidates.

dcherman commented 8 years ago

If you wanted to prototype this for interest, I actually don't think you need any extensibility hooks at all - it can be done entirely with overriding .directive and .component in a .config call.

The class and/or attribute used to emulated Shadow DOM can be added by modifying the compile function, and the addition/removal of stylesheets can be easily done in link/$destroy.

I had prototyped a project to dynamically add/remove stylesheets so that the styles only exist when a directive is actually present on the page (perf testing), but it's not that big of a stretch to expand that to this, would just need to drop a CSS parser into it.

schmod commented 8 years ago

I'm actually pretty fascinated that Angular 2 has its own (large and fairly complex) CSS parser.

I wonder if the ng2 folks have ever considered pulling it out into a standalone library...

thorn0 commented 8 years ago

I'd like to discuss a simple but seemingly good enough solution for this issue. What if components had a cssClassPrefix setting? With this setting set, every class and ng-class directive in the component's template should work a bit differently: 1) prefix every class name, 2) understand some escaping syntax for 'global' class names.

E.g. this markup

<div class="foo bar @baz">...</div>

would become the following DOM

<div class="my-component--foo my-component--bar baz">...</div>

It's important that this setting should work on the template level and shouldn't affect any nested templates. For this to be possible, the class and ng-class directives need to understand somehow in which template they're located and be able to obtain the settings associated with this template. It's something that directives can't do in the current version of AngularJS.

If we had this, the rest could be done just with LESS and its & syntax or rather with a simple custom PostCSS-based preprocessor. E.g. we can have custom syntax like:

@component my-component {
  foo { color: black; }
  bar { color: white; }
}

compiled to

my-component--foo { color: black; }
my-component--foo { color: white; }

Component names seem to be good enough prefixes, however it's totally easy to make class names even more unique by passing them through some function used by both AngularJS and the preprocessor.

dcherman commented 8 years ago

@thorn0 That defeats the point though, no? You would need to use said prefix in all of your CSS, so you may as well just have written the prefixed className to begin with. You would also need to make sure that your prefix itself is unique between components, otherwise you again risk collisions. This also gives up a different optimization wherein the styles for a component are only present on the page if at least one component is currently present.

I have not circled back to this yet, but one idea to keep the impact of this change down would actually be to use the browser's own CSS parser for the CSS modifications, but that potentially means that any styleUrl styles that aren't present in the local cache would need to be present on the same domain to be fetched (or have CORS enabled), but that's no different than templateUrl I suppose.

thorn0 commented 8 years ago

@dcherman For a component, I'd have to write the prefix only twice: one time in its settings and one time in the LESS file. As for uniqueness, component names are unique. Why not use them as prefixes?

Instead of LESS, a simple custom build-time transformation can be easily created using a framework like PostCSS. Moreover, for something as simple as prefixing, I doubt we even need a parser. A good regexp may suffice, so the code might turn out to be really tiny and usable for a run-time transformation. (Yet I don't really think run-time transformations and optimizations are that valuable. For production, everyone uses bundling anyway. If templateUrl got removed from Angular, I wouldn't be sad at all.)

meiram-tr commented 7 years ago

since the #15805 was closed adding it here also:

Do you want to request a feature or report a bug?

feature

What is the current behavior?

angularjs does not include css styles loader like the html template loader as part of the components definitions.

lets say we have a component:

module.component('myComponent', {
    template: '<some-html></some-html>'    // or we can use templateUrl: 'path/to/template.html'
}

What is the expected behavior?

I would love to add something like:

module.component('myComponent', {
    template: '<some-html></some-html>' ,   // or we can use templateUrl: 'path/to/template.html'
   // the addion
    styles: '.some-css { color: green;}' // or styleUrls: ['path/to/file.css', 'another/path/to/file.css']
}

What is the motivation / use case for changing the behavior?

  1. help developers or designers using the platform to use a structural styles.
  2. the final destination is to encapsulate the styles within a component.
  3. bigger apps often using one or few huge css files, the dynamic css style will help in performance since the majority of rules will not load at all only the relevant rules.
  4. get closer to the angular 2.x syntax - which will make the transition from angularjs to angular more natural.

Other information (e.g. stacktraces, related issues, suggestions how to fix)

https://github.com/angular/angular.js/pull/15799

andreaslarssen commented 6 years ago

+1

arielayaviri commented 6 years ago

+1

egor-xyz commented 6 years ago

+1

luninroman commented 6 years ago

+1

piller187 commented 6 years ago

Yes please!

thorn0 commented 6 years ago

I tried to play with the approach described in my comment. However, hacking class and ng-class directives, I faced problems due to #4383.

If anyone is curious to see it, a proof of concept can be found here: http://jsbin.com/timageb/edit?html,css,js,output

It might be a monstrous hack, but its potential for maintainability improvement is huge.

ryzhov commented 5 years ago

+1

abhishekkhandait commented 4 years ago

+1

shiftgeist commented 4 years ago

+1