41

I'm writing a component using AngularJS and AngularJS directives.

I'm doing something like this:

var MyApp = angular.module('MyApp', []);

MyApp.directive('myTag', function() {
    return { /* Some logic here*/ }
});

I want to be able to change style of my component (using CSS), something like this:

<my-tag class="MyClass"></my-tag>

Besides this I want to be able to manipulate all elements style inside my component (HTML markup inside of my-tag).

Do you have any advice or useful examples how to manipulate the style properties of custom tags using AngularJS?

2
  • I am not sure what exactly you mean about changing styles. There are already ng-style and ng-class for this. Commented Oct 8, 2013 at 11:53
  • 1
    Are you talking about styles, or classes? There's a big difference. Manipulating classes is easy. Styles, not so much. Commented Mar 31, 2014 at 22:13

10 Answers 10

17

This should do the trick.

var MyApp = angular.module('MyApp', []);

MyApp.directive('myTag', function() {
    return { 
      link: function(scope, element, attributes){
        element.addClass('MyClass');
      }
    }
});
Sign up to request clarification or add additional context in comments.

4 Comments

So, I have to put a name of class inside of my directive, right? If so, I think it is not a good solution...
perhaps this is what you are after docs.angularjs.org/api/ng.directive:ngStyle
Why is this upvoted at all?? The question does not ask how to manipulate class of a directive element.
@dmr07 I'm upvoting it to encourage the author to make his question clearer in future - and since this answer worked for me based on his title :-)
16

This is how AngularJS adds core CSS styles:

angular.element(document).find('head').prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide{display:none !important;}ng\\:form{display:block;}</style>');

You can find this code in angular.js v1.2.0-rc.2.

EDIT

In a custom directive, I use this solution to bundle CSS stylesheets in the directive:

  var outputColorCSS = {
    selector: 'span.ouput-color',
    rules: [
        'display: inline-block',
        'height: 1em',
        'width: 5em',
        'background: transparent',
        'border: 3px solid black',
        'text-align: center',
        'font-weight: bold',
        'font-size: 0.8em'
    ]
  };
  var outputColorStyleSheet = outputColorCSS.selector + outputColorCSS.rules.join(';');
  angular.element(document).find('head').prepend('<style type="text/css">' + outputColorStyleSheet + '</style>');

Then you can use class="ouput-color" in your directive templates.

I found it very clean and useful.

3 Comments

Great job, I had to add braces though: ... outputColorCSS.selector + '{' + outputColorCSS.rules.join(';') + '}';
Just to clarify: this code is plain javascript, must not be placed in your link() function, or a <style> block will be added to <head> on every use of your directive!
that gave me an idea: would it be too bad to put an id on the style tag and check if it already exists??
14

I'm a little late to the party, but why aren't you all using the built in .css() method?

just use:

link: function(scope, elem, attr, ctrl)
{
    elem.css({'display': 'block', 'height': '100%', 'width': '100%'});

}

or whatever css you desire.

4 Comments

Could you give me some more details and maybe I can help you figure out why it's not working? jQlite is available by default on angular. It includes the .css() function. docs.angularjs.org/api/ng/function/angular.element api.jquery.com/css
It's working now. I had a ng-binding on an inline style of the element. The css I had during the initial linking got overwritten by that later bind. Thanks for the reply.
needs to be $(elem[0]).css({...})
@SoldeplataSaketos I think you are mistaken, I just double checked and the "css" method is indeed on elem without having to wrap it in a jquery element. Elem is already a jquery-lite element.
7

You can put custom styles in a directive's declaration with a parameter, just like you exemplified.

In order to declare a style like that, you have to define a variable to hold the custom styles:

scope: {
    myClass: '@myClass'
  },

And then set that parameter in the directive's template, like this:

<my-tag my-class="CustomClass"></my-tag>

Finally, in the template of the directive itself, reference that class:

<h1 class="{{myClass}}">{{myContent}}</h1>

I made a plunker that shows how you can customize styles in a directive, check it out here .

2 Comments

Could I make a default value if the attribute isn't set?
Where's the link to the plnkr?
6

Plunker

To manipulate the css style through an attribute directive, you could do something like this:

var app = angular.module('colorSwap', []);

app.directive('styleChanger', function() {
  return {
    'scope': false,
    'link': function(scope, element, attrs) {
      var someFunc = function(data)
      {
        /* does some logic */
        return 'background-color:' + data;
      }
      var newStyle = attrs.styleChanger;
      scope.$watch(newStyle, function (style) {
        if (!style) {
          return ;
        }
        attrs.$set('style', someFunc(style));
      });
    }
  };
});

Some html:

<div ng-app="colorSwap">
  <input type="txt" ng-init="colorName= 'yellow'" ng-model="colorName" />
  <div style-changer="colorName">this is the div content</div>
</div>

To make an element directive, change it's own style, something like this:

app.directive('elementWithStyle', function() {
  return {
    'restrict' : 'E',
    'scope': {},
    'controller': function($scope) {
      $scope.someStyle = 'Cyan';
      $scope.someFunc = function() { $scope.someStyle = 'purple' };
    },
    'template': '<div style="background: {{someStyle}}" ng-click="someFunc()"> click me to change colors </div>'
  }
});

And the html:

<div ng-app="colorSwap">
  <element-with-style>123</element-with-style>
</div>

I hope this helps. The rest of the answers cover class manipulation more or less.

Comments

3

For css manipulation inside of the childs of your directive try this:

var MyApp = angular.module('MyApp', []);

MyApp.directive('myTag', function() {
    return { 
      link: function(scope, element, attr){

       // For your tag
       element.addClass('MyClass');

       // For elements inside your directive tag
       var tag_childs = element[0].childNodes;
       for(var i = 0; i < element[0].childElementCount; i++){

          tag_childs[i].style.height = '70px';

        }

      }
    }
});

Comments

2

Here is an example, please note that this is probably not the best use of AngularJS, being declarative, you would likely want to just put the classes on the markup. However, just so you understand what's going on, let me demonstrate a simple directive to do what you first asked.

var MyApp = angular.module('MyApp', []);

MyApp.directive('myTag', function($compile) {
    return {
        restrict: 'E', // this means it will be an element
        link: function(scope, element, attrs, ctrl) {
            // First, I included the $compile service because it will be needed
            // to compile any markup you want to return to the element.

            // 1. Add the class, as you wanted
            element.addClass('MyClass');

            // 2. Add markup
            var html = '<div>Hello World</div>';
            //Compile it and add it back
            $compile(html)(scope);
            element.html(html);
        }
    };
});

Finally, on your markup, you just put this in:

<my-tag />

2 Comments

Ok. But the questions is still open - do I need to put the name "MyClass" of my custom class inside the directive declaration? This is not good solution, I try to avoid it. How to do the same, but via some parameter?
You can still put the class outside. Directives only replace content if replace: true is declared.
1

app.directive('bookslist', function() {

    return {
    	scope: true,
        templateUrl: 'templates/bookslist.html',
        restrict: "E",
        controller: function($scope){

        },
        link: function(scope, element, attributes){
        element.addClass('customClass');
      }

    }

});
.customClass table{
	background: tan;

}
.customClass td{
	border: 1px solid #ddd;

}
<!DOCTYPE html>
<html>

<head>
    <link href="app.css" rel="stylesheet">
    <script type="text/javascript" src="angular.min.js"></script>
    <script type="text/javascript" src="app.js"></script>
    <title>Task</title>
</head>

<body ng-app="app">
    <div ng-controller="myCtrl">
      
         <bookslist></bookslist>


    </div>
</body>

</html>

1 Comment

Thanks for your answe. Please edit. A good answer will always have an explanation of what was done and why it was done in such a manner, not only for the OP but for future visitors to SO.
1

Angular

app.directive("time",function(){
            var directive={};
            directive.restrict="A";
            directive.link=function(scope,element,attr,ctrl){                   
                element.css({
                    backgroundColor:'#ead333'
                });
            }
            var time=new Date().toTimeString();
            directive.template=time;
            return directive;
        });

HTML

The times is <span time></span>

Comments

0

I didn't found the perfect solution just yet, but I'm following John Papa's styling of controllers even with directives:

  • the directive is a folder (directiveName.directive)
  • 3 files inside: directiveName.directive.js, directiveName.template.html, directiveName.styles.css
  • use templateUrl when declaring the directive. The template has the link to the css file, as usual

I found it to be very clean and follows a pattern. The bad side of it is that you create several <link> tags near the directives in the rendered HTML (not seem to be a issue still, though). Check out this comment too.

That being said, take a look at Angular 1.5 component's. It's relatively new and has a much better approach. Now I use directives only for DOM manipulation (not reusability as components).

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.