I want to apply a custom Style to the Toasts I show with izitoast in my Angular application.
I have installed and included the library according to instructions here
I have included the izitoast css and script in my angular.json in addition to adding the module in my app-module and also managed to display a toast with a button in a component:
html-template (toaster.component.html)
<p>toaster works!</p>
<div><button (click)="produceToast()">Test Toast</button></div>
corresponding typescript file (toaster.component.ts)
import { Component, OnInit } from '#angular/core';
import {Ng2IzitoastService} from "ng2-izitoast";
#Component({
selector: 'app-toaster',
templateUrl: './toaster.component.html',
styleUrls: ['./toaster.component.scss']
})
export class ToasterComponent implements OnInit {
constructor( public iziToast: Ng2IzitoastService) { }
ngOnInit() {
}
public produceToast() {
this.iziToast.show({ title: "Hello World"});
}
}
I understand that if I want to apply custom styling I have to somehow specify the class in the object I pass into the the show()-function, but how do I do that? Writing a CSS-class in my toaster.component.css and just referencing the name doesn't work:
.myOwnToastClass {
background-color: blueviolet;
color: white; //font-color
}
Adding the class into my styles.css doesn't work either. Neither
{class: "myOwnToastClass", title: "Hello World"} nor {class: ".myOwnToastClass", title: "Hello World"} do anything.
Can someone tell me how to pass my own custom CSS to a toast? The documentation simply says "class: The class that will be applied to the toast. It may be used as a reference." but other than that there is no documentation on how to use it.
Okay. So thanks to user fridoo I was able resolve it.
You have to add the class to your styles.css and be careful about the !important modifier:
.myOwnToastClass {
background-color: blueviolet !important;
border-radius: 0 !important;
//color: white; // you can't change the font-color with this
// you have to use the object-properties in the ts-file
}
Then reference it in the typescript files like so:
public produceToast() {
this.iziToast.show({class: "myToastClass", title: "Hello World", timeout: 3000, titleColor: "#ffffff"});
// titleColor: "white" would also work, I think it's a css-class somewhere in the background
}
Related
I am trying to implement medium-zoom from https://www.npmjs.com/package/medium-zoom
These are the steps I followed
ng new medium_zoom_test (Angular 13) with routing & css
npm install medium-zoom
image is kept in assets
app.component.html
<h1>Zoom test</h1>
<img class="zoomy" width="50%" src="assets/image.svg">
app.component.ts
import { Component } from '#angular/core';
import mediumZoom from 'medium-zoom';
mediumZoom('.zoomy', {
margin: 40,
background: '#ffffff',
scrollOffset: 40
})
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'medium_zoom_test';
}
On serving the application the webpage with image is visible. However, there's no zoom icon on hover and the image won't zoom on click. It's just a normal webpage with no change.
Any alternatives to implement the zooming of images are welcome too.
I don't think mediumZoom will work when the passed selector has no corresponding element in the DOM. What I mean is where you call mediumZoom function the component is not attached to the DOM yet. If you want to guarantee that you need to call it on ngOnInit lifecycle hook. Or you can do better which is to define a directive that applies the zoom affect like this :
import mediumZoom from 'medium-zoom';
#Directive({
selector: '[appZoomie]'
})
export class ZoomieDirective {
constructor(private el: ElementRef) {
mediumZoom(el.nativeElement, {
margin: 40,
background: '#ffffff',
scrollOffset: 40
})
}
}
And use it like this :
<img appZoomie src=".."/>
Stackblitz
So I have been looking around on how to load CSS and HTML from the server.
What I want to achieve is to request a certain template to be displayed which sends the HTML and CSS to the website and loads it in together with some user-defined styles like colour
So far I was able to inject HTML using:
<div [innerHTML]="template | sanitizeHtml"></div>
and
import { Pipe, PipeTransform, SecurityContext } from '#angular/core';
import { DomSanitizer } from '#angular/platform-browser';
#Pipe({
name: 'sanitizeHtml'
})
export class SanitizeHtmlPipe implements PipeTransform {
constructor(private sanitizer: DomSanitizer) { }
transform(value: any): any {
return this.sanitizer.bypassSecurityTrustHtml(value);
}
}
Which I have seen from different posts and blogs (thank you for that).
The HTML I have been building works like a charm:
this.template = "<div class='template' style='width: 1080px; height: 1920px; background-color: #212121;'><div class='clr-row' style='padding:45px 0px 10px 25px; position: relative; width: inherit;'><div class='clr-col-5'><div style='width: 230px; height: 60px; background-image: url(*LINK_TO_IMAGE*); background-repeat: no-repeat; float: left;'></div></div></div></div>"
This HTML is a part of the complete template.
So what I would like to do is to use styles on this by using variables.
So what I have tried is to make a style object:
public style: {};
public template: string;
ngOnInit(){
this.style = {
template: {
"color": "#D8B088",
}
}
this.template = "<div [ngStyle]='style.template' class='template' style='width: 1080px; height: 1920px; background-color: #212121;'><div class='clr-row' style='padding:45px 0px 10px 25px; position: relative; width: inherit;'><div class='clr-col-5'><div style='width: 230px; height: 60px; background-image: url(*LINK_TO_IMAGE*); background-repeat: no-repeat; float: left;'></div></div></div></div>"
}
I have added the style object to the template by using [ngStyle]='style.template', for some reason the style didn't get loaded, so I tried to use camelCasing instead but still no success.
So does someone know how to get the CSS to work in this case, and eventually use user-defined styles?
Thanks in advance.
Edit 1:
I have also included the Sanitize pipe in the app.module.ts:
#NgModule({
declarations: [
...,
SanitizeHtmlPipe
],
...
});
(for those who were wondering)
Edit 2:
So I have been working out what I kinda want to have with these templates:
A user can register multiple devices of where they want to display the bookings from office 365. A user can setup templates in 2 ways, but this does not matter. When a user wants to display the template for a certain device they go to /device/:deviceid/template/:templateid.
This way the component will load in the template of that device.
So first we load in the device settings which contains the user styles for the template. Afterwards, we load in the data from office365 that has to be displayed in the template and finally load in the template with the template styles.
So there will be 3 requests to the server.
DeviceSettings -- Data Office365 -- Template
So far I have been able to load in the data and place this in the template, but the template was available locally and not from the server.
The reason why I want to have the templates to be requested from the server is that there will be an admin portal where those templates will be made and managed.
These templates will have a name, the HTML and the CSS.
For big template differences you can use Angular CDK Portal: https://material.angular.io/cdk/portal/overview
Example here: https://stackblitz.com/angular/mkvvyvgqxox?file=src%2Fapp%2Fcdk-portal-overview-example.ts
Instead of using [ngStyle] in sanitized HTML, I would instead just change class for dom element, into which sanitized HTML is inserted:
<div [ngClass]="templateClass" [innerHTML]="templateHtml"></div>
In this way code is more readable and styling code is separated from HTML.
Css for templates would look like this:
.template-class-1 {
background-color: #f44336;
}
.template-class-2 {
background-color: #4caf50;
}
Update 14/10/2020:
The previous solution required the compiler to be included that way you couldn't build the project in production mode. Thanks to Owen Kelvins answer it is now possible to add dynamic html and css while still being to build to production since it doesn't require the compiler:
Angular multiple templates in one component based on id (with template store)
For adding custom CSS you can either use Owen Kelvins method or append the "" tag at the end of the html and add in your custom CSS together with the end tag.
Original Answer:
I have found the solution to this subject. Thanks to someone in the discord server "The Coding Den", he messaged me about this and give me a link to Dynamically load template for a component on Github. After scrolling through this long post I found the answer of Alarm9k. This is how I used it to create a component that could display different templates based on a given id through a server request, I have also added some comments to explain it.
import { Component, AfterViewInit, Compiler, NgModule, ViewChild, ViewContainerRef, OnInit } from '#angular/core';
import { CommonModule } from '#angular/common';
import { BookingService } from 'src/app/services/booking.service';
import { ApplicationModel } from 'src/app/models/application.model';
import { Booking } from 'src/app/models/vo/booking';
import { Subscription } from 'rxjs';
import { SplitStringPipe } from '../../utils/split-string.pipe';
import { HttpClientModule } from '#angular/common/http';
import { BrowserAnimationsModule } from '#angular/platform-browser/animations';
import { BrowserModule } from '#angular/platform-browser';
#Component({
selector: 'app-bookings-template',
templateUrl: './bookings-template.component.html',
styleUrls: ['./bookings-template.component.css']
})
export class BookingsTemplateComponent implements AfterViewInit {
public template: string;
public date: Date;
public locale: string;
public id: string;
#ViewChild('container', { read: ViewContainerRef, static: false }) container: ViewContainerRef;
constructor(private compiler: Compiler, private bs: BookingService, private apm: ApplicationModel) { }
ngAfterViewInit() {
// Must clear cache.
this.compiler.clearCache();
// fill in template from server request
this.template = "<div class="test">{{test}}</div>;
var styles = ".test{color:red}";
// Define the component using Component decorator.
const component = Component({
template: this.template + "<div>Hard Coded html for error checks and loading spinner</div>",
styles: [styles]
})(class implements OnInit {
//example properties
public date: Date;
public bookings: Array<Booking>;
public isLoading: boolean = true;
public hasError: boolean = false;
public errorMessage: string;
public errorMessageSub: Subscription;
public bs: BookingService;
public apm: ApplicationModel;
// Do not pass any parameters in the constructor or it will break!
// Instead pass it within the factory method down below as a property!
constructor() {
// refresh template every minute
setInterval(() => {
this.ngOnInit();
}, 60000);
// refresh date every second
setInterval(() => {
this.date = new Date();
}, 1000);
}
ngOnInit() {
// get data to fill in template
}
ngOnDestroy() {
//remove error subscription
this.errorMessageSub.unsubscribe();
}
});
// Define the module using NgModule decorator.
//Modules can be changed based on your needs
const module = NgModule({
imports: [
CommonModule,
BrowserAnimationsModule,
BrowserModule,
HttpClientModule],
declarations: [component, SplitStringPipe],
providers: [BookingService]
})(class { });
// Asynchronously (recommended) compile the module and the component.
this.compiler.compileModuleAndAllComponentsAsync(module)
.then(factories => {
// Get the component factory.
const componentFactory = factories.componentFactories[0];
// Create the component and add to the view.
const componentRef = this.container.createComponent(componentFactory);
// pass parameters that would go in the constructor as properties
// subscriptions should also work.
componentRef.instance.bs = this.bs;
componentRef.instance.apm = this.apm;
componentRef.instance.errorMessageSub = this.apm.getMessageError().subscribe(me => componentRef.instance.errorMessage = me);
});
}
}
The BookingsTemplateComponent acts as the parent of the anonymous component class which acts as the child. This way the child can be added to the parent thanks to #ViewChild where the container name is specified and matches with the parent html id:
<div #container></div> (in this case).
You will also need to add some things to the app module:
import { NgModule, CompilerFactory, Compiler, COMPILER_OPTIONS } from '#angular/core';
import { JitCompilerFactory } from '#angular/platform-browser-dynamic';
import { CommonModule } from '#angular/common';
export function createCompiler(compilerFactory: CompilerFactory) {
return compilerFactory.createCompiler();
}
#NgModule({
declarations: [
// components and pipes
...
],
imports: [
CommonModule, // required
... //other modules
],
providers: [
// different services
...,
// these are need to add the compiler manually to the project
{ provide: COMPILER_OPTIONS, useValue: {}, multi: true },
{ provide: CompilerFactory, useClass: JitCompilerFactory, deps: [COMPILER_OPTIONS] },
{ provide: Compiler, useFactory: createCompiler, deps: [CompilerFactory] }
],
bootstrap: [AppComponent]
})
export class AppModule { }
WARNING:
The most important factor of this is that you cannot build the project in production mode. The reason for this is because JIT compilation doesn't work and you will get the following error:
This is because the angular compiler is not included in the production environment, even when you try to add it manually.
I want to understand that if I create two style sheets
Style 1
.heading {
color: green;
}
Style 2
.heading {
color: blue;
}
Now if these two styles are written in two different views, when rendering them
on a layout as a Partial View, then in this case a conflict could occur
and one could override the style of the other.
BUT
Using angular(see page 16), how come these two styles in different components render on the same page with encapsulation? How come the CSS is not overriden?
For example
import { Component } from '#angular/core';
#Component({
selector: 'app-user-item',
template: '<p class="heading">abc</p>',
styleUrls: ['./user-item.css']
})
export class UserItemComponent implements OnInit {
constructor() {}
ngOnInit() {}
}
user-item.css
.heading{ color :green}
app-user.component.ts
import { Component } from '#angular/core';
#Component({
selector: 'app-user',
template: '<p class="heading">abc</p>',
styleUrls: ['./user.css']
})
export class UserItemComponent implements OnInit {
constructor() {}
ngOnInit() {}
}
user.css
.heading{ color :blue}
When rendering this on a page:
<app-user></app-user>
<app-user-item></app-user-item>
This is the result:
Angular (by default) emulates a shadow DOM.
It dynamically creates some HTML attributes that are only applicable to elements in that component.
For example:
<app-user></app-user>
<app-user-item></app-user-item>
will be transformed to
<app-user _ngcontent-1></app-user>
<app-user-item _ngcontent-2></app-user-item>
And your css will be transformed to:
.heading[_ngcontent-1] { color: blue }
.heading[_ngcontent-2] { color: green }
You can find a more complete explanation here and the documentation here
Angular comes with CSS encapsulation out of the box. When generating a new project, the default is for the styles.css file at the root of the project to apply globally to the application, and for component styles, such as the styles found in foo.component.css,to only apply to the foo component and nowhere else. But that is not the only way styles can be encapsulated in Angular, let us take a closer look.#Component({
selector: 'app-foo',
templateUrl: './foo.component.html',
styleUrls: ['./foo.component.css']
})
<template>
<header>
<hamburger></hamburger>
<app-title></app-title>
<lives></lives>
</header>
</template>
<script>
export default {
name: 'Titlebar',
data() {
return {
}
}
}
</script>
<style lang="scss" scoped>
#import "../styles/variables";
header {
padding: $padding-titlebar;
position: relative;
}
lives {
position: absolute;
right: 2.5vh;
}
</style>
Is it possible to use component tags like any regular HTML tag for styling purposes like I've written down there in lives { }?
I see that that writing <lives class="lives"> and using .lives { } in css works but that seems kinda redundant, would rather like to ommit adding aditional classes if it's possible to just use component tag.
I understand that Vue compiles <lives> into HTML code and that there is no "lives" tag for css to use after it's compiled, but still wondering.
Vue component's name has no relationship with css rule, but it's template does.
Vue component has a template property, it contains html tag. and also, you can use custom html tag in template. for example:
template: `<lives class="lives">{{a}}</lives>`
so now you can define css rule by lives tag.
You can use vue-custom-element:
https://github.com/karol-f/vue-custom-element
First register your component with:
Vue.customElement('widget-vue', {
props: [
'prop1',
'prop2',
'prop3'
],
data: {
message: 'Hello Vue!'
},
template: '<p>{{ message }}, {{ prop1 }}, {{prop2}}, {{prop3}}</p>'
});
Then you can do:
widget-vue {
display: block;
background: red;
...
}
If you want to register a Single File Component, register your own like this:
import TopBar from '#/components/core/TopBar'
Vue.customElement('top-bar', TopBar)
I hope it helps, Cheers!
I have a generic component I want to re-use throughout my app. The problem is that I want to style it differently for various parts of the site. Is this possible?
I'm guessing there's a way to pass in a path for the styleUrl, but that seems really messy and i'm hoping there's a better alternative.
I also tried this but it didn't work: When specifying the component, add in the class, something like this
<generic-component class="customStyle1"></generic-component>
and then added styling based on customStyle1 into generic-component's stylesheet, but it didn't seem to pick up on the style.
You may use :host-context in the style to theme your component based upon some class applied where it is being used.
Read more about it here!!
test.css
:host-context(.theme-green) h3 {
background-color: green;
}
:host-context(.theme-red) h3 {
background-color: red;
}
app.component
import { Component } from '#angular/core';
#Component({
selector: 'my-app',
template: `
<h3 class="title">Basic Angular 2</h3>
<my-test class="theme-green" ></my-test>
<my-test class='theme-red' ></my-test>
`
})
export class AppComponent {
constructor(){}
}
test.component
#Component({
selector: 'my-test',
template: `<h3>Test Component</h3>
`,
styleUrls : ['./app/test.css']
})
export class TestComponent {
constructor(){}
}
Here is the Plunker!!
Hope this helps!!
You can use the :host(...) selector combined with #HostBinding() like:
#Component({
selector: 'my-comp',
styles: `
:host([type-default]) {
background-color: red;
}
:host([type-header]) {
background-color: blue;
}
:host([type-main]) {
background-color: green;
}
`
})
export class MyComponent {
#Input()
#HostBinding('component-type')
componentType:String="type-default"
}
and then switch style like
<header>
<my-comp componentType="type-header"></my-comp>
</header>
<main>
<my-comp componentType="type-main"></my-comp>
</main>
<my-comp></my-comp>
You can also apply a class from the outside like in your question and then use the :host(...) selector like
:host(.customStyle1) {
Then you don't need this part
#Input()
#HostBinding('component-type')
componentType:String="type-default"
but this way might be beneficial if you want to combine styling with other configuration settings for the component.