praktikant vor 3 Wochen
Ursprung
Commit
8e1a5a48bf

+ 8 - 2
src-angl/is/src/app/article-form/article-form.css

@@ -13,6 +13,12 @@ form{
     margin: 0 0 1.5rem 0;
 }
 
-input, select{
-    margin-bottom: 1.5rem;
+.input-error, select{
+    margin-bottom: 0.5rem;
 }
+
+
+.input-error{
+    color: red;
+    min-height: 1.2rem;
+}

+ 39 - 8
src-angl/is/src/app/article-form/article-form.html

@@ -8,21 +8,52 @@
 <form [formGroup]="articleForm" (ngSubmit)="handleSubmit()">
 	<h2 class="form-header">Create Article</h2>
 	<label>Title</label>
-	<input type="text" formControlName="title"/>
+	<input type="text" formControlName="title" required/>
+	<span class="input-error">
+		@if (formSubmitted && articleForm.controls.title.errors?.['required']) {
+			Please enter a title
+		}
+
+		@if (articleForm.controls.title.errors?.['invalidString']) {
+			Please ensure the title only contains letters
+		}
+
+	</span>
 	<label>Text</label>
-	<input type="text" formControlName="text"/>
-	<label>Category Name</label>
-	<select name="categoryNames" id="categoryNames">
+	<input type="text" formControlName="text" required/>
+	<span class="input-error">
+		@if (formSubmitted && articleForm.controls.text.errors?.['required']) {
+			Please enter a text
+		}
+
+		@if (articleForm.controls.text.errors?.['invalidString']) {
+			Please ensure the text only contains letters
+		}
+	</span>
+	<label>Category</label>
+	<select id="categoryNames" formControlName="categoryName" required>
+		<option value="" disabled>--Select a category--</option>
 		@for (cn of categoryNames; track cn) {
-			<option value={cn}>{{ cn }}</option>
+			<option [value]="cn">{{ cn }}</option>
 		}
 	</select>
+	<span class="input-error">
+	@if (formSubmitted && articleForm.controls.categoryName.errors?.['required']) {
+		Please select a category
+	}
+	</span>
 	<label>Article Group</label>
-	<select name="articleGroups" id="articleGroups">
-		@for (articleGroup of articleGroups$ | async; track articleGroup.id) {
-			<option value="{{ articleGroup.name }}">{{ articleGroup.name }}</option>
+	<select id="articleGroups" formControlName="articleGroup">
+		<option value=null disabled>--Select a Article Group--</option>
+		@for (ag of articleGroups$ | async; track ag.id) {
+			<option [value]="ag.id">{{ ag.name }}</option>
 		}
 	</select>
+	<span class="input-error">
+	@if (formSubmitted && articleForm.controls.articleGroup.errors?.['required']) {
+		Please select a Article Group
+	}
+	</span>
 	<input type="submit" value="Create Article">
 </form>
 </body>

+ 36 - 6
src-angl/is/src/app/article-form/article-form.ts

@@ -5,6 +5,7 @@ import { Observable } from 'rxjs';
 import { Article, CategoryName } from '../bo/article';
 import { ArticleGroup } from '../bo/article-group';
 import { FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
+import { Router } from '@angular/router';
 
 @Component({
 	selector: 'is-article-form',
@@ -17,11 +18,12 @@ import { FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators }
 	]
 })
 export class ArticleForm implements OnInit {
+	formSubmitted = false;
 	articleForm = new FormGroup({
 		title: new FormControl('', Validators.required),
 		text: new FormControl('', Validators.required),
-		categoryName: new FormControl(''),
-		articleGroup: new FormControl(''),
+		categoryName: new FormControl('', Validators.required),
+		articleGroup: new FormControl(null, Validators.required),
 	})
 	private dataService = inject(DataService)
 
@@ -29,13 +31,25 @@ export class ArticleForm implements OnInit {
 	articleGroups$?: Observable<ArticleGroup[]>;
 	categoryNames: CategoryName[] = Object.values(CategoryName);
 
+	constructor(private router: Router) {
+	}
+
 	ngOnInit() {
-		const fc = new FormControl('', Validators.required);
 		this.articleGroups$ = this.dataService.getArticleGroups();
+		this.listenToInputChange("title");
+		this.listenToInputChange("text");
 	}
 
 	handleSubmit(){
+		this.formSubmitted = true;
+
+		this.articleForm.updateValueAndValidity();
 		console.log(this.articleForm.valid);
+		console.log(this.articleForm.controls.articleGroup.value);
+		console.log(this.articleForm.controls.title.errors);
+		console.log(this.articleForm.controls.text.errors);
+		console.log(this.articleForm.controls.categoryName.errors);
+		console.log(this.articleForm.controls.articleGroup.errors);
 		if (!this.articleForm.valid) {
 			return;
 		}
@@ -44,15 +58,31 @@ export class ArticleForm implements OnInit {
 			id: 0,
 			title: this.articleForm.value.title!,
 			text: this.articleForm.value.text!,
-			categoryName: CategoryName.sport,
-			articleGroupId: 3
+			categoryName: this.articleForm.value.categoryName! as CategoryName,
+			articleGroupId: this.articleForm.value.articleGroup!
 		};
 
 		this.dataService.postArticle(article).subscribe((article: Article) => {
-			console.log(article);
+			console.log(article)
+			this.router.navigateByUrl('/');
 		});
 	}
 
+	listenToInputChange(controlName: keyof typeof this.articleForm.controls){
+		console.log(this.articleForm.controls)
+		const control = this.articleForm.controls[controlName];
+		control.valueChanges.subscribe(value => {
+			const regExp = new RegExp('^([A-Za-z]|\\s)*$')
+			const errors = { ...control.errors };
 
+			if (value && !regExp.test(value)) {
+				errors['invalidString'] = true;
+			} else {
+				delete errors['invalidString'];
+			}
+
+			control.setErrors(Object.values(errors).length === 0 ? null : errors);
+		});
+	}
 
 }

+ 14 - 0
src-php/app/internship/controller/ArticleController.php

@@ -28,6 +28,20 @@ class ArticleController extends ControllerAdapter {
 	#[Inject]
 	private ArticleGroupDao $articleGroupDao;
 
+	function prepare(): void {
+		if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
+			header('Access-Control-Allow-Origin: *');
+			header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
+			header('Access-Control-Allow-Headers: Content-Type, Authorization');
+			header('Access-Control-Max-Age: 86400'); // cache preflight
+			http_response_code(204); // No Content
+			exit;
+		}
+		$this->getResponse()->setHeader("Access-Control-Allow-Headers: X-Requested-With, Content-Type,Accept, Origin");
+		$this->getResponse()->setHeader("Access-Control-Allow-Origin: http://localhost:4200");
+		$this->getResponse()->setHeader('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, HEAD, OPTIONS');
+	}
+
 	/**
 	 * Gibt den {@see Article} mit der entsprechenden id im JSON Format zurück.
 	 *

+ 14 - 0
src-php/app/internship/controller/ArticleGroupController.php

@@ -26,6 +26,20 @@ class ArticleGroupController extends ControllerAdapter {
 	#[Inject]
 	private ArticleGroupDao $articleGroupDao;
 
+	function prepare(): void {
+		if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
+			header('Access-Control-Allow-Origin: *');
+			header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
+			header('Access-Control-Allow-Headers: Content-Type, Authorization');
+			header('Access-Control-Max-Age: 86400'); // cache preflight
+			http_response_code(204); // No Content
+			exit;
+		}
+		$this->getResponse()->setHeader("Access-Control-Allow-Headers: X-Requested-With, Content-Type,Accept, Origin");
+		$this->getResponse()->setHeader("Access-Control-Allow-Origin: http://localhost:4200");
+		$this->getResponse()->setHeader('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, HEAD, OPTIONS');
+	}
+
 	/**
 	 * Gibt den {@see ArticleGroup} mit der entsprechenden id im JSON Format zurück.
 	 *

+ 3 - 3
src-php/public/index.php

@@ -1,7 +1,7 @@
 <?php
-header("Access-Control-Allow-Origin: http://localhost:4200");
-header("Access-Control-Allow-Headers: Content-Type, Authorization");
-header("Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS");
+//header("Access-Control-Allow-Origin: http://localhost:4200");
+//header("Access-Control-Allow-Headers: Content-Type, Authorization");
+//header("Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS");
 
 $pubPath = realpath(dirname(__FILE__));
 $appPath = realpath($pubPath . '/../app');