Compare commits

...

5 Commits

Author SHA1 Message Date
af5da29daf uebung-6 2025-12-06 08:46:55 +01:00
a8a8ec9779 clean up and install angular cli 2025-11-22 13:24:40 +01:00
210dd12663 ignore jetty.base directory 2025-11-22 12:43:38 +01:00
761eb6f4a6 working jetty config 2025-11-22 12:42:42 +01:00
b98cbf3615 uebung-5 2025-11-22 09:04:51 +01:00
36 changed files with 16029 additions and 52 deletions

1
.envrc
View File

@@ -1 +0,0 @@
use flake

7
.gitignore vendored
View File

@@ -40,3 +40,10 @@ build/
### nix / direnv
.direnv
result
### jetty
**/.jetty/home
**/.jetty/base
### Angualr Demo ###
**/*/node_modules

17
demo/.editorconfig Normal file
View File

@@ -0,0 +1,17 @@
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.ts]
quote_type = single
ij_typescript_use_double_quotes = false
[*.md]
max_line_length = off
trim_trailing_whitespace = false

42
demo/.gitignore vendored Normal file
View File

@@ -0,0 +1,42 @@
# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files.
# Compiled output
/dist
/tmp
/out-tsc
/bazel-out
# Node
/node_modules
npm-debug.log
yarn-error.log
# IDEs and editors
.idea/
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# Visual Studio Code
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# Miscellaneous
/.angular/cache
.sass-cache/
/connect.lock
/coverage
/libpeerconnection.log
testem.log
/typings
# System files
.DS_Store
Thumbs.db

101
demo/angular.json Normal file
View File

@@ -0,0 +1,101 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"demo": {
"projectType": "application",
"schematics": {},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:application",
"options": {
"outputPath": "dist/demo",
"index": "src/index.html",
"browser": "src/main.ts",
"polyfills": [
"zone.js"
],
"tsConfig": "tsconfig.app.json",
"assets": [
{
"glob": "**/*",
"input": "public"
}
],
"styles": [
"src/styles.css"
],
"scripts": [],
"server": "src/main.server.ts",
"prerender": true,
"ssr": {
"entry": "src/server.ts"
}
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kB",
"maximumError": "1MB"
},
{
"type": "anyComponentStyle",
"maximumWarning": "4kB",
"maximumError": "8kB"
}
],
"outputHashing": "all"
},
"development": {
"optimization": false,
"extractLicenses": false,
"sourceMap": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"buildTarget": "demo:build:production"
},
"development": {
"buildTarget": "demo:build:development"
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n"
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"polyfills": [
"zone.js",
"zone.js/testing"
],
"tsConfig": "tsconfig.spec.json",
"assets": [
{
"glob": "**/*",
"input": "public"
}
],
"styles": [
"src/styles.css"
],
"scripts": []
}
}
}
}
}
}

14917
demo/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

43
demo/package.json Normal file
View File

@@ -0,0 +1,43 @@
{
"name": "demo",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"watch": "ng build --watch --configuration development",
"test": "ng test",
"serve:ssr:demo": "node dist/demo/server/server.mjs"
},
"private": true,
"dependencies": {
"@angular/common": "^19.2.0",
"@angular/compiler": "^19.2.0",
"@angular/core": "^19.2.0",
"@angular/forms": "^19.2.0",
"@angular/platform-browser": "^19.2.0",
"@angular/platform-browser-dynamic": "^19.2.0",
"@angular/platform-server": "^19.2.0",
"@angular/router": "^19.2.0",
"@angular/ssr": "^19.2.3",
"express": "^4.18.2",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.15.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "^19.2.3",
"@angular/cli": "^19.2.3",
"@angular/compiler-cli": "^19.2.0",
"@types/express": "^4.17.17",
"@types/jasmine": "~5.1.0",
"@types/node": "^18.18.0",
"jasmine-core": "~5.6.0",
"karma": "~6.4.0",
"karma-chrome-launcher": "~3.2.0",
"karma-coverage": "~2.2.0",
"karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "~2.1.0",
"typescript": "~5.7.2"
}
}

BIN
demo/public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

18
demo/src/app/Auto.ts Normal file
View File

@@ -0,0 +1,18 @@
export class Auto {
constructor(
private _hersteller: string,
private _typ: string,
private _kennzeichen: string,
) {
}
get typ(): string { return this._typ; }
set typ(typ: string) { this._typ = typ; }
get hersteller(): string { return this._hersteller; }
set hersteller(hersteller: string) { this._hersteller = hersteller; }
get kennzeichen(): string { return this._kennzeichen; }
set kennzeichen(kennzeichen: string) { this._kennzeichen = kennzeichen; }
}

View File

@@ -0,0 +1,197 @@
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
<!-- * * * * * * * * * * * The content below * * * * * * * * * * * -->
<!-- * * * * * * * * * * is only a placeholder * * * * * * * * * * -->
<!-- * * * * * * * * * * and can be replaced. * * * * * * * * * * -->
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
<!-- * * * * * * * * * Delete the template below * * * * * * * * * -->
<!-- * * * * * * * to get started with your project! * * * * * * * -->
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
<style>
:host {
--bright-blue: oklch(51.01% 0.274 263.83);
--electric-violet: oklch(53.18% 0.28 296.97);
--french-violet: oklch(47.66% 0.246 305.88);
--vivid-pink: oklch(69.02% 0.277 332.77);
--hot-red: oklch(61.42% 0.238 15.34);
--orange-red: oklch(63.32% 0.24 31.68);
--gray-900: oklch(19.37% 0.006 300.98);
--gray-700: oklch(36.98% 0.014 302.71);
--gray-400: oklch(70.9% 0.015 304.04);
--red-to-pink-to-purple-vertical-gradient: linear-gradient(
180deg,
var(--orange-red) 0%,
var(--vivid-pink) 50%,
var(--electric-violet) 100%
);
--red-to-pink-to-purple-horizontal-gradient: linear-gradient(
90deg,
var(--orange-red) 0%,
var(--vivid-pink) 50%,
var(--electric-violet) 100%
);
--pill-accent: var(--bright-blue);
font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
"Segoe UI Symbol";
box-sizing: border-box;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
h1 {
font-size: 3.125rem;
color: var(--gray-900);
font-weight: 500;
line-height: 100%;
letter-spacing: -0.125rem;
margin: 0;
font-family: "Inter Tight", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
"Segoe UI Symbol";
}
p {
margin: 0;
color: var(--gray-700);
}
main {
width: 100%;
min-height: 100%;
display: flex;
justify-content: center;
align-items: center;
padding: 1rem;
box-sizing: inherit;
position: relative;
}
.angular-logo {
max-width: 9.2rem;
}
.content {
display: flex;
justify-content: space-around;
width: 100%;
max-width: 700px;
margin-bottom: 3rem;
}
.content h1 {
margin-top: 1.75rem;
}
.content p {
margin-top: 1.5rem;
}
.divider {
width: 1px;
background: var(--red-to-pink-to-purple-vertical-gradient);
margin-inline: 0.5rem;
}
.pill-group {
display: flex;
flex-direction: column;
align-items: start;
flex-wrap: wrap;
gap: 1.25rem;
}
.pill {
display: flex;
align-items: center;
--pill-accent: var(--bright-blue);
background: color-mix(in srgb, var(--pill-accent) 5%, transparent);
color: var(--pill-accent);
padding-inline: 0.75rem;
padding-block: 0.375rem;
border-radius: 2.75rem;
border: 0;
transition: background 0.3s ease;
font-family: var(--inter-font);
font-size: 0.875rem;
font-style: normal;
font-weight: 500;
line-height: 1.4rem;
letter-spacing: -0.00875rem;
text-decoration: none;
}
.pill:hover {
background: color-mix(in srgb, var(--pill-accent) 15%, transparent);
}
.pill-group .pill:nth-child(6n + 1) {
--pill-accent: var(--bright-blue);
}
.pill-group .pill:nth-child(6n + 2) {
--pill-accent: var(--french-violet);
}
.pill-group .pill:nth-child(6n + 3),
.pill-group .pill:nth-child(6n + 4),
.pill-group .pill:nth-child(6n + 5) {
--pill-accent: var(--hot-red);
}
.pill-group svg {
margin-inline-start: 0.25rem;
}
.social-links {
display: flex;
align-items: center;
gap: 0.73rem;
margin-top: 1.5rem;
}
.social-links path {
transition: fill 0.3s ease;
fill: var(--gray-400);
}
.social-links a:hover svg path {
fill: var(--gray-900);
}
@media screen and (max-width: 650px) {
.content {
flex-direction: column;
width: max-content;
}
.divider {
height: 1px;
width: 100%;
background: var(--red-to-pink-to-purple-horizontal-gradient);
margin-block: 1.5rem;
}
}
</style>
<main class="main">
<div class="content">
<create-auto (autoInitializedEvent)="setAuto($event)"/>
<div> Typ: {{ auto?.typ ?? "N/A" }} </div>
<div> Hersteller: {{ auto?.hersteller ?? "N/A" }} </div>
<div> Kennzeichen: {{ auto?.kennzeichen ?? "N/A" }} </div>
</div>
</main>
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
<!-- * * * * * * * * * * * The content above * * * * * * * * * * * * -->
<!-- * * * * * * * * * * is only a placeholder * * * * * * * * * * * -->
<!-- * * * * * * * * * * and can be replaced. * * * * * * * * * * * -->
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
<!-- * * * * * * * * * * End of Placeholder * * * * * * * * * * * * -->
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
<router-outlet />

View File

@@ -0,0 +1,29 @@
import { TestBed } from '@angular/core/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [AppComponent],
}).compileComponents();
});
it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app).toBeTruthy();
});
it(`should have the 'demo' title`, () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app.title).toEqual('demo');
});
it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.nativeElement as HTMLElement;
expect(compiled.querySelector('h1')?.textContent).toContain('Hello, demo');
});
});

View File

@@ -0,0 +1,21 @@
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { Auto } from './Auto';
@Component({
selector: 'app-root',
imports: [RouterOutlet],
templateUrl: './app.component.html',
styleUrl: './app.component.css'
})
export class AppComponent {
title = 'demo';
constructor(public auto?: Auto) {
}
setAuto(auto: Auto) {
this.auto = auto;
}
}

View File

@@ -0,0 +1,11 @@
import { mergeApplicationConfig, ApplicationConfig } from '@angular/core';
import { provideServerRendering } from '@angular/platform-server';
import { appConfig } from './app.config';
const serverConfig: ApplicationConfig = {
providers: [
provideServerRendering(),
]
};
export const config = mergeApplicationConfig(appConfig, serverConfig);

View File

@@ -0,0 +1,9 @@
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
import { provideClientHydration, withEventReplay } from '@angular/platform-browser';
export const appConfig: ApplicationConfig = {
providers: [provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes), provideClientHydration(withEventReplay())]
};

View File

@@ -0,0 +1,3 @@
import { Routes } from '@angular/router';
export const routes: Routes = [];

View File

@@ -0,0 +1,3 @@
<p>create-auto works!</p>
<button (click)="createAuto">Auto erstellen</button>

View File

@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { CreateAutoComponent } from './create-auto.component';
describe('CreateAutoComponent', () => {
let component: CreateAutoComponent;
let fixture: ComponentFixture<CreateAutoComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [CreateAutoComponent]
})
.compileComponents();
fixture = TestBed.createComponent(CreateAutoComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,21 @@
import { Component, Output, EventEmitter } from '@angular/core';
import { Auto } from '../Auto';
@Component({
selector: 'app-create-auto',
imports: [],
templateUrl: './create-auto.component.html',
styleUrl: './create-auto.component.css'
})
export class CreateAutoComponent {
constructor(
@Output() public autoInitializedEvent: EventEmitter<Auto>
) {
autoInitializedEvent = new EventEmitter<Auto>();
}
createAuto() {
var auto = new Auto("BMW", "e36", "DE-gho-st");
this.autoInitializedEvent.emit(auto);
}
}

13
demo/src/index.html Normal file
View File

@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Demo</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root></app-root>
</body>
</html>

7
demo/src/main.server.ts Normal file
View File

@@ -0,0 +1,7 @@
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { config } from './app/app.config.server';
const bootstrap = () => bootstrapApplication(AppComponent, config);
export default bootstrap;

6
demo/src/main.ts Normal file
View File

@@ -0,0 +1,6 @@
import { bootstrapApplication } from '@angular/platform-browser';
import { appConfig } from './app/app.config';
import { AppComponent } from './app/app.component';
bootstrapApplication(AppComponent, appConfig)
.catch((err) => console.error(err));

67
demo/src/server.ts Normal file
View File

@@ -0,0 +1,67 @@
import { APP_BASE_HREF } from '@angular/common';
import { CommonEngine, isMainModule } from '@angular/ssr/node';
import express from 'express';
import { dirname, join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import bootstrap from './main.server';
const serverDistFolder = dirname(fileURLToPath(import.meta.url));
const browserDistFolder = resolve(serverDistFolder, '../browser');
const indexHtml = join(serverDistFolder, 'index.server.html');
const app = express();
const commonEngine = new CommonEngine();
/**
* Example Express Rest API endpoints can be defined here.
* Uncomment and define endpoints as necessary.
*
* Example:
* ```ts
* app.get('/api/**', (req, res) => {
* // Handle API request
* });
* ```
*/
/**
* Serve static files from /browser
*/
app.get(
'**',
express.static(browserDistFolder, {
maxAge: '1y',
index: 'index.html'
}),
);
/**
* Handle all other requests by rendering the Angular application.
*/
app.get('**', (req, res, next) => {
const { protocol, originalUrl, baseUrl, headers } = req;
commonEngine
.render({
bootstrap,
documentFilePath: indexHtml,
url: `${protocol}://${headers.host}${originalUrl}`,
publicPath: browserDistFolder,
providers: [{ provide: APP_BASE_HREF, useValue: baseUrl }],
})
.then((html) => res.send(html))
.catch((err) => next(err));
});
/**
* Start the server if this module is the main entry point.
* The server listens on the port defined by the `PORT` environment variable, or defaults to 4000.
*/
if (isMainModule(import.meta.url)) {
const port = process.env['PORT'] || 4000;
app.listen(port, () => {
console.log(`Node Express server listening on http://localhost:${port}`);
});
}
export default app;

1
demo/src/styles.css Normal file
View File

@@ -0,0 +1 @@
/* You can add global styles to this file, and also import other style files */

19
demo/tsconfig.app.json Normal file
View File

@@ -0,0 +1,19 @@
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/app",
"types": [
"node"
]
},
"files": [
"src/main.ts",
"src/main.server.ts",
"src/server.ts"
],
"include": [
"src/**/*.d.ts"
]
}

27
demo/tsconfig.json Normal file
View File

@@ -0,0 +1,27 @@
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
{
"compileOnSave": false,
"compilerOptions": {
"outDir": "./dist/out-tsc",
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"skipLibCheck": true,
"isolatedModules": true,
"esModuleInterop": true,
"experimentalDecorators": true,
"moduleResolution": "bundler",
"importHelpers": true,
"target": "ES2022",
"module": "ES2022"
},
"angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false,
"strictInjectionParameters": true,
"strictInputAccessModifiers": true,
"strictTemplates": true
}
}

15
demo/tsconfig.spec.json Normal file
View File

@@ -0,0 +1,15 @@
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/spec",
"types": [
"jasmine"
]
},
"include": [
"src/**/*.spec.ts",
"src/**/*.d.ts"
]
}

View File

@@ -9,12 +9,63 @@
\begin{exercises}
\item Worum handelt es sich bei Facelets und welche Vorteile bietet die Verwendung von Facelets?
Facelets sind eine Tag Library, die zusätzliche Tags bereitstellt.
Mit Facelets können Templates erstellt werden, oder wiederholte Ausgaben von html deklerativ in xml erstellt werden.
\item Wie können Templates mit Facelets erstellt bzw. verwendet werden?
In Templates wird der generelle Aufbau einer Seite definiert. Dies erfolgt wie in einer normale xhtml Seite, nur mit Platzhaltern (\texttt{<ui:insert>}):
\begin{xmlCode}
...
<body>
<ui:insert name="header">
<ui:include src="default-header.xhtml">
</ui:insert>
<ui:insert name="content">
</ui:insert>
<ui:insert name="footer">
</ui:insert>
</body>
...
\end{xmlCode}
Verwendet wird ein Template dann, in dem man mit \texttt{<ui:composition>} das template angibt, und mit ui:define die Platzhalter fŭllt:
\begin{xmlCode}
<ui:composition template="template.xhtml">
<ui:define name="content">
<p>hello world</p>
</ui:define>
</ui:composition>
\end{xmlCode}
\item Worum handelt es sich bei PrimeFaces?
PrimeFaces stellen eine Komponentenbibliothek dar, die Komponenten mit AJAX-Unterstützung anbieten.
\item Welcher Namespace wird für die Verwendung von PrimeFaces benutzt?
\texttt{xmlns:p}
\item Wie kann per Ajax auf die Eingabe eines Tastenklicks reagiert werden?
Indem man auf das \textit{keydown} Event reagiert:
\begin{xmlCode}
<p:ajax event="keydown" .../>
\end{xmlCode}
\item Zählen Sie vier PrimeFaces-Komponenten auf und geben deren Funktion an.
\begin{itemize}
\item \texttt{<p:autocomplete>}: Ermöglicht completion-Vorschläge für ein Input-Feld
\item \texttt{<p:calendar>}: Kalendar-Datum Eingabe-Element
\item \texttt{<p:gmap>}: Google-Maps Element
\item \texttt{<p:textEditor>}: Ein Wysiwyg Texteditor
\end{itemize}
\item Erstellen Sie eine JSF-Seite, die eine Klappliste anzeigt in der einer der Werte „Farben“, „Zahlen“ oder „Buchstaben“ ausgewählt werden kann. Darunter befindet sich eine Klappliste die zunächst nur den Inhalt „---“ hat. Nach einer Auswahl in der ersten Klappliste soll ein AJAX-Request generiert werden, der den Inhalt der zweiten Klappliste setzt (denken Sie sich zu den in Klappliste 1 auswählbaren Werten passende Werte aus).
\end{exercises}
\end{document}

248
docs/uebungen/uebung-6.tex Normal file
View File

@@ -0,0 +1,248 @@
\documentclass{uebung}
\author{Linus Nagel}
\chapter{5}
\begin{document}
\maketitle
\begin{exercises}
\item Worin bestehen die Vorteile von Single Page Applications?
\begin{itemize}
\item Geringerer Web Traffic: Höhere Performance durch eine geringere Datenmenge bei der Kommunikation: Mobile Devices profitieren von der Reduzierung des notwendigen online Volumens.
\item Kein 'Flackern' der Seite, da sie nicht jedes Mal komplett neu geladen wird.
\item Durch die direkte Reaktion des Frontends auf Bedienereingaben besteht insgesamt eine bessere User Experience als bei klassischen Web Applications.
\item Solange keine Informationen nachgeladen werden müssen, wird ein Offline-Betrieb ermöglicht.
\end{itemize}
\item Erläutern Sie die Vorteile von TypeScript gegenüber JavaScript.
\begin{itemize}
\item Statische Typisierung
\item TypeScript ermöglicht die Nutzung vieler aus der Objektorientierung bekannten Sprachkonstrukte
\item TypeScript ermöglicht die Verwendung von Lambda-Ausdrücken und Dekoratoren (in Java Annotation genannt).
\end{itemize}
\item Wie werden in TypeScript Klassen erstellt?
z.B. so:
\begin{minted}{ts}
class Person {
constructor(private _name: string) {
}
get name(): string {
return this._name;
}
set name(name: string) {
this._name = name;
}
}
\end{minted}
\item Wie werden in TypeScript Konstruktoren erstellt? Können mehrere Konstruktoren erstellt werden?
Es kann nur ein constructor erstellt werden. Dieser kann dafür dann aber default-Werte haben, damit der caller nicht alle Attribute setzen muss:
\begin{minted}{ts}
class Person {
constructor(private _name: string = "Max Mustermann") {
}
}
\end{minted}
\item Wie werden in TypeScript Methoden erstellt?
\begin{minted}{ts}
getStuff(param: paramType = paramDefault): StuffType {
...
return ...
}
\end{minted}
\item Worum handelt es sich, wenn von einem ist Dekorator gesprochen wird?
Schlüsselwörter, die mit dem Zeichen @ beginnen. Durch in runden Klammern folgende Attribute können Dekoratoren noch weitere Konfigurationsdaten aufnehmen.
\item Beschreiben Sie den Aufbau einer Komponente.
\begin{minted}{ts}
@Component({
selector: '...',
standalone: true,
imports: [...],
templateUrl: '...',
styleUrls: ['..']
})
export class NameDerKomponente {
...
}
\end{minted}
In @Component wird zunächst im Attribut selector ein Selektor angegeben, der die Elemente im DOM auswählt, an die die Komponente gebunden werden soll.
Dabei handelt es sich um einen Namen der Komponente, der an anderer Stelle als Tag verwendet werden kann.
An der Stelle, an der dieser Selektor angegeben wird, wird zur Laufzeit der View der Komponente eingefügt.
Das auf den Selektor matchende Element wird dabei als Host- Element bezeichnet.
Wird als selector nur ein Elementname angegeben, wird ein entsprechendes HTML-Tag gesucht und dessen Inhalt durch den View der Komponente ersetzt.
In der eigentlichen Klasse der Komponente wird das Datenmodell und die Businesslogik der Komponente erstellt.
\item Worum handelt es sich bei Lifecycle-Hooks?
Methoden, die, falls sie vom Entwickler definiert wurden, automatisch aufgerufen werden (Callback-Methoden). Sobald eine Komponente den Zustand wechselt, wird die entsprechende Lifecycle-Hook-Methode aufgerufen.
\item Welchen Zweck erfüllt das Angular-CLI?
Das CLI umfasst eine Reihe von Befehlen für sich wiederholende Tätigkeiten, so dass Entwicklern die manuelle Erstellung dieser Dateien abgenommen wird. Mit dem CLI können die Bestandteile der Angular-Anwendung, wie beispielsweise Komponenten oder Klassen, bis hin zum ganzen Angular-Projekt erstellt werden, und stellt somit das Standardvorgehen zum Erstellen
projektrelevanter Dateien dar.
\item Worum handelt es sich bei der Interpolation?
Bei der Interpolation können Daten aus der Komponentenklasse im Template angezeigt werden.
z.B:
Komponent:
\begin{minted}{ts}
export class AppComponent {
location: string;
constructor(){ this.location = 'Welt'; }
}
\end{minted}
Template:
\begin{minted}{ng2}
Hallo {{ location }}!
\end{minted}
\item Wie kann durch ein Property Binding ein Attribut einer Kind-Komponente gesetzt werden? Geben Sie ein Beispiel hierfür an.
Im Template der Root-Komponente:
\begin{minted}{ng2}
<kind-komponente [Eigenschaft der Kind-Komponente]="Eigenschaft der Eltern-Komponente">
..
</kind-komponente>
\end{minted}
\item Wie wird ein Event-Binding mit einem JavaScript-Event erstellt? Geben Sie ein Beispiel mit einem Knopf und dem click-Event an.
Template:
\begin{minted}{ng2}
<button (click)="verarbeiteKlick()">Klick</button>
Anzahl der Klicks: {{ klickZaehler }}
\end{minted}
Komponent:
\begin{minted}{ts}
export class EventComponent implements OnInit {
klickZaehler!: number;
ngOnInit() { this.klickZaehler = 0; }
verarbeiteKlick() {
this.klickZaehler++;
}
}
\end{minted}
\item Beschreiben Sie das Vorgehen, um ein selbstdefiniertes Event in der Kind-Komponente zu erstellen und in der Eltern-Komponente zu empfangen.
In der Kind-Komponente muss zunächst das event erstellt werden:
\begin{minted}{ts}
@Output() event = new EventEmitter();
\end{minted}
Dieses kann dann in der Eltern-Komponente verwendet verwenden:
\begin{minted}{ng2}
<tag (event)="funktionsname()">...
\end{minted}
Das event kann wie folgt aktiviert werden, damit die Eltern-Komponente dann auch das Event bekommt:
\begin{minted}{ts}
this.event.emit();
\end{minted}
\item Erklären Sie die Verwendung der Strukturdirektive *ngIf.
Die Strukturdirektive *ngIf wird verwendet, um ein DOM Element nur unter einer Bedingung einzufügen.
\item Beschreiben Sie, wie die Strukturdirektive *ngFor verwendet werden kann.
Die Strukturdirektive *ngFor wird verwendet, um ein DOM Element bei der Instanziierung des Templates mehrfach einzufügen.
\item Erstellen Sie eine TypeScript Klasse namens Auto mit den Attributen Hersteller, Typ und Kennzeichen vom Typ string.
\begin{minted}{ts}
class Auto {
constructor(
private _hersteller: string,
private _typ: string,
private _kennzeichen: string,
) {
}
get typ(): string { return this._typ; }
set typ(typ: string) { this._typ = typ; }
get hersteller(): string { return this._hersteller; }
set hersteller(hersteller: string) { this._hersteller = hersteller; }
get kennzeichen(): string { return this._kennzeichen; }
set kennzeichen(kennzeichen: string) { this._kennzeichen = kennzeichen; }
}
\end{minted}
\item Erstellen Sie ein Programm in dem im Template einer Komponente ein Knopf erstellt wird, der beim click-Event eine Methode aufruft. In dieser Methode soll ein Objekt vom Typ Auto (siehe vorherige Aufgabe) erstellt werden. Dieses Objekt soll anschließend per Event-Binding an eine Eltern-Komponente gesendet und dort in einem Attribut gespeichert werden. Abschließend geben Sie die Attribute des empfangenen Auto-Objekts im Template der Eltern-Komponente aus. Bei der Ausgabe soll beachtet werden, dass es zu keinem Fehler kommen darf (siehe Konsole), falls das Event noch nicht empfangen wurde und das Auto-Attribut (noch) undefined ist. \textit{Die Konsole wird in den meisten Browsern über F12 oder CMD-alt-i geöffnet.}
Main page:
\begin{minted}{ts}
export class AppComponent {
title = 'demo';
constructor(public auto?: Auto) {
}
setAuto(auto: Auto) {
this.auto = auto;
}
}
\end{minted}
Main page template:
\begin{minted}{ng2}
<main class="main">
<div class="content">
<create-auto (autoInitializedEvent)="setAuto($event)"/>
<div> Typ: {{ auto?.typ ?? "N/A" }} </div>
<div> Hersteller: {{ auto?.hersteller ?? "N/A" }} </div>
<div> Kennzeichen: {{ auto?.kennzeichen ?? "N/A" }} </div>
</div>
</main>
\end{minted}
Auto erstellen kompolnente:
\begin{minted}{ts}
export class CreateAutoComponent {
constructor(
@Output() public autoInitializedEvent: EventEmitter<Auto>
) {
autoInitializedEvent = new EventEmitter<Auto>();
}
createAuto() {
var auto = new Auto("BMW", "e36", "DE-gho-st");
this.autoInitializedEvent.emit(auto);
}
}
\end{minted}
Auto erstellen template:
\begin{minted}{ng2}
<button (click)="createAuto">Auto erstellen</button>
\end{minted}
\end{exercises}
\end{document})

View File

@@ -15,6 +15,10 @@
\RequirePackage{graphicx}
\RequirePackage{amsmath}
\RequirePackage{hyperref}
\RequirePackage{minted}
% Minted config
\usemintedstyle{default}
% Define chapter counter for exercise sheets
\newcounter{chapter}

2
flake.lock generated
View File

@@ -29,8 +29,8 @@
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"rev": "9da7f1cf7f8a6e2a7cb3001b048546c92a8258b4",
"type": "github"
}
},

View File

@@ -3,10 +3,14 @@
inputs = {
flake-parts.url = "github:hercules-ci/flake-parts";
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
nixpkgs.url = "github:NixOS/nixpkgs/9da7f1cf7f8a6e2a7cb3001b048546c92a8258b4";
};
outputs = inputs @ {flake-parts, ...}:
outputs = inputs @ {
flake-parts,
self,
...
}:
flake-parts.lib.mkFlake {inherit inputs;} {
imports = [];
systems = ["x86_64-linux" "aarch64-linux" "aarch64-darwin" "x86_64-darwin"];
@@ -45,6 +49,10 @@
upquote
]))
];
angularPackages = with pkgs; [
nodePackages."@angular/cli"
];
in {
# Per-system attributes can be defined here. The self' and inputs'
# module parameters provide easy access to attributes of the same
@@ -79,12 +87,13 @@
mkdir -p $out/share $out/log $out/artifacts
cp out/uebung-${toString n}.pdf "$out/share/${name}"
cp out/*.log $out/log/
cp out/* $out/artifacts
cp -r out/* $out/artifacts
'';
}
// args;
in {
"uebung-5" = mkUebungPdf 5 {};
"uebung-6" = mkUebungPdf 6 {};
};
devShells.default = with pkgs;
@@ -95,13 +104,38 @@
mermaid-cli
openjdk
jetty
ant
maven
]
++ latexPackages;
++ latexPackages
++ angularPackages;
shellHook =
# bash
''
# jetty docs: https://jetty.org/docs/jetty/12.1/operations-guide/begin/index.html
export JETTY_HOME="$(readlink -f .)/.jetty/home"
export JETTY_BASE="$(readlink -f .)/.jetty/base"
mkdir -p "$JETTY_HOME" "$JETTY_BASE"
for item in ${pkgs.jetty}/*; do
ln -sf "$item" "$JETTY_HOME/"
done
jetty() {
mkdir -p $JETTY_BASE/webapps/
ln -fs "$(readlink -f target/*.war)" $JETTY_BASE/webapps/
java \
-Djetty.home="$JETTY_HOME" \
-Djetty.base="$JETTY_BASE" \
-jar $JETTY_HOME/start.jar $@
}
jetty --add-modules=http,ee11-cdi,ee11-ext,ee11-deploy,ee11-environment,ee11-annotations,ee11-glassfish-jstl,ee11-jndi,ee11-jsp,ee11-jstl,ee11-plus,ee11-security,ee11-servlet,ee11-servlets,ee11-webapp,ee11-websocket-jakarta,ee11-websocket-jetty,ee11-websocket-jetty-client-webapp
shellHook = ''
echo "Usage Example:"
echo "jetty ..."
echo "mvn clean package # build demo.war"
echo "jetty # run webserver"
'';
};
};

61
pom.xml
View File

@@ -1,59 +1,70 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>demo</artifactId>
<version>1.0-SNAPSHOT</version>
<name>demo</name>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.target>8</maven.compiler.target>
<maven.compiler.source>8</maven.compiler.source>
<project.report.sourceEncoding>UTF-8</project.report.sourceEncoding>
<maven.compiler.release>21</maven.compiler.release>
<junit.version>5.11.0-M2</junit.version>
<compiler-plugin.version>3.13.0</compiler-plugin.version>
<war-plugin.version>3.4.0</war-plugin.version>
</properties>
<dependencies>
<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-web-api</artifactId>
<version>11.0.0-M3</version>
<version>11.0.0</version>
<scope>provided</scope>
</dependency><dependency>
<groupId>org.jboss.weld.se</groupId>
<artifactId>weld-se-core</artifactId>
<version>6.0.0.Beta1</version>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>jakarta.faces</artifactId>
<version>4.1.3</version>
</dependency>
<dependency>
<groupId>jakarta.enterprise</groupId>
<artifactId>jakarta.enterprise.cdi-api</artifactId>
<version>4.1.0</version>
</dependency>
<dependency>
<groupId>jakarta.enterprise</groupId>
<artifactId>jakarta.enterprise.cdi-el-api</artifactId>
<version>4.1.0</version>
</dependency>
<dependency>
<groupId>org.jboss.weld.servlet</groupId>
<artifactId>weld-servlet-core</artifactId>
<version>5.1.2.Final</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>RELEASE</version>
<scope>provided</scope>
<version>1.18.30</version>
</dependency>
</dependencies>
<build>
<finalName>demo</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${compiler-plugin.version}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.4.0</version>
</plugin> </plugins>
<version>${war-plugin.version}</version>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/beans_4_0.xsd"
version="4.0" bean-discovery-mode="annotated">
</beans>

View File

@@ -3,11 +3,18 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
version="6.0">
<context-param>
<param-name>jakarta.faces.CONFIG_FILES</param-name>
<param-value>/WEB-INF/faces-config.xml</param-value>
</context-param>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>jakarta.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.xhtml</url-pattern>