I'm learning Angular, and I'm working on a project ,in which, I need to use a CSS grid layout. However, I'm trying to find a way to insert a component inside a grid with given grid-area.
I tried to do this, <app-slots></app-slots>, in app.component.html but the component <app-slots> was counted as one grid place only; even though, it's 42 places.
slots.component.html:
<div class="abc" *ngFor="let in of getArrayOfNumbers(42) ;let i = index" [style.grid-row] = "i+1" style = "height:20px" > {{in}} </div>
slots.component.ts:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-slots',
templateUrl: './slots.component.html',
styleUrls: ['../../app.component.css']
})
export class SlotsComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
getArrayOfNumbers(x: number){
var slots:number[] = [];
var a: number = x;
while(x != 0){
slots.push(x);
x--;
}
return slots;
}
}
Note: If something is not clear please tell me to add more info
can you just insert the component between your tags (instead of {{in}}), then send whatever updating variables from the .ts file through that using angular's binding feature ?
two way binding
Related
I want to allow users to customize colors and some styles in Angular application. For that I want to make something like this
Structure:
component-one
folder-with-css-files
style-for-component-1-for-client1.css
style-for-component-1-for-client2.css
style-for-component-1-for-client3.css
style-for-component-1-for-client4.css
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.style-for-component-1-for-client{clientId}.css'],
})
export class AppComponent implements OnInit {
clientId: string;
ngOnInit(): void {
// pseudocode
clientId = service.fetchClientId() // for example
}
}
How I can achieve that? I want to have some css files for every component and depend on user id I want to assign them to styleUrls. Someone can tell me how to do it?
You can use scss and wrap every stylesheet in a different class then based on the user’s selection change the class of your body.
Something like:
DarkMode.scss
.darkMode{
your first css here…
}
LightMode.scss
.lightMode{
your second css here…
}
index.html:
<body class="lightMode"></body>
Doing the above will by default apply the lightMode styling and if you remove the classes of your body in any of your components and replace it with darkMode then the darkMode styling will apply.
Adding/Removing class to/from body:
constructor(private renderer: Renderer2) {}
addClass(){
this.renderer.addClass(document.body, 'className');
}
removeClass(){
this.renderer.removeClass(document.body, 'className');
}
Adding/Removing the class does not have to be only in the index.html file, all you need to do is have a tag with a certain class that wraps around your whole code. For example in one of your components:
component.html:
<div [class]="className">
the rest of your html here...
</div>
component.scss:
.greenDefault{
green default css here...
}
.redDefault{
red default css here...
}
.greyDefault{
grey default css here...
}
component.ts
className: string;
changeStyle(class: string){
this.className = class;
}
Does anyone have a complete example of a custom filter for the clarity datagrid? The custom filter documentation is lacking and I can't figure out how to get their example to work. Better yet, a stackblitz for the full datagrid demo would be amazing!
Hope this helps:
1) My Field model field.ts
export interface Field {
field_nbr: number;
fieldType: string;
dataType: string;
}
2) Utility file util-filters.ts -
import {ClrDatagridStringFilterInterface} from "#clr/angular";
//Models
import { Field } from '../models/field';
/**
* Class for filtering Field metadata in datagrids on filterType property of model
*/
export class FieldTypeFilter implements ClrDatagridStringFilterInterface<Field> {
accepts(field: Field, search: string):boolean {
return "" + field.fieldType == search
|| field.fieldType.toLowerCase().indexOf(search) >= 0;
}
}
3) model-component.html
<clr-datagrid [(clrDgSingleSelected)]="singleSelected" [clDgRowSelection]="false">
<clr-dg-column >
Field Type
<clr-dg-string-filter [clrDgStringFilter]="fieldTypeFilter"></clr-dg-string-filter>
</clr-dg-column>
<clr-dg-column ><ng-container *clrDgHideableColumn="{hidden: false}">Data Type</ng-container></clr-dg-column>
<clr-dg-placeholder>No matching fields found</clr-dg-placeholder>
<clr-dg-row *clrDgItems="let field of allFields" [clrDgItem]="field" (click)='openModal(field)'>
<clr-dg-cell>{{field.fieldType}}</clr-dg-cell>
<clr-dg-cell>{{field.dataType}}</clr-dg-cell>
</clr-dg-row>
</clr-datagrid>
4) model-component.ts
import { Component, OnInit, ViewChild } from '#angular/core';
import { NgIf } from '#angular/common';
import { Wizard } from "#clr/angular";
import { Observable } from 'rxjs';
//Models
import { Field } from '../models/field';
//Utilities
import { FieldTypeFilter } from "../utils/field-filters";
#Component({
selector: 'model',
templateUrl: './model.component.html',
providers: [],
styleUrls: ['../app.component.css']
})
export class ModelComponent {
private fieldTypeFilter = new FieldTypeFilter;
....
}
Edit 01/14/2022
Sorry, I moved some things around on stackblitz and we have updated the repo since this was written.
Here is where the custom filter lives for the demo: https://github.com/vmware/clarity/blob/angular/src/app/datagrid/utils/color-filter.ts
I'm not posting source code because there are 12 different files and some are pretty long (> 100 loc).
Here is a working reproduction of the full demo in the Clarity docs: https://stackblitz.com/edit/full-datagrid-demo
If you ever have questions about how a Clarity component works you can always dive into the source code for the demos we use for development and testing. Take a look here, I linked to the dev app we use for dev/testing so you know where I got the full datagrid code from. https://github.com/vmware/clarity/tree/master/src/dev/src/app
I'm quite new with Angular and i'm trying to create a registration form using Angular and Bootstrap 4.
The result i'd like is to use the styles of Bootstrap with the validation of Angular.
More precisely, when validating the form, Angular applies styles (ng-valid, ng-invalid, etc.) in two different places: the input element and the form element.
Two questions:
1) As Bootstrap uses 'has-danger' and 'has-success' instead of 'ng-[in]valid', is it possible to configure angular to use these styles instead of the default one. Currently, i'm considering extending bootstrap by adding the angular styles (with #extend has-danger/success)
2) Angular applies the style to the input and form elements whereas bootstrap expects it on the form-group element. Is it possible to have angular put the style there instead of the input element (or both?)
I'm using reactive forms and i'd like to avoid things like (not tested):
<form>
<div class="form-group" [class.has-error]="!fg.get('username').valid" [class.has-success]="fg.get('username').valid">
<label>Username</label>
<input formControlName="username" type="text"/>
</div>
</form>
Is there a simple way (not too verbose) of achieving this?
If you're using SASS you can do the following with out needing to rewrite all the css.
.ng-touched.ng-invalid {
#extend .is-invalid;
}
Note: you'll need to be importing bootstrap as part of your SASS build instead of reference it directly.
If you're not using SASS it's pretty to install see here
Angular CLI SASS options
Another option is this directive:
import {Directive, HostBinding, Self} from '#angular/core';
import {NgControl} from '#angular/forms';
#Directive({
selector: '[formControlName],[ngModel],[formControl]',
})
export class BootstrapValidationCssDirective {
constructor(#Self() private cd: NgControl) {}
#HostBinding('class.is-invalid')
get isInvalid(): boolean {
const control = this.cd.control;
return control ? control.invalid && control.touched : false;
}
}
It simply adds the is-invalid class to each field, if the field is touched or invalid. It basically behaves the same as Oliver's SASS-solution, but get's along without SASS and might also have a smaller compiled output.
The best idea that came to me while looking at the angular docs is to use a directive.
My implementation works only with Reactive forms and if the element you want to apply the style contains the form control (which, if you use bootstrap is the case). Should be extended for compatibility with select and textarea.
import { Directive, ElementRef, Input, OnInit } from '#angular/core';
import { FormControl, FormGroup } from '#angular/forms'
#Directive({ selector: '[formValidationStyle]' })
export class FormValidationStyleDirective implements OnInit {
#Input('formValidationStyle') private formGroup: FormGroup;
private component: FormControl;
static VALID_STYLE: string = 'has-success';
static INVALID_STYLE: string = 'has-danger';
constructor(private el: ElementRef) { }
ngOnInit(): void {
let componentName: string;
let inputElement = this.el.nativeElement.querySelector('input');
if (inputElement) {
componentName = inputElement.getAttribute('formControlName');
}
if (!componentName) {
console.error('FormValidationStyleDirective: Unable to get the control name. Is the formControlName attribute set correctly?')
return;
}
let control = this.formGroup.get(componentName)
if (!(control instanceof FormControl)) {
console.error(`FormValidationStyleDirective: Unable to get the FormControl from the form and the control name: ${componentName}.`)
return;
}
this.component = control as FormControl;
this.component.statusChanges.subscribe((status) => {
this.onStatusChange(status);
});
this.onStatusChange(this.component.status);
}
onStatusChange(status: string): void {
let cl = this.el.nativeElement.classList;
if (status == 'VALID') {
cl.add(FormValidationStyleDirective.VALID_STYLE)
cl.remove(FormValidationStyleDirective.INVALID_STYLE)
} else if (status == 'INVALID') {
cl.add(FormValidationStyleDirective.INVALID_STYLE)
cl.remove(FormValidationStyleDirective.VALID_STYLE)
}
}
}
Example:
The component:
#Component({
selector: 'security-register',
templateUrl: './register.component.html'
})
export class RegisterComponent {
registerForm: FormGroup;
constructor(private http: Http, private fb: FormBuilder) {
this.registerForm = this.fb.group({
username: ['', Validators.required]
});
}
}
And its template:
<form [formGroup]="registerForm" novalidate>
<div class="form-group" [formValidationStyle]="registerForm">
<label class="form-control-label" for="dbz-register-username">Login</label>
<input formControlName="username" type="text" class="form-control" id="dbz-register-username" required>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">Register</button>
</div>
</form>
Is it possible to dynamically inject urls to stylesheets into a component?
Something like:
styleUrls: [
'stylesheet1.css',
this.additionalUrls
]
or (wishful thinking and note that this is just fake code):
export class MyComponent implements dynamicUrls {
ngDynamicUrls() {
this.inject(['anotherStylesheet.css', 'anotherStylesheet2.css']);
}
}
Because if you're gonna be able to override certain styles from stylesheet1 without having access to it, how are you supposed to do that? My only idea is to have dynamic styleUrls somehow but I don't think this is even possible from what I could find.
Any ideas?
It's possible in some maybe hack way it worked for me. You can manipulate Angular 2 Component decorator, create your own and return Angular's decorator with your properties.
import { Component } from '#angular/core';
export interface IComponent {
selector: string;
template?: string;
templateUrl?: string;
styles?: string[];
styleUrls?: string[];
directives?: any;
providers?: any;
encapsulation?: number;
}
export function CustomComponent( properties: IComponent): Function {
let aditionalStyles: string;
try {
aditionalStyles = require(`path to aditional styles/${ properties.selector }/style/${ properties.selector }.${ GAME }.scss`);
properties.styles.push(aditionalStyles);
} catch (err) {
console.warn(err)
}
}
return Component( properties );
}
And in your component replace default angular 2 #Component with new one.
import { CustomComponent } from 'path to CustomComponent';
#CustomComponent({
selector: 'home',
templateUrl: './template/home.template.html',
styleUrls: [ './style/home.style.scss']
})
export class ......
I have found a solution:
1. I have changed the styleurls to styles in the component decorator.
2. I will get my variable.
3. I will use the require command in my decorator.
import { Component } from '#angular/core';
import { environment } from '../environments/environment';
let lang = environment['lang'];
#Component({
selector: 'app',
templateUrl: './app.component.html',
styles: [require('./app.component.css'), require('./app.component.' + lang + '.css')]
})
export class AppComponent {
constructor() { }
}
In this basic example I have imported the environment variable and changed the css string dynamically.
I used to have the same need to dynamically inject urls to stylesheets and eventually ended to initialize a component for each variable css (in my case 3 differents styles) with empty template and use them in my app component with ngIf condition.
Thanks to the use of property "encapsulation: ViewEncapsulation.None", the style of the selected component is then added to the header of the page and enable to get the correct renderer for the whole page.
I don't think you can have dynamic stylesheet URLs, because you cannot access your class property or method inside #Component decorator.
But you can achieve your goal by having dynamic style classes in your template.
I used the stylesheet link in the html template with ngIf condition, it worked for me.
<link rel='stylesheet' href="/stylesheets/classicTheme.css" *ngIf="theme === 'classic' " />
<link rel='stylesheet' href="/stylesheets/styleTheme.css" *ngIf="theme === 'style' " />
I'm trying to call functions on an element declared in my Angular 2 component.
The issue is that I don't know how to retrieve the element from my JS code.
If I can pass the element from the template to the JS code, it works, but
using document.querySelector does not return anything.
Example code (plunk):
#View({
template: `
<div>
<span id="p1">{{name}}</span>
<span #p2>{{name}}</span>
</div>
`,
encapsulation: ViewEncapsulation.Native
})
export class Person {
sayHello(e) {
p1 = document.querySelector('p1');
console.log('p1:', p1)
p2 = document.querySelector('p2');
console.log('p2:', p2)
alert('VanillaId: ' + (p1 ? p1.innerHTML : 'null') +
'\nAngularId: ' + (p2 ? p2.innerHTML : 'null'));
}
}
I suspect that it has something to do with shadow dom, but I don't know how to
get the shadow root and use it to do the query. this doesn't seem to expose
anything useful to access the dom.
Use ElementRef see it here http://plnkr.co/edit/LISYnq?p=preview
I did play around with your plunker:
I don't know how to retrieve the element from my JS code
It strikes me you might be able to just setup your component state in your js code and then mutate/display it usingg property binding, and communicate in/out with events.
If you provide a more specific use case maybe we can offer more advice. Anyway, heres the code:
person.ts
//a simple person component
import {Component, View, ViewEncapsulation, Input, ElementRef} from 'angular2/angular2'
#Component({
selector: 'my-person',
inputs: ['name'],
template: `
<pre>
<span> (Unsuspecting shadow dom node minding its own business)</span>
<span #p0el> Name : {{name}}</span>
<span #p1el> Passed in : {{p1}}</span>
</pre>
`,
encapsulation: ViewEncapsulation.Native
})
export class Person {
public p1:string = "...";
#Input('name') name:string;
constructor (elementRef: ElementRef) {
this.elementRef = elementRef;
}
sayHello(str) {
this.p1 = str;
this.elementRef.nativeElement.shadowRoot.querySelector('span').textContent = "BAM!"
}
}
app.ts
//our root app component
import {Component, View, CORE_DIRECTIVES, ViewEncapsulation} from 'angular2/angular2'
import {Person} from './person'
#Component({
selector: 'my-app',
template: `
<div>
<!-- Passing the element here works fine -->
<button (click)="person.sayHello('Clicked!') && person.name = 'Clicky name'">Test</button>
<my-person #person [name]="'World'"></my-person>
</div>`,
directives: [CORE_DIRECTIVES, Person],
encapsulation: ViewEncapsulation.Native
})
export class App {
test(personComponent: Person) {
}
}