Add content from cmds. and GitHub repo notes
This commit is contained in:
parent
234851aa91
commit
79cb0aafbe
8
.editorconfig
Normal file
8
.editorconfig
Normal file
|
@ -0,0 +1,8 @@
|
|||
# EditorConfig is awesome: https://EditorConfig.org
|
||||
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
insert_final_newline = true
|
|
@ -1,4 +1,4 @@
|
|||
# Angular Notes
|
||||
# Angular Framework Notes
|
||||
Notes on developing with the Angular framework.
|
||||
|
||||
Powered by mdBook.
|
||||
|
|
|
@ -4,3 +4,8 @@ language = "en"
|
|||
multilingual = false
|
||||
src = "src"
|
||||
title = "Angular Framework Notes"
|
||||
|
||||
[output.html]
|
||||
git-repository-url = "https://gitlab.com/strivinglife/book-angular"
|
||||
edit-url-template = "https://gitlab.com/strivinglife/book-angular/-/edit/main/{path}"
|
||||
git-repository-icon = "fa-gitlab"
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
# Summary
|
||||
|
||||
- [Chapter 1](./chapter_1.md)
|
||||
- [Basics](./basics.md)
|
||||
- [Testing](./testing.md)
|
||||
- [Course notes]()
|
||||
- [Angular: Getting Started](./courses/angular-getting-started.md)
|
||||
- [Angular Component Communication](./courses/angular-component-communication.md)
|
||||
- [Related: Creating Layouts with CSS Grid](./courses/related/creating-layouts-css-grid.md)
|
||||
|
|
76
src/basics.md
Normal file
76
src/basics.md
Normal file
|
@ -0,0 +1,76 @@
|
|||
# Basics
|
||||
Written as of Angular 12+.
|
||||
|
||||
## Install the Angular CLI
|
||||
```bash
|
||||
npm install -g @angular/cli
|
||||
```
|
||||
|
||||
## Help/available commands
|
||||
```bash
|
||||
ng help
|
||||
ng ___ --help
|
||||
```
|
||||
|
||||
## Create a new site/project
|
||||
```bash
|
||||
ng new new-project-name
|
||||
# This option can be used if there's an issue during npm install.
|
||||
ng new new-project-name --skip-install
|
||||
# Use a custom prefix for selector names.
|
||||
ng new new-project-name --prefix ___
|
||||
```
|
||||
|
||||
## Start a development server
|
||||
Runs on port 4200 by default.
|
||||
|
||||
```bash
|
||||
ng serve
|
||||
# Opens the site in the default browser.
|
||||
ng serve --open
|
||||
ng serve -o
|
||||
```
|
||||
|
||||
## Code scaffolding
|
||||
See [ng generate](https://angular.io/cli/generate).
|
||||
|
||||
```bash
|
||||
# Create a new component.
|
||||
ng generate component heroes
|
||||
ng generate component hero-detail
|
||||
ng g c input-button-unit
|
||||
ng g c products/product-detail --flat
|
||||
ng generate component messages
|
||||
# Create a new service.
|
||||
ng generate service hero
|
||||
ng generate service message
|
||||
ng g s services/todo-list
|
||||
# Create an interface
|
||||
ng g i interfaces/todo-item
|
||||
# Create a new module, putting it into AppModule.
|
||||
ng g m products/product --flat -m app
|
||||
```
|
||||
|
||||
## Build the project
|
||||
```bash
|
||||
# Puts in dist/ by default.
|
||||
ng build
|
||||
```
|
||||
|
||||
## Add an in-memory web API
|
||||
For mocking up a backend.
|
||||
|
||||
|
||||
```bash
|
||||
npm install angular-in-memory-web-api --save
|
||||
```
|
||||
|
||||
```bash
|
||||
# Generate a module in src/app (flat) and register it in AppModule imports (module=app)
|
||||
ng generate module app-routing --flat --module=app
|
||||
ng generate component dashboard
|
||||
# In-memory Web API
|
||||
npm install angular-in-memory-web-api --save
|
||||
ng generate service InMemoryData
|
||||
ng generate component hero-search
|
||||
```
|
|
@ -1 +0,0 @@
|
|||
# Chapter 1
|
489
src/courses/angular-component-communication.md
Normal file
489
src/courses/angular-component-communication.md
Normal file
|
@ -0,0 +1,489 @@
|
|||
# Angular Component Communication
|
||||
My notes from Deborah Kurata's excellent [Angular Component Communication](https://www.pluralsight.com/courses/angular-component-communication).
|
||||
|
||||
# Module 3
|
||||
## Binding and structural directives
|
||||
### Interpolation
|
||||
```typescript
|
||||
// Component
|
||||
pageTitle: string = 'Product List';
|
||||
```
|
||||
```html
|
||||
<!-- Template -->
|
||||
<div>{{pageTitle}}</div>
|
||||
```
|
||||
|
||||
Can also use functions (`{{ getPageTitle() }}`) but may run into performance issues due to how Angular checks for changes.
|
||||
|
||||
### Property binding
|
||||
```typescript
|
||||
// Component
|
||||
imageWidth: number = 50;
|
||||
```
|
||||
```html
|
||||
<!-- Template -->
|
||||
<img [style.width.px]="imageWidth" />
|
||||
```
|
||||
|
||||
### Event binding
|
||||
```typescript
|
||||
// Component
|
||||
toggleImage(): void {
|
||||
this.showImage = !this.showImage;
|
||||
}
|
||||
```
|
||||
```html
|
||||
<!-- Template -->
|
||||
<button (click)="toggleImage()">Toggle Image</button>
|
||||
```
|
||||
|
||||
### Two-way binding
|
||||
```typescript
|
||||
// Component
|
||||
listFilter: string;
|
||||
```
|
||||
```html
|
||||
<!-- Template -->
|
||||
<input type="text" [(ngModel)]="listFilter" />
|
||||
```
|
||||
|
||||
### *ngIf
|
||||
```typescript
|
||||
// Component
|
||||
showImage: boolean = false;
|
||||
```
|
||||
```html
|
||||
<!-- Template -->
|
||||
<img *ngIf="showImage" [src]="product.imageUrl" />
|
||||
```
|
||||
|
||||
### *ngFor
|
||||
```typescript
|
||||
// Component
|
||||
products: IProduct[];
|
||||
```
|
||||
```html
|
||||
<!-- Template -->
|
||||
<tr *ngFor="let product of products">
|
||||
```
|
||||
|
||||
### Two-way binding, long way
|
||||
`[(ngModel)]="listFilter"` is shorthand for `[ngModel]="listFilter" (ngModelChange)="listFilter=$event"`.
|
||||
|
||||
So you could do `[ngModel]="listFilter" (ngModelChange)="onFilterChange($event)"`, where `onFilterChange` updates `listFilter`.
|
||||
|
||||
### Getters and setters
|
||||
```typescript
|
||||
// Component
|
||||
listFilter: string;
|
||||
```
|
||||
|
||||
```typescript
|
||||
// Component
|
||||
private _listFilter: string;
|
||||
get listFilter(): string {
|
||||
return this._listFilter;
|
||||
}
|
||||
set listFilter(value: string) {
|
||||
this._listFilter = value;
|
||||
}
|
||||
```
|
||||
|
||||
# Module 4
|
||||
In my opinion, using these has more downsides than benefits.
|
||||
|
||||
## `ViewChild`
|
||||
|
||||
```
|
||||
// Angular Directive
|
||||
@ViewChild(NgModel) filterInput: NgModel; // <input type="text" [(ngModel)]="listFilter" />
|
||||
// Custom Directive or Child Component
|
||||
@ViewChild(StarComponent) star: StarComponent;
|
||||
// Template Reference Variable
|
||||
@ViewChild('divElementVar') divElementRef: ElementRef; // <div #divElementVar>{{pageTitle}}</div>
|
||||
// Above is available during/after ngAfterViewInit(), which is after constructor() and ngOnInit().
|
||||
// However, if within a *ngIf you may run into an issue.
|
||||
// ElementRef has a nativeElement which allows for access to any HTML element properties or methods.
|
||||
@ViewChild(NgForm) editForm: NgForm; // if using template-driven forms.
|
||||
```
|
||||
|
||||
With `NgModel` we can for example:
|
||||
```
|
||||
@ViewChild(NgModel) filterInput: NgModel;
|
||||
this.filterInput.valueChanges.subscribe(() => this.performFilter(this.listFilter));
|
||||
```
|
||||
|
||||
Otherwise, `NgModel` and `NgForm` are read-only.
|
||||
|
||||
## `ViewChildren`
|
||||
```
|
||||
@ViewChildren(NgModel) inputs: QueryList<NgModel>;
|
||||
// Above would support checking for status.
|
||||
@ViewChildren(StarComponent) stars: QueryList<StarComponent>;
|
||||
@ViewChildren('divElementVar' divElementRefs: QueryList<ElementRef>;
|
||||
@ViewChildren('filterElement, nameElement' divElementRefs: QueryList<ElementRef>;
|
||||
// Tracks changes in the DOM.
|
||||
this.divElementRefs.changes.subscribe(() => { /* act */ });
|
||||
```
|
||||
|
||||
# Module 5
|
||||
## Parent to child component communication
|
||||
|
||||
- Child: `@Input`, getters/setters, `OnChanges`
|
||||
- Parent: Template reference variable, `@ViewChild`
|
||||
- Use a service
|
||||
|
||||
## `@Input()`
|
||||
Child:
|
||||
```typescript
|
||||
@Input() propertyName: string;
|
||||
```
|
||||
|
||||
Parent:
|
||||
```html
|
||||
<app-child propertyName="binding source"></app-child>
|
||||
```
|
||||
|
||||
Or:
|
||||
```typescript
|
||||
parentProperty: string;
|
||||
```
|
||||
```html
|
||||
<app-child [propertyName]="parentProperty"></app-child>
|
||||
```
|
||||
|
||||
## Getter and setter
|
||||
Child component:
|
||||
```typescript
|
||||
private _propertyName: string;
|
||||
get propertyName(): string {
|
||||
rerturn this._propertyName;
|
||||
}
|
||||
@Input() set propertyName(value: string) {
|
||||
this._propertyName = value;
|
||||
}
|
||||
```
|
||||
|
||||
## `OnChanges`
|
||||
Child component:
|
||||
```typescript
|
||||
@Input() propertyName: string;
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
// Note: values start at undefined.
|
||||
if (changes['propertyName']) {
|
||||
changes['propertyName'].currentValue
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Template reference value
|
||||
```html
|
||||
<app-child #childReference [propertyName]="parentProperty"></app-child>
|
||||
|
||||
{{ childReference.propertyName }}
|
||||
{{ childReference.methodName() }}
|
||||
```
|
||||
|
||||
## `@ViewChild`
|
||||
```html
|
||||
<app-child #childReference [propertyName]="parentProperty"></app-child>
|
||||
```
|
||||
```typescript
|
||||
@ViewChild('childReference') childComponent: ChildComponent;
|
||||
```
|
||||
or
|
||||
```html
|
||||
<app-child [propertyName]="parentProperty"></app-child>
|
||||
```
|
||||
```typescript
|
||||
@ViewChild(ChildComponent) childComponent: ChildComponent;
|
||||
parentPropertyName: string;
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
this.parentVariable = this.childComponent.propertyName;
|
||||
}
|
||||
```
|
||||
## Summary
|
||||
1. Use a child component when:
|
||||
- for a specific task
|
||||
- complex
|
||||
- reusable
|
||||
2. `@Input`, getter/setter, and `OnChanges` are easier for parent to child
|
||||
- Favor getter/setter if you only need to react to changes to specific properties
|
||||
- Favor `OnChanges` if you want to react to any input property changes, or if you need current/previous values
|
||||
- The key here is that it's `@Input()` property changes.
|
||||
3. Template reference variable if you want to use it in the parent's template
|
||||
4. `ViewChild` if you want to use it in the class
|
||||
- but it won't receive notification of changes
|
||||
|
||||
# Module 6
|
||||
## Child to parent component communication
|
||||
- Event notification: `@Output`
|
||||
- Provide information: Template reference variable, `@ViewChild`
|
||||
- Service
|
||||
|
||||
## `@Output`
|
||||
Child:
|
||||
```typescript
|
||||
@Output() valueChange = new EventEmitter<string>(); // in @angular/core
|
||||
|
||||
this.valueChange.emit(value);
|
||||
```
|
||||
Parent:
|
||||
```html
|
||||
<app-child (valueChange)="onValueChange($event)"></app-child>
|
||||
```
|
||||
|
||||
```typescript
|
||||
onValueChange(value: string): void {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
# Module 7 - Services
|
||||
## Managing state options
|
||||
From simple to complex:
|
||||
1. Property bag
|
||||
2. Basic state management
|
||||
3. State management with notifications
|
||||
4. ngrx (inspired by Redux)
|
||||
|
||||
## Property bag
|
||||
Service that just contains properties.
|
||||
|
||||
Service:
|
||||
```typescript
|
||||
@Injectable()
|
||||
export class ThingService {
|
||||
propertyName1: string;
|
||||
propertyName2: boolean;
|
||||
}
|
||||
```
|
||||
Component:
|
||||
```typescript
|
||||
get propertyName1(): string {
|
||||
return this.thingService.propertyName1;
|
||||
}
|
||||
set propertyName1(value: string) {
|
||||
this.thingService.propertyName1 = value;
|
||||
}
|
||||
|
||||
constructor(private thingService: ThingService) {
|
||||
}
|
||||
```
|
||||
|
||||
Great for 'stashing away properties for itself or other components.'
|
||||
|
||||
## Service scope
|
||||
Register the service based upon what you want to be able to use it (scope), and how long it is retained for (lifetime).
|
||||
|
||||
- Register in the component - `@Component({ providers: [ ThingService ]})` - for that component and children (template or via router).
|
||||
- Good if you need multiple instances of the service for different component instances.
|
||||
- Register in a module - `@NgModule({ providers: [ ThingService ] })` - no matter which module it's registered in (unless lazy-loaded) it will be available to all components.
|
||||
- Lazy-loaded module services are only available to components declared in that module, but is then available for the entire application lifetime.
|
||||
|
||||
`ngOnDestroy(): void { }` if you want to see the lifetime of a service.
|
||||
|
||||
## Guidelines
|
||||
Property bag for:
|
||||
- Retaining view state
|
||||
- Retaining user selections
|
||||
- Sharing data or other state
|
||||
- Communicating state changes
|
||||
- Okay if any component can read or change the values
|
||||
- Components are only notified of state changes if they use template binding
|
||||
|
||||
# Module 8
|
||||
## Basic state management
|
||||
Essentially store data on the service in a private property, and populate it from the server only when needed.
|
||||
|
||||
1. Provide state values
|
||||
2. Maintain and update state
|
||||
3. Observe state changes
|
||||
|
||||
```typescript
|
||||
@Injectable()
|
||||
export class ThingService {
|
||||
private things: IThing[];
|
||||
|
||||
getThings(): Observable<IThing[]> {
|
||||
if (this.things) {
|
||||
return of(this.things);
|
||||
}
|
||||
// get things from the data store and save to this.things
|
||||
}
|
||||
|
||||
getThing(id: number): Observable<IThing> {
|
||||
if (this.things) {
|
||||
const foundThing = this.things.find(item => item.id === id);
|
||||
if (foundThing) {
|
||||
return of(foundThing);
|
||||
}
|
||||
}
|
||||
// get thing from the server and return it
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For create/update/delete either post to the server and update the item in the property list, or pull fresh content from the server, depending upon need.
|
||||
|
||||
May want to always pull fresh data when doing an edit.
|
||||
|
||||
You may also want to store a pull or expiration date so that you don't have stale data.
|
||||
|
||||
## Concurrent components
|
||||
Could put a public property in a property bag or state management service that could be read/updated.
|
||||
|
||||
In the component that reads the property, use a getter to `return this.thingService.currentThing;` if you want it to update every time `currentThing` is changed.
|
||||
|
||||
- Define a property in the service
|
||||
- Bind that property in a template
|
||||
- Use a getter in the component class
|
||||
|
||||
Note that you'll either need to use binding in the template to have Angular pick up changes or have a timer (`import { timer } from 'rxjs/observable/timer';` has one) in `ngOnInit` that polls for changes. Timers are not ideal.
|
||||
|
||||
```typescript
|
||||
ngOnInit() {
|
||||
timer(0, 1000).subscribe(t => console.log(this.thing));
|
||||
// unsubscribe on destroy
|
||||
}
|
||||
```
|
||||
|
||||
# Module 9
|
||||
## Service notifications
|
||||
Can add notifications to any service, not just a state management service.
|
||||
|
||||
`EventEmitter` only works child to parent, and you don't want to force it for service notifications.
|
||||
|
||||
Use `Subject` or a variant like `BehaviorSubject` instead. `Subject` is a type of `Observable`, and an `Observer`. Don't necessarily need, if you can use binding.
|
||||
|
||||
Service:
|
||||
```typescript
|
||||
@Injectable()
|
||||
export class ThingService {
|
||||
private selectedThingSource = new Subject<IThing | null>();
|
||||
// source = source of knowledge about selected thing
|
||||
selectedThingChanges$ = this.selectedThingSource.asObservable();
|
||||
// $ convention = observable (that can be subscribed to)
|
||||
// could make the source public but then anyone could push to it
|
||||
// instead the asObservable makes it read-only externally
|
||||
|
||||
changeSelectedThing(selectedThing: IThing | null): void {
|
||||
this.selectedThingSource.next(selectedThing);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Component, updating:
|
||||
```typescript
|
||||
// ...
|
||||
this.thingService.changeSelectedThing(thing);
|
||||
// ...
|
||||
```
|
||||
|
||||
Component, subscribing:
|
||||
```typescript
|
||||
export class ThingDetailComponent implements OnInit, OnDestroy {
|
||||
thing: IThing | null;
|
||||
thingSub: Subscription;
|
||||
|
||||
constructor(private thingService: ThingService) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.thingSub = this.thingService.selectedThingChanges$.subscribe(
|
||||
selectedThing => this.thing = selectedThing
|
||||
);
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.thingSub.unsubscribe();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## `BehaviorSubject`
|
||||
Many variants of subject, but this one:
|
||||
- requires an initial value
|
||||
- provides the current value on a new subscription
|
||||
|
||||
Service:
|
||||
```typescript
|
||||
//private selectedThingSource = new Subject<IThing | null>();
|
||||
private selectedThingSource = new BehaviorSubject<IThing | null>(null);
|
||||
```
|
||||
|
||||
Works even when components are destroyed. However, will want to make sure the component that updates also subscribes, if it needs to.
|
||||
|
||||
## Summary
|
||||
Don't need to use `Subject` if notifications are not required, or the only notifications are for changes to bound properties.
|
||||
|
||||
Subjects can also be used to sync multiple observables (advanced, not covered by this course).
|
||||
|
||||
There is a `Subject` variant that can provide all previous messages.
|
||||
|
||||
# Module 10
|
||||
## Route parameters
|
||||
- Required
|
||||
- Optional
|
||||
- Query
|
||||
|
||||
### Required parameters
|
||||
Define:
|
||||
```typescript
|
||||
{ path: 'products/:id', component: ProductDetailComponent }
|
||||
```
|
||||
Activate:
|
||||
```html
|
||||
<a [routerLink]="['/products', product.id]">...</a>
|
||||
```
|
||||
```typescript
|
||||
this.router.navigate(['/products', this.product.id]);
|
||||
```
|
||||
Read:
|
||||
```typescript
|
||||
this.route.snapshot.paramMap.get('id');
|
||||
```
|
||||
|
||||
### Optional parameters
|
||||
In the URL, uses `;` as the delimiter. Can be lost during navigation (versus standard query parameters).
|
||||
|
||||
Define:
|
||||
```typescript
|
||||
{ path: 'products', component: ProductListComponent }
|
||||
```
|
||||
Activate:
|
||||
```html
|
||||
<a [routerLink]="['/products', { name: cart, code: g }]">...</a>
|
||||
```
|
||||
```typescript
|
||||
this.router.navigate(['/products', { name: 'cart', code: 'g' }]);
|
||||
```
|
||||
Read:
|
||||
```typescript
|
||||
this.route.snapshot.paramMap.get('name');
|
||||
```
|
||||
|
||||
### Query parameters
|
||||
In the URL, uses standard `?` and `&`. Can be retained across routes.
|
||||
|
||||
Define:
|
||||
```typescript
|
||||
{ path: 'products', component: ProductListComponent }
|
||||
```
|
||||
Activate:
|
||||
```html
|
||||
<a [routerLink]="['/products']" [queryParams]="{ name: cart, code: g }">...</a>
|
||||
```
|
||||
```typescript
|
||||
this.router.navigate(['/products'], { queryParams: { name: 'cart', code: 'g' }});
|
||||
```
|
||||
Read:
|
||||
```typescript
|
||||
this.route.snapshot.queryParamMap.get('name');
|
||||
```
|
||||
|
||||
## Summary
|
||||
- Simple
|
||||
- Bookmarkable and sharable
|
||||
- Good for small amounts of data
|
4
src/courses/angular-getting-started.md
Normal file
4
src/courses/angular-getting-started.md
Normal file
|
@ -0,0 +1,4 @@
|
|||
# Angular: Getting Started
|
||||
My notes from Deborah Kurata's [Angular: Getting Started](https://www.pluralsight.com/courses/angular-2-getting-started-update).
|
||||
|
||||
[My repo with pull request with training notes](https://github.com/JamesSkemp/Angular-GettingStarted/pull/1).
|
191
src/courses/related/creating-layouts-css-grid.md
Normal file
191
src/courses/related/creating-layouts-css-grid.md
Normal file
|
@ -0,0 +1,191 @@
|
|||
# Related: Creating Layouts with CSS Grid
|
||||
Playing around while following [Matt Henry's course on Pluralsight](https://app.pluralsight.com/library/courses/css-grid-creating-layouts/table-of-contents).
|
||||
|
||||
[My repo from the course](https://github.com/JamesSkemp/css-grid).
|
||||
|
||||
## Notes
|
||||
|
||||
### Basics
|
||||
- grid-column-start
|
||||
- grid-column-end
|
||||
- grid-template-columns
|
||||
- grid-template-rows
|
||||
|
||||
```css
|
||||
html, body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
/* Changes all direct children into grid items. */
|
||||
/* By default 1 column 1 row grid. */
|
||||
display: grid;
|
||||
/* Number of columns and width. */
|
||||
grid-template-columns: 15em auto 15em;
|
||||
/* Number of rows and heights. */
|
||||
/* min-content is minimum amount to fit content, and no more. */
|
||||
/* auto only works here because of 100% height on html and body. */
|
||||
grid-template-rows: min-content auto min-content;
|
||||
/* Above is the same as the following. rows / columns. */
|
||||
/*
|
||||
grid-template: min-content auto min-content / 15em auto 15em;
|
||||
*/
|
||||
/* Can also use the more powerful grid, with just what we want. */
|
||||
/* grid is also recommended, while grid-template is not. */
|
||||
/*
|
||||
grid: min-content auto min-content / 15em auto 15em;
|
||||
*/
|
||||
}
|
||||
|
||||
header, footer {
|
||||
/* Grid line to start at. */
|
||||
grid-column-start: 1;
|
||||
/* Grid line to end at. */
|
||||
grid-column-end: 4;
|
||||
}
|
||||
```
|
||||
|
||||
### Sizing
|
||||
|
||||
Viewport-responsive:
|
||||
- Percentage
|
||||
- fr
|
||||
- These will be min-content until all other tracks reach their growth limit (for example, `minmax()`).
|
||||
- auto
|
||||
- Growth limit is `max-content`.
|
||||
- This means if a `1fr` is used it won't get any larger than `max-content`.
|
||||
|
||||
Content-responsive:
|
||||
- min-content
|
||||
- max-content
|
||||
- fit-content()
|
||||
|
||||
|
||||
- `fr` fractional unit.
|
||||
- `minmax(max-content, 50%)` define minimum and maximum values for a track.
|
||||
- `fit-content(30em)` try to fit the content, but don't go any larger than an amount.
|
||||
- `repeat(3, 20em)` repeats x times a track size of a certain amount.
|
||||
- `repeat(6, 1fr 2fr)`
|
||||
- `repeat(auto-fill, 10em)` fills up available space with tracks of set size.
|
||||
- `repeat(auto-fill, minmax(15em, 1fr))` is an example of combining for a responsive design.
|
||||
- `auto-fit` tries to fill empty columns, versus `auto-fill`.
|
||||
/* Default. Fills it row-by-row. */
|
||||
- `grid-auto-flow: row;` is the default and tries to fill the grid row-by-row.
|
||||
- `grid-auto-flow: column;` fills column-by-column instead.
|
||||
- Alternative is to put it in the `grid` definition. For example `grid: auto-flow 10em / 10em 10em`.
|
||||
- `writing-mode: vertical-rl;` and `writing-mode: horizontal-tb` are examples of ways to get `grid-auto-flow` to start a certain way, but may also make columns look like rows.
|
||||
- `grid-auto-rows` can impact implicit rows created if the grid definition doesn't account for all grid items.
|
||||
- Supports more than one size, in which case it will alternate.
|
||||
- `grid-auto-columns` is the other.
|
||||
- Depends upon `grid-auto-flow` definition.
|
||||
|
||||
#### Determing track size (slide)
|
||||
1. All tracks start at their base size.
|
||||
2. Extra space is allocated evenly to tracks which haven't reached their growth limit.
|
||||
3. Additional remaining space is given to fractional unit (`fr`) tracks.
|
||||
4. If there are no fractional unit tracks, additional space is given to `auto` tracks.
|
||||
|
||||
### Gaps
|
||||
- `column-gap`, `row-gap`, and `gap` (shorthand) are the recommended properties.
|
||||
- Makes it consistent with CSS Grid, Flexbox, and Multi-column Layout.
|
||||
- `grid-` prefix is also acceptable.
|
||||
- `margin` on grid container for outer margin.
|
||||
|
||||
### Positioning
|
||||
- Can use -1 for end position (or any negative numbers) instead of a positive number. Negative numbers count from the end of the row to the start.
|
||||
- However, avoid micromanaging.
|
||||
- On a grid item, can use span x to span a certain number of tracks.
|
||||
- `grid-column-end: span 2;`
|
||||
- `grid-column: start / end;`
|
||||
- `grid-column: 3 / 5;`
|
||||
- `grid-column: 3 / span 2;`
|
||||
- `grid-column: span 2;`
|
||||
- `grid-row`
|
||||
- `grid-area: 2 / 1 / 5 / 6;` is grid row start, column start, row end, column end.
|
||||
- May want to just use `grid-column` and `grid-row` for readability.
|
||||
|
||||
### Alignment
|
||||
- Can align a grid on a web page or the content within a grid cell, and there must be extra space to align within.
|
||||
- Properties:
|
||||
- `justify-content` default is start.
|
||||
- Other options are center, end, space-around, space-between, and space-evenly. Controls entire grid.
|
||||
- `align-content` default is start.
|
||||
- Other options are center, end, space-around, space-between, and space-evenly. Controls entire grid.
|
||||
- `justify-items` default is stretch.
|
||||
- Other options (for this and next three properties) are start, center, and end. Controls content within grid item.
|
||||
- `align-items` default is stretch. Controls content within grid item.
|
||||
- `justify-self` default is stretch. Controls an individual item.
|
||||
- `align-self` default is stretch. Controls an individual item.
|
||||
- Justify = left to right.
|
||||
- Align = top to bottom.
|
||||
|
||||
### Accessibility
|
||||
- Can use `dense` in `grid-auto-flow` and it will try to fill in empty gaps (in the case of spanning grid items).
|
||||
- Can also use `order` to force items before/after items. Default is `order: 0;`.
|
||||
- However, try to keep the source order = display order.
|
||||
- Can also overlaps grid items by overlapping row/column placements. Combine with `z-index` as needed.
|
||||
|
||||
### Naming
|
||||
- When defining track sizes, you can name them.
|
||||
- `grid-template-columns: [left-edge] 1fr 1fr [midpoint] 1fr 1fr [right-edge];`
|
||||
- Then use like `grid-column: left-edge / right-edge;`.
|
||||
- Something like `body { display: grid; grid-template-rows: [header-start] 2em 5em [header-end body-start] 10em 10em [body-end]; }` is a bit more conventional.
|
||||
- Can also use names within `repeat()` which can cause multiple named lines.
|
||||
- When used for grid items, this will cause them to jump to the closest named line.
|
||||
- Can put a number afte the name to use that instance.
|
||||
- Can also use `span x` before to span over multiple, but be careful when repeating.
|
||||
|
||||
#### Simple layouts
|
||||
```
|
||||
body {
|
||||
display: grid;
|
||||
grid-template-areas: "header header header"
|
||||
"nav main aside"
|
||||
"footer footer footer";
|
||||
grid-template-rows: min-content auto min-content;
|
||||
grid-template-columns: 15em 1fr 1fr;
|
||||
}
|
||||
|
||||
header { grid-area: header; }
|
||||
nav { grid-area: nav; }
|
||||
main { grid-area: main; }
|
||||
aside { grid-area: aside; }
|
||||
footer { grid-area: footer; }
|
||||
```
|
||||
|
||||
Good for simple layouts.
|
||||
|
||||
- Must be rectangular shape.
|
||||
- Must have same number of cells.
|
||||
- `...` to define empty areas.
|
||||
- Lines automatically get names based upon area names.
|
||||
- Named lines automatically create named areas.
|
||||
|
||||
Shorter version of the above:
|
||||
|
||||
```
|
||||
body {
|
||||
display: grid;
|
||||
grid: "header header header" min-content "nav main aside" auto "footer footer footer" min-content / 15em 1fr 1fr;
|
||||
}
|
||||
```
|
||||
|
||||
Or to improve readability:
|
||||
|
||||
```
|
||||
body {
|
||||
display: grid;
|
||||
grid: "header header header" min-content
|
||||
"nav main aside" auto
|
||||
"footer footer footer" min-content /
|
||||
15em 1fr 1fr;
|
||||
}
|
||||
```
|
||||
|
||||
### Responsive design
|
||||
- Start small.
|
||||
- Use media queries. :|
|
||||
|
||||
### Subgrids
|
||||
- May not be well supported (as of me typing this, still only Firefox supported).
|
||||
- You can of course put a grid within a grid.
|
13
src/testing.md
Normal file
13
src/testing.md
Normal file
|
@ -0,0 +1,13 @@
|
|||
# Testing
|
||||
## Run unit tests
|
||||
By default uses [Karma](https://karma-runner.github.io/)/[Jasmine](https://jasmine.github.io/).
|
||||
|
||||
```bash
|
||||
ng test
|
||||
```
|
||||
|
||||
## Add eslint
|
||||
See [angular-eslint on GitHub](https://github.com/angular-eslint/angular-eslint) for current information. As of 9/5/2021:
|
||||
```bash
|
||||
ng add @angular-eslint/schematics
|
||||
```
|
Loading…
Reference in New Issue
Block a user