bdavidxyz
bdavidxyz

Reputation: 2560

angularjs directive : how to encapsulate css?

After a project with Web Components, i'm going back to AngularJS. I'm frustrated by the fact I can't find a proper way for a directive to keep its CSS internal (or encapsulated).

With web component I hadn't this problem since there is already a style tag that can be embedded in the template.

That's not the case with AngularJS directives.

Until here what I saw is :

1) define an CSS rule in an external file : my-directive {color:red;}, but this is not encapsulation.

2) define an internal rule with element.css({}); inside link function, but in this case the style is applied inline and thus is too heavy and cannot be easily override by external css.

Are there other ways ?

Thanks

Upvotes: 5

Views: 9740

Answers (3)

IMujagic
IMujagic

Reputation: 1259

There is a one angular service already created on GitHub, you can load your css files dinamically, maybe it can be helpful

https://github.com/Yappli/angular-css-injector

Or you can give a chance to GruntJS and you can have a very nice project structure, every module/folder can have own css file, and Grunt will bundle all that files into one (or many, it depends how you configure). It's easy to manage and change, but also you have only one file loaded on your page. Maybe these links can be helpful to find Grunt module that can help you.

https://www.npmjs.org/package/grunt-contrib-cssmin

Upvotes: 1

Michael Warner
Michael Warner

Reputation: 4217

One Method you can try is declaring and assigning the styles in JavaScript.

Declare a Style object.

styles = {
        background: 'green',
        color: 'black'
      };

Assign the object to a template using ng-style

<div ng-style="$ctrl.styles">
   My Component with local CSS
</div>

Here are some the following advantages using this method

  1. Using Variables to drive themes.
  2. Mixins through merging two Style Objects together (lodashes _.defaults)
  3. Control over style overwriting.
  4. Applying styles through logical conditions.
  5. Having local Styles that can't effect other components.
  6. You can place styles in angular services and inject them into only the component that need them.

Full Example

//Component

class myComponent {
    constructor( myCSSService ) {
        this.styles = {
            background: 'black',
            color: 'white'
        };
        this.myCSSService = myCSSService;
    }
}

myComponent.$inject = [ 'myCSSService' ];

angular.module( 'myModule', [] )
    .component( 'myComponent', {
        controller: myComponent,
        bindings: {},
        template: `
          <div ng-style="$ctrl.styles">
            My Component with local CSS
          </div>
          <div ng-style="$ctrl.myCSSService.styles">
            My Component with Injected CSS
          </div>
        `,
    } );

//Style Service

class myCSSService{
  constructor( ) {
      this.styles = {
        background: 'green',
        color: 'black'
      };
  }
}

angular.module( 'myCSSModule', [] )
    .service( 'myCSSService', myCSSService );

Upvotes: 1

rchawdry
rchawdry

Reputation: 1266

You can allow users to pass in class and/or style elements on the directive itself and you can have fine-grained control over how that class/style is applied to your template. The first step is to declare your directive with replace : true, which will then carry any class/style information to your underlying template. For instance:

app.directive("myDirective",function(){
  return {
    restrict:'AE',
    replace : true,
    template: '<div>This is my directive</div>'
  };
});

When you use this in HTML like this:

<my-directive class="red"></my-directive>

The resulting HTML will be:

<div class="red">This is my directive</div>

As you can see, the replace directive removes the directive tag, but preserves the attributes of the directive and applies them to the template. Therefore, in your directive, you don't technically have to do anything and your users can pass in style information to be automatically applied as necessary.

However, let's assume that your directive layout is more complicated:

app.directive("myDirective",function(){
  return {
    restrict:'AE',
    replace : true,
    template: '<div><div>My Title</div>My content</div>'
  };
});

Then you can explicitly indicate additional class/style references that your directive can optionally use and where the class would be applied. For example:

<my-directive class="red" class-title="blue"></my-directive>

And then, in your directive's compile or link function, you can set the class for internal template items if these are indicated:

app.directive("myDirective",function(){
  return {
    restrict:'AE',
    replace : true,
    template: '<div><div>Title</div>Content</div>',
    compile : function(elem,attr){
        if (attr.classTitle){
          var titleElem = angular.element(elem.children()[0]);
          titleElem.attr("class",attr.classTitle)
        }
    }
  };
});

Which would result in the following:

<div class="red" class-header="blue">
    <div class="blue">My Title</div>
    My Content
</div>

There's even more good things you can do with transclusion so that people can use their own content and styling for elements.

Upvotes: 0

Related Questions