38

Assuming we want to use a component from some library in angular2 (example from material2). The component annotation looks like this:

@Component({
  moduleId: module.id,
  selector: 'md-input',
  templateUrl: 'input.html',
  styleUrls: ['input.css'],
  providers: [MD_INPUT_CONTROL_VALUE_ACCESSOR],
  host: {'(click)' : 'focus()'}
})

This component ships with a "default" stylesheet, the "input.css". If we use this component in our app we likely want to override/extend some of the style, without copying and manipulating the component itself. How to do this?

Possible Solution 1: Set the Encapsulation to "ViewEncapsulation.None":
This is not really a solution, just a workaround.

Possible Solution 2: Use "::shadow" or "/deep/" in CSS:
Works also, but its deprecated according to WebComponent spec.

Possible Solution 3: Use global CSS and override the component CSS:
Works also, but it violates the shadow DOM concept.

Possible Solution 4: Override directly in the template of parent component:

Example:

<my-cmp [font-size]="100"></my-cmp>

Is not really suitable if we do a lot of overriding.

Possible Solution 5: Override or extend the "@Component" definition with an additional stylesheet somehow:
This seems to be the only correct solution (at least for me). But i have no idea how to do this...

Any advice on this? Maybe i got something wrong... Thanks.

patrickkeller
  • 1,236
  • 2
  • 11
  • 20

4 Answers4

25

In Angular 4, We can override the style with the ::ng-deep pseudo-class selector from the inherited class style sheet.

:host ::ng-deep element {
    //your style here
}

For more information refer http://blog.angular-university.io/angular-ngclass-ngstyle/

Mohammed Safeer
  • 20,751
  • 8
  • 75
  • 78
11

For the solution 5, you need to create a subclass for the targetted component, create a custom decorator that handles / overrides the metadata and set it for the current sub component.

Here is a sample:

@CustomComponent({
  styleUrls: ['css/style.css']
})
export class OverridenComponent extends SomeComponent {
}

The CustomComponent decorator would look like this:

export function CustomComponent(annotation: any) {
  return function (target: Function) {
    var parentTarget = Object.getPrototypeOf(target.prototype).constructor;
    var parentAnnotations = Reflect.getMetadata('annotations', parentTarget);

    var parentAnnotation = parentAnnotations[0];
    Object.keys(parentAnnotation).forEach(key => {
      if (!isPresent(parentAnnotation[key])) {
        annotation[key] = parentAnnotation[key];
      }
    });
    var metadata = new ComponentMetadata(annotation);

    Reflect.defineMetadata('annotations', [ metadata ], target);
  }
}

See this question for more details:

Community
  • 1
  • 1
Thierry Templier
  • 198,364
  • 44
  • 396
  • 360
8

Possible Solution 2: Use "::shadow" or "/deep/" in CSS:

This only applies if you use ViewEncapsulation.Native.

If you use ViewEncapsulation.Emulated (default) then Angular uses it's own interpretation of /deep/ and ::shadow and deprecation doesn't apply.

If you use ViewEncapsulation.Native then you're currently out of luck because browser native ::shadow and /deep/ deep are deprecated and Angular doesn't yet provide any support for themeing support for ViewEncapsulation.Native like for example Polymer does with (polyfilled) CSS variables and mixins.

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • 1
    You should be aware that extending decorators will not be supported by the offline template compiler and is discouraged https://github.com/angular/angular/issues/7968#issuecomment-223779992 – Günter Zöchbauer Jun 06 '16 at 15:58
7

Starting with Angular 2.3 we can use component inheritance. In order to accomplish your Solution 5 we could do this.

//This is our base component that we want to override
@Component({
  selector: 'pagination',
  templateUrl: './pagination.component.html',
  styleUrls: ['./pagination.component.css']
})
export class PaginationComponent {
}


//This inherits from our base component and uses a different style sheet.
@Component({
  selector: 'pagination2',
  //This is sharing the template with the parent class.  Note
  //this needs to be included since templateUrl doesn't automatically
  //inherit.
  templateUrl: './pagination.component.html',
  //This is using a unique css file 
  styleUrls: ['./pagination2.component.css']
})
export class PaginationComponent2 extends PaginationComponent {
}
seescode
  • 2,101
  • 15
  • 31