Skip to content

Commit a75347d

Browse files
committed
Add readme to Angular/SVG sample, add comments
1 parent 66e870e commit a75347d

File tree

5 files changed

+85
-24
lines changed

5 files changed

+85
-24
lines changed

Diff for: AngularSvg/readme.md

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Angular and SVG
2+
3+
## Introduction
4+
5+
This sample has originally been developed for [*Magdeburger Dev Days*](https://md-devdays.de/) 2018. The goal is to show how to use SVG in Angular projects.
6+
7+
## Demo Checklist
8+
9+
### SVG Recap
10+
11+
The goal of this project is *not* to do a general SVG introduction. However, some people might not be that familiar with SVG. Therefore, use [*whats-svg.component.html*](src/app/whats-svg/whats-svg.component.html) to recap the SVG concepts that one needs to understand to be able to follow the Angular-related part of that talk.
12+
13+
* External vs. internal SVG sources - internal sources are important for Angular
14+
* Style SVG with CSS
15+
* How to work with [transforms](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/transform)
16+
* Animate SVG with CSS, discuss other options for animations
17+
* [Dashed lines](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-dasharray) in SVG
18+
* [Paths](https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths) and arcs
19+
20+
### Binding
21+
22+
Use [*attr-binding.component.html*](src/app/attr-binding/attr-binding.component.html) to discuss binding topics.
23+
24+
* [Attribute binding](https://angular.io/guide/template-syntax#attribute-binding) instead of property binding
25+
* Show that event binding just works
26+
* Demonstrate the combination of CSS styling of SVG, [class binding](https://angular.io/guide/template-syntax#class-binding), and Angular to build a simple SVG-based control with clear view/logic separation
27+
28+
### Building SVG-based Components
29+
30+
The component in [*budget-chart.component.ts*](src/app/attr-selector/budget-chart/budget-chart.component.ts) shows how to build an SVG-based Angular directive.
31+
32+
* Use of attribute selector in component
33+
* `svg:` namespace for identifying SVG elements in Angular templates
34+
* `@Input` decorator for inpupt parameters
35+
* `:host ::ng-deep` in CSS (see [*project-list.component.css*](src/app/attr-selector/project-list/project-list.component.css))
36+
* Use component in parent SVG (see [*project-list.component.html*](src/app/attr-selector/project-list/project-list.component.html)) including data binding in a structural directive
37+
38+
### Animations
39+
40+
The last sample brings everything together. It implements a silo element in a ficticious IoT visualization library. The element [*silo*](src/app/iot/silo) is a reusable component, uses Angular animations and supports context menus.
41+
42+
* Context menu with [*ngx-contextmenu*](https://github.com/isaacplmann/ngx-contextmenu/)
43+
* Use of [Angular Animations](https://angular.io/guide/animations) with SVG
44+
* Using the component in a parent view (see [*animation.component.ts*](src/app/animation/animation.component.ts))

Diff for: AngularSvg/src/app/animation/animation.component.ts

+15-7
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,27 @@ import { Component, OnInit } from '@angular/core';
66
<h1 class="mat-headline">Animation</h1>
77
88
<svg id="silo" width="152" height="122">
9-
<g app-silo />
9+
<g class="critical" app-silo [fill]="30" [maxFill]="100" />
10+
<g class="ok" app-silo [fill]="75" [maxFill]="100" transform="translate(75, 0)" />
1011
</svg>
1112
`,
1213
styles: [`
13-
:host >>> .silo-fill {
14+
:host >>> .critical .silo-fill {
1415
fill: red;
15-
}`]
16-
})
17-
export class AnimationComponent implements OnInit {
16+
}
1817
19-
constructor() { }
18+
:host >>> .critical .material-flow {
19+
stroke: red;
20+
}
2021
21-
ngOnInit() {
22+
:host >>> .ok .silo-fill {
23+
fill: green;
2224
}
2325
26+
:host >>> .ok .material-flow {
27+
stroke: green;
28+
}
29+
`]
30+
})
31+
export class AnimationComponent {
2432
}

Diff for: AngularSvg/src/app/attr-selector/budget-chart/budget-chart.component.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { Component, OnInit, Input } from '@angular/core';
22

33
@Component({
4+
// Note the attribute selector here. This is necessary because Angular
5+
// would otherwise destroy the SVG code by adding an unknown element.
46
selector: '[app-budget-chart]',
57
template: `
68
<svg:line x1="0" y1="5" [attr.x2]="width" y2="5" class="background-line" />

Diff for: AngularSvg/src/app/iot/silo/silo.component.html

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
<svg:g [contextMenu]="openCloseMenu">
22
<g class="silo-fill">
3-
<svg:rect x="1" y="15" width="49" height="65" stroke="none" />
4-
<svg:path [@fill]="isOpen ? 'open' : 'closed'" d="M10,75 l15,15 l15,-15z" />
3+
<rect x="1" [attr.y]="convertToSize(maxFill - fill)" width="49" [attr.height]="convertToSize(fill)" stroke="none" />
4+
<path [@fill]="openingState" d="M10,75 l15,15 l15,-15z" />
55
</g>
66
</svg:g>
77

88
<svg:path d="M5,80 H0 V0 H50 V80 h-5" fill="none" stroke="black" transform="translate(0.5, 0.5)" />
99

10-
<svg:path [@flapStateLeft]="isOpen ? 'open' : 'closed'" d="M25,90 l-10,-10 h-12.5" fill="none" stroke="black" />
11-
<svg:path [@flapStateRight]="isOpen ? 'open' : 'closed'" d="M25,90 l10,-10 h12.5" fill="none" stroke="black" />
12-
<svg:line [@materialFlow]="isOpen ? 'open' : 'closed'" x1="25" y1="75" x2="25" y2="125" stroke-width="4" stroke="red" stroke-dasharray="5, 3" />
10+
<svg:path [@flapStateLeft]="openingState" d="M25,90 l-10,-10 h-12.5" fill="none" stroke="black" />
11+
<svg:path [@flapStateRight]="openingState" d="M25,90 l10,-10 h12.5" fill="none" stroke="black" />
12+
<svg:line [@materialFlow]="openingState" x1="25" y1="75" x2="25" y2="125" class="material-flow" stroke-width="4" stroke-dasharray="5, 3" />
1313

1414
<context-menu #openCloseMenu>
1515
<ng-template contextMenuItem (execute)="toggleFlap()">
16-
{{ isOpen ? 'Close' : 'Open' }}
16+
{{ openingState == 'open' ? 'Close' : 'Open' }}
1717
</ng-template>
1818
<ng-template contextMenuItem>
1919
Something else...

Diff for: AngularSvg/src/app/iot/silo/silo.component.ts

+18-11
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {animate, state, style, transition, trigger} from '@angular/animations';
2-
import {Component, OnInit} from '@angular/core';
2+
import {Component, OnInit, Input} from '@angular/core';
33

44
@Component({
55
selector: '[app-silo]',
@@ -13,8 +13,8 @@ import {Component, OnInit} from '@angular/core';
1313
state('closed', style({
1414
transform: 'translate(0, 0.5px)'
1515
})),
16-
transition('open => closed', animate('200ms ease-in')),
17-
transition('closed => open', animate('200ms ease-out'))
16+
transition('open => closed', animate('400ms ease-in')),
17+
transition('closed => open', animate('400ms ease-out'))
1818
]),
1919
trigger('flapStateRight', [
2020
state('open', style({
@@ -23,8 +23,8 @@ import {Component, OnInit} from '@angular/core';
2323
state('closed', style({
2424
transform: 'translate(0, 0.5px)'
2525
})),
26-
transition('open => closed', animate('200ms ease-in')),
27-
transition('closed => open', animate('200ms ease-out'))
26+
transition('open => closed', animate('400ms ease-in')),
27+
transition('closed => open', animate('400ms ease-out'))
2828
]),
2929
trigger('fill', [
3030
state('open', style({
@@ -33,8 +33,8 @@ import {Component, OnInit} from '@angular/core';
3333
state('closed', style({
3434
transform: 'translate(0, 0px)'
3535
})),
36-
transition('open => closed', animate('200ms ease-in')),
37-
transition('closed => open', animate('200ms ease-out'))
36+
transition('open => closed', animate('400ms ease-in')),
37+
transition('closed => open', animate('400ms ease-out'))
3838
]),
3939
trigger('materialFlow', [
4040
state('open', style({
@@ -43,15 +43,22 @@ import {Component, OnInit} from '@angular/core';
4343
state('closed', style({
4444
opacity: 0
4545
})),
46-
transition('open => closed', animate('200ms ease-in')),
47-
transition('closed => open', animate('200ms ease-out'))
46+
transition('open => closed', animate('400ms ease-in')),
47+
transition('closed => open', animate('400ms ease-out'))
4848
])
4949
]
5050
})
5151
export class SiloComponent {
52-
public isOpen: boolean = false;
52+
public openingState = 'closed';
5353

5454
public toggleFlap() {
55-
this.isOpen = !this.isOpen;
55+
this.openingState = (this.openingState == 'open') ? 'closed' : 'open';
56+
}
57+
58+
@Input() maxFill: number = 150;
59+
@Input() fill: number = 0;
60+
61+
convertToSize(fill: number): number {
62+
return fill * 80 / this.maxFill;
5663
}
5764
}

0 commit comments

Comments
 (0)