angular / angular

Deliver web apps with confidence 🚀
https://angular.dev
MIT License
95.79k stars 25.29k forks source link

CSS style not applying on dynamic injected html elements #7845

Closed srehanuddin closed 7 years ago

srehanuddin commented 8 years ago

I am using Angular 2.0.0-beta.12 & D3, i am injecting D3 barchart after viewInit, and inject css in component by adding attribute "styles", element injected and display on browser, but css is not apply on dynamic elements which are created by D3, found an issue, after component initialization css embed in document head tag, and all css selectors changed to .myClass[_ngcontent-[variable]], and this variable attribute attached to all elements of that component, but not with dynamically appended components.

here are my code

@Component({
    selector: 'app',
        template: `
            <h1>D3.js Integrated if background is yellow</h1>

            style apply on this static element but not dynamic element
            <svg><rect class="bar" width="200" height="200"></rect></svg>
        `,
        styles : [`.bar { fill: steelblue; } .bar:hover { fill: brown; }
` ]
})
class App { 

    constructor(public elementRef: ElementRef){
    } 

    ngOnInit(){
        var componentContainer = d3.select(this.elementRef.nativeElement);

        componentContainer.select("h1").style("background-color", "yellow");

//Dynamic Element Injeciton
        componentContainer.append("svg")
            .append("rect")
            .attr("class", "bar")
            .attr("width", 200)
            .attr("height", 200);
    }

}
zoechi commented 8 years ago

Looks like http://stackoverflow.com/questions/36265026/angular-2-innerhtml-styling

pkozlowski-opensource commented 8 years ago

Seems like this one was answered on SO, thnx @zoechi

ufoscout commented 7 years ago

This issue has been closed based on the fact that a workaround is available; however, the original problem is not solved and this really seems to be a bug in Angular that has a different behaviour compared to the one of the real shadow dom. We faced the same issue and solved it with the provided workaround; nevertheless, the bug is not solved, I think this issue should be reopened.

NiceGuyNimni commented 7 years ago

Came across the same issue, will they reopen it?

vicb commented 7 years ago

Duplicate of #7845

NiceGuyNimni commented 7 years ago

This is #7845

vicb commented 7 years ago

@robwormald why ?

robwormald commented 7 years ago

@vicb you closed this as a duplicate - your link comes back to this issue.

vicb commented 7 years ago

your link comes back to this issue.

See it's a duplicate ;)

I meant #7108

niravpatel9898 commented 7 years ago

But what if my style is not predefined ? Eg: When the HTML element to be injected is big and has inline css.

How will these inline css be rendered as you have no clue that they will come so you cannot predefine it ?

Toxicable commented 7 years ago

@niravpatel9898 please see #7108

niravpatel9898 commented 7 years ago

@Toxicable saw that issue. You have predefined styles which are not able to apply in that example. What I am saying is that if the html is appended later on in [innerHTML] and that html content fed to this innerHTML has inline styling in it then it is not able to apply. Example:

@Component({
  selector: 'my-app',
  template: `
    <input type="text" [(ngModel)]="html">
    <div [innerHtml]="html">
    </div>
})
export class App {
  name:string;
  html: string;
  constructor() {
    this.name = 'Angular2'
    this.html = "<span style=\"color:red;\">1234</span>";
  }
}

In the above example 1234 is not red. Now this.html can be much bigger and the styles in its html content so styles cannot be predefined. What to do in that case.

Toxicable commented 7 years ago

@niravpatel9898 as above please see http://stackoverflow.com/questions/36265026/angular-2-innerhtml-styling

niravpatel9898 commented 7 years ago

@Toxicable the link you gave me also has predefined styles. My question has been answered on stackoverflow.

Mr-Sloth commented 7 years ago

This problem took me hours... I used a material component (Angular Material and PrimeNG) and searched what they are doing wrong, cause the style did not append to some of their components. Thanks to god, it came in my mind that this could be a angular problem.

So if someone googles the problem and uses PrimeNG or Angular Material, the solution is to simply insert encapsulation: ViewEncapsulation.None in the @Component definition.

zoechi commented 7 years ago

@darklinki I think using /deep/ is the better option

Mr-Sloth commented 7 years ago

@zoechi If you want to rewrite their complete css files and this every time u update the package sure :D Or is there a way to use /deep/ on complete css files ?

zoechi commented 7 years ago

@darklinki I don't know what you mean. How is that related to "update the package" and what to you mean with "on complete css files"?

Mr-Sloth commented 7 years ago

@zoechi

css:

element /deep/ Selector { //styles }

If I understand it right we would have to use /deep/ on every style declarations. Cause you import css files and themes with angular material or primeng you would have to change their style documents. This looks for me like very much work and after updating the package it would be reverted. (I know css files could be excluded..)

YoussefTaghlabi commented 7 years ago

isn't host: /deep/ deprecated?

zoechi commented 7 years ago

@YoussefTaghlabi /deep/ and >>> are deprecated in Chrome, but that's not relevant, because Angular "emulates" (rewrites) this selector combinators so that view encapsulation is not applied. The resulting selector won't contain /deep/ or >>>.

emilio-martinez commented 7 years ago

@zoechi Given https://github.com/angular/angular/commit/b754e60 is commencing the deprecation of the shadow-piercing descendant combinators, I think there would be a gap as far as a solution here. Mainly, even if something like Renderer.createElement were to be used, _ngcontent-* isn't added to those elements as far as I can see, making them not stylable with the default ViewEncapsulation.

zoechi commented 7 years ago

@emilio-martinez I don't really understand what your point is. The change is about adding support for ::ng-deep in addition to /deep/ and >>> and eventually remove /deep/ and >>>. I don't see a gap here as long as the deprecated combinators are still supported.

mlc-mlapis commented 7 years ago

@zoechi Do you see any other possible solution for long term period than using Native Shadow DOM or CSS Custom Properties?

zoechi commented 7 years ago

@mlc-mlapis Not sure what you mean - a solution for what? I think native shadow DOM is the future. There were also some additional theming features for native shadow DOM added to the HTML specs recently.

mlc-mlapis commented 7 years ago

@zoechi I meant how to apply custom CSS changes from outside of a component with ViewEncapsulation.Emulated enabled and override/add to component's CSS and its children.

Thanks for the mention about the addition of recent HTML specs. I have to read ...

zoechi commented 7 years ago

@mlc-mlapis until native shadow DOM provides enough features in all browsers, I guess ::ng-deep will stay available, and then perhaps native will be the default and emulated go away (just my guess)

mlc-mlapis commented 7 years ago

@zoechi ... yes, it looks like that. I am trying to play with CSS Custom Properties also. It works fine and the performance is really good ... but not implemented in IE so not applicable for some apps.

zoechi commented 7 years ago

@mlc-mlapis right, adoption is slow and polyfilling is difficult and causes performance issues. We just have to wait until browsers support that stuff.

emilio-martinez commented 7 years ago

@zoechi I believe the intent is to drop all three, including ::ng-deep

The shadow-piercing descendant combinator is deprecated and support is being removed from major browsers and tools. As such we plan to drop support in Angular (for all 3 of /deep/, >>> and ::ng-deep). Until then ::ng-deep should be preferred for a broader compatibility with the tools.

mlc-mlapis commented 7 years ago

@emilio-martinez ... sure but if Angular declares a compatibility with IE you can't.

zoechi commented 7 years ago

@emilio-martinez you're right, I read the PR comment only after I posted above comment.

tytskyi commented 7 years ago

@emilio-martinez there is another discussion which says an alternative will be provided by the time it is needed https://github.com/angular/angular/issues/17867#issuecomment-312322174

ericmartinezr commented 7 years ago

FWIW, complementing what @tytskyi commented, this is what @vicb said on gitter. I'll copy & paste it as gitter's search is awful.

https://gitter.im/angular/angular?at=5956fea4329651f46e3f151f

what I said on the PR/issue comments. The shadow-piercing combinator is being deprecated by browser vendors and tools. SASS plans to remove support soon. We have introduced ::ng-deepto make sure you still will be able to pierce SD in a near future (if you you sass or other tools that don't support /deep/ any more). Any use of the shadow piercing combinator will only be removed when there is a clear migration path - my best guess is that it will be around for at least the coming year. It does not make sense to keep it long term because it makes emulated & native SD behaves differently. Hope this helps.

(removed the user ping in the chat to avoid the ping here)

Alkasih commented 6 years ago

@darklinki, you are the angular solver (LOL the developer itself cant solve the problem, the ironic)

DynamicRemo commented 6 years ago

Angular does this on purpose and actually blocks the nested referencing of DOM Elements just to save the components from overlapping with other components. There are two options either to go with DomSanitizer or via ViewEncapsulation.

1: Use DOM Sanitizer, sanitize your CSS and then simply apply it as an Angular variable in your template.

return this.sanitizer.bypassSecurityTrustHtml(htmlString);

2: Choose an appropriate View Encapsulation option as per your need. ViewEncapsulation.None - No Shadow DOM at all. Therefore, also no style encapsulation. ViewEncapsulation.Emulated - No Shadow DOM but style encapsulation emulation. ViewEncapsulation.Native - Native Shadow DOM with all it’s goodness. ViewEncapsulation.ShadowDom - Native Shadow DOM with all no restriction.

Simply add them to your @Component directive and watch your html getting styled.

@Component({
    selector: 'my-angular-tag',
    templateUrl: './myfile.component.html',
    styleUrls: ['./myfile.component.css'],
    encapsulation: ViewEncapsulation.None,
})

Hope this helps in understanding. Cheers!

mmdsharifi commented 6 years ago

This medium post helps me a lot in creating a pipe for the solution that @DynamicRemo said.

yifei-zhan commented 6 years ago

@mmdsharifi it works, however it does not help if I want to use with sass, with some sass-variables

mmdsharifi commented 6 years ago

@Kiiiwiii you can use native CSS variables!

didac-wh commented 5 years ago

This problem took me hours... I used a material component (Angular Material and PrimeNG) and searched what they are doing wrong, cause the style did not append to some of their components. Thanks to god, it came in my mind that this could be a angular problem.

So if someone googles the problem and uses PrimeNG or Angular Material, the solution is to simply insert encapsulation: ViewEncapsulation.None in the @component definition.

Based on the thumbs up your comment got, I assume people think it's the best approach. However, I see it pretty risky, since ViewEncapsulation.None will write the CSS at the top of the DOM. If by any chance you had two views with no view encapsulation, they could overwrite eachother. Actually these specific view styles will be applied to all the elements rendered.

Personally I see this situation as an important bug, and IMO the best approach is to use :host ::ng-deep .whateverSelector when the dynamic HTML is rendered inside a specific host.

I'm not even a frontend developer, so If I'm mistaken please let me know.

Cheers.

Mr-Sloth commented 5 years ago

@didac-wh

I commented nearly 2 years ago, at this moment and from what I know its still the only solution for external modules. Since you have to insert :host /deep/ inside of a loaded module every update will let you loose the functionality.

Another workaround for smaller projects seems to be binding the css files inside of the angular-cli. The disadvantage is that they are loaded for all components.

Simply for innerHtml I also use ng-deep in my own css/scss files.

kishore5a8 commented 5 years ago

Facing same issue but context is bit different. creating angular component to render react component. React component styles are not getting applied even after importing correctly. works fine with ViewEncapsulation.None but I do not want to do this as it is not providing style encapsulation.

Is there any better way to handle this?. Thanks in advance

angular-automatic-lock-bot[bot] commented 5 years ago

This issue has been automatically locked due to inactivity. Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.