patrickkeller
patrickkeller

Reputation: 1246

Extend/Override style of reusable angular2 component

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.

Upvotes: 38

Views: 29403

Answers (4)

Mohammed Safeer
Mohammed Safeer

Reputation: 21535

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/

Upvotes: 25

seescode
seescode

Reputation: 2131

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 {
}

Upvotes: 7

Thierry Templier
Thierry Templier

Reputation: 202146

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:

Upvotes: 11

G&#252;nter Z&#246;chbauer
G&#252;nter Z&#246;chbauer

Reputation: 657148

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.

Upvotes: 8

Related Questions