Reputation: 1246
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
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
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
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
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