
М
ИНОБРНАУКИ
РОССИИ
ФЕДЕРАЛЬНОЕ ГОСУДАРСТВЕННОЕ БЮДЖЕТНОЕ ОБРАЗОВАТЕЛЬНОЕ
УЧРЕЖДЕНИЕ ВЫСШЕГО ОБРАЗОВАНИЯ
НИЖЕГОРОДСКИЙ ГОСУДАРСТВЕННЫЙ ТЕХНИЧЕСКИЙ
УНИВЕРСИТЕТ ИМ. Р.Е.АЛЕКСЕЕВА
Институт радиоэлектроники и информационных технологий
Кафедра «Графические информационные системы»
ОТЧЕТ |
к лабораторной работе №6.1 |
«Инициализация и валидация форм. Работа с сервером.» |
(наименование работы) |
|
по дисциплине |
«WEB-технологии» |
(наименование дисциплины) |
ПРОВЕРИЛ: |
|
|
|
|
Агафонов Н.Е. |
(подпись) |
|
(фамилия, и.,о.) |
СТУДЕНТ: |
|
|
|
|
Какушкна О.В |
(подпись) |
|
(фамилия, и.,о.) |
|
|
23-ИСТ-1-1 |
|
|
(шифр группы) |
Оглавление
МИНОБРНАУКИ РОССИИ 1
НИЖЕГОРОДСКИЙ ГОСУДАРСТВЕННЫЙ ТЕХНИЧЕСКИЙ 1
1 Введение 3
1.1 Цели и задачи 3
1.1.1 Цель работы: 3
1.1.2 Постановка задачи: 3
2 Основная Часть 4
3 Заключение 19
Введение
Цели и задачи
Цель работы:
Инициализация и валидация форм. Работа с сервером..
Постановка задачи:
Инициализировать и валидировать формы
Произвести отправку значений формы на сервер
Произвести получения значений с сервера
Основная Часть
Форма регистрации
Шаблон формы регистрации:
<form [formGroup]="registrationForm" (ngSubmit)="onRegister()" class="container_registration">
<div *ngIf="showSuccessMessage" class="success-message">
<p>Вы успешно зарегистрировались!</p>
</div>
<div class="textbox-container" *ngIf="registrationForm.get('firstName')">
<label for="firstName">Имя:</label>
<input id="firstName" formControlName="firstName" class="input_cnt" />
<div *ngIf="registrationForm.get('firstName')?.invalid && (registrationForm.get('firstName')?.touched || registrationForm.get('firstName')?.dirty)" class="error-message">
<div *ngIf="registrationForm.get('firstName')?.errors?.['required']">Это поле обязательно для заполнения.</div>
<div *ngIf="registrationForm.get('firstName')?.errors?.['invalidName']">Имя должно содержать только буквы.</div>
</div>
</div>
<div class="textbox-container">
<label for="lastName">Фамилия:</label>
<input id="lastName" formControlName="lastName" class="input_cnt" />
<div *ngIf="registrationForm.get('lastName')?.invalid && (registrationForm.get('lastName')?.touched || registrationForm.get('lastName')?.dirty)" class="error-message">
<div *ngIf="registrationForm.get('lastName')?.errors?.['required']">Это поле обязательно для заполнения.</div>
</div>
</div>
<div class="textbox-container">
<label for="email">Почта:</label>
<input id="email" formControlName="email" class="input_cnt" />
<div *ngIf="registrationForm.get('email')?.touched && registrationForm.get('email')?.invalid" class="error-message">
<div *ngIf="registrationForm.get('email')?.errors?.['required']">Почта обязательна для заполнения.</div>
<div *ngIf="registrationForm.get('email')?.errors?.['email']">Неверный формат почты.</div>
</div>
<div *ngIf="emailExists && registrationForm.get('email')?.touched" class="error-message">
<p>Email уже существует. Пожалуйста, используйте другой.</p>
</div>
</div>
<div class="textbox-container">
<label for="password">Пароль:</label>
<div class="input-container">
<input id="password" [type]="showPassword ? 'text' : 'password'" formControlName="password" class="input_cnt" />
<span class="password-icon" (click)="showPassword = !showPassword">
<i [ngClass]="showPassword ? 'fa fa-eye' : 'fa fa-eye-slash'"></i>
</span>
</div>
<div *ngIf="registrationForm.get('password')?.invalid && (registrationForm.get('password')?.touched || registrationForm.get('password')?.dirty)" class="error-message">
<div *ngIf="registrationForm.get('password')?.errors?.['required']">Поле обязательно для заполнения.</div>
<div *ngIf="registrationForm.get('password')?.errors?.['minlength']">Пароль должен содержать минимум 8 символов.</div>
<div *ngIf="registrationForm.get('password')?.errors?.['passwordWeak']">Пароль должен содержать заглавные буквы, строчные буквы, цифры и специальные символы.</div>
</div>
</div>
<div class="textbox-container">
<label for="section">Секция:</label>
<select formControlName="section" class="form-control" class="input_cnt">
<option *ngFor="let section of sections" [value]="section.id">
{{ section.title }} (Доступно мест: {{ section.capacity }})
</option>
</select>
<div *ngIf="registrationForm.get('section')?.invalid && (registrationForm.get('section')?.touched || registrationForm.get('section')?.dirty)" class="error-message">
Поле обязательно для заполнения.
</div>
</div>
<div class="textbox-container">
<label for="group">Группа:</label>
<input id="group" formControlName="group" class="input_cnt" />
<div *ngIf="registrationForm.get('group')?.invalid && (registrationForm.get('group')?.touched || registrationForm.get('group')?.dirty)" class="error-message">
<div *ngIf="registrationForm.get('group')?.errors?.['required']">Поле обязательно для заполнения.</div>
<div *ngIf="registrationForm.get('group')?.errors?.['invalidGroup']">Группа должна соответствовать формату XX-ЛЛЛ-X-X.</div>
</div>
</div>
<div class="textbox-container">
<label for="institute">Институт:</label>
<select id="institute" formControlName="institute" class="input_cnt">
<option value="" disabled>Выберите институт</option>
<option *ngFor="let institute of institutes" [value]="institute.id">{{ institute.name }}</option>
</select>
<div *ngIf="registrationForm.get('institute')?.invalid && (registrationForm.get('institute')?.touched || registrationForm.get('institute')?.dirty)" class="error-message">
Выберите институт.
</div>
</div>
<div class="textbox-container">
<label for="payment">Сумма оплаты:</label>
<select id="payment" formControlName="payment" class="select-input">
<option value="" disabled>Выберите сумму оплаты</option>
<option value="5040">1-2 курс: 5040 р</option>
<option value="2520">3 курс: 2520 р</option>
</select>
<div *ngIf="registrationForm.get('payment')?.invalid && (registrationForm.get('payment')?.touched || registrationForm.get('payment')?.dirty)" class="error-message">Поле обязательно для заполнения.</div>
</div>
<div class="cnt_button">
<button type="submit" class="section" [disabled]="registrationForm.invalid">Оплатить</button>
</div>
</form>
<div *ngIf="showPaymentForm">
<h2>Форма оплаты</h2>
<div *ngIf="remainingTime > 0" class="error-messagep-pay">
<p>Оставшееся время для оплаты: {{ remainingTime }} секунд</p>
</div>
<app-payment [registrationForm]="registrationForm" (paymentSuccess)="onPaymentSuccess()"></app-payment>
</div>
В форме если поля не валидны отображаются сообщения, которые помогают пользователю решить проблему И если какое-то поле не валидно, то кнопка оплатить будет не доступна, если все хорошо, то отобразится форма оплаты.
Для работы с формами в Angular необходимо импортировать соответствующие модули. В вашем компоненте используется ReactiveFormsModule, который позволяет создавать реактивные формы:
В @Component класса RegistrationComponent создается форма с несколькими полями и проверяется валидация полей :
Для каждого поля формы добавлены валидаторы, которые проверяют, выполнены ли определенные условия. Например, Validators.required для обязательных полей и пользовательские валидаторы (nameValidator, passwordStrengthValidator, и т.д.) для своих условий:
Метод onRegister, который вызывается при отправке формы. Внутри этого метода проверяется валидность формы и выполняются запросы на резервирование мест и проверку существования email:
Если форма регистрации с полями валидная, то мы отображаем форму для оплаты и запускаем таймер, занимаем место для секции.
Шаблон формы оплаты:
<form [formGroup]="paymentForm" (ngSubmit)="onPay()" class="container_registration">
<div class="textbox-container">
<label for="cardNumber">Номер карты:</label>
<input
id="cardNumber"
formControlName="cardNumber"
class="input_cnt"
(input)="formatCardNumber($event)"
placeholder="XXXX XXXX XXXX XXXX" />
<div *ngIf="cardNumber?.invalid && (cardNumber?.touched || cardNumber?.dirty)" class="error-message">
<div *ngIf="cardNumber?.errors?.['required']">Это поле обязательно.</div>
<div *ngIf="cardNumber?.errors?.['pattern'] || cardNumber?.errors?.['invalidCardNumber']">Некорректный номер карты.</div>
</div>
</div>
<div class="textbox-container">
<label for="expiryDate">Срок действия:</label>
<input
id="expiryDate"
formControlName="expiryDate"
class="input_cnt"
(input)="formatExpiryDate($event)"
placeholder="MM/YY" />
<div *ngIf="expiryDate?.invalid && (expiryDate?.touched || expiryDate?.dirty)" class="error-message">
<div *ngIf="expiryDate?.errors?.['required']">Это поле обязательно.</div>
<div *ngIf="expiryDate?.errors?.['pattern']">Некорректная дата. Используйте формат MM/YY.</div>
</div>
</div>
<div class="textbox-container">
<label for="cvv">CVV:</label>
<input id="cvv" formControlName="cvv" type="password" class="input_cnt" />
<div *ngIf="cvv?.invalid && (cvv?.touched || cvv?.dirty)" class="error-message">
<div *ngIf="cvv?.errors?.['required']">Это поле обязательно.</div>
<div *ngIf="cvv?.errors?.['pattern']">Некорректный CVV. Введите 3 цифры.</div>
</div>
</div>
<div class="cnt_button">
<button type="submit" class="pay">Оплатить</button>
</div>
</form>
Валидация: Каждое поле имеет свою логику валидации, которая проверяет наличие значений и их соответствие определенным шаблонам. Используются как встроенные валидаторы (Validators.required, Validators.pattern), так и кастомные валидаторы (например, для проверки номера карты).
Для валидации номера карты у меня создан алгоритм Луна:
Для формы регистрации используем данные методы в сервисе, для работы с базой данных:
Сервисы — это так же обычные класссы. Нам нужно в конструктор класса инжектировать HTTP объект, и добавить декоратор Injectable, необходимый для работы сервиса. Для удобства создадим переменную apiUrl, которая содержит URL-адрес API, с которым будет взаимодействовать сервис. В данном случае это адрес локального сервера (localhost) и конечная точка для пользователей (/students)
submitStudent(student: Student): Observable<any> { return this.http.post(this.apiUrl, student).pipe( catchError(err => { console.error('Ошибка при отправке студента', err); return of(null); }) ); }
Отправляет данные студента на сервер, чтобы создать нового студента. Метод принимает объект student типа Student. this.http.post(this.apiUrl, student): отправляет POST-запрос на указанный apiUrl (например,http: //localhost:3000/students) с данными студента в теле запроса.
RxJS Pipeline:
pipe: Метод позволяет обрабатывать ответ или ошибку.
catchError: Если возникнет ошибка при отправке запроса, она будет перехвачена. В этом случае в консоль выводится сообщение об ошибке, и возвращается of(null), что позволяет продолжить выполнение без прерывания.
checkEmailExists(email: string): Observable<boolean> { return this.http.get<Student[]>(`${this.apiUrl}?email=${email}`).pipe( map(students => students.length > 0), catchError(() => of(false)) ); }
Проверяет существует ли введенный пользователь в базе данных. Метод принимает email как строку. this.http.get<Student[]>(${this.apiUrl}?email=${email}): отправляет GET-запрос с параметром email на сервер. Это вернет список студентов с указанным email.
RxJS Pipeline:
map: Параметр students содержит массив объектов Student. Функция проверяет, есть ли в массиве элементы, и возвращает true, если email существует, или false, если не существует.
catchError: В случае ошибки (например, при проблемах с сетью) метод вернет false.
uniqueEmailValidator(): AsyncValidatorFn {
return (control: AbstractControl): Observable<ValidationErrors | null> => {
const email = control.value;
if (!email) {
return of(null); // Если поле пустое, возвращаем null для валидации
}
return this.checkEmailExists(email).pipe(
map(exists => (exists ? { emailExists: true } : null)),
catchError(() => of(null)) // Возвращаем null в случае ошибки
);
};
}uniqueEmailValidator(): AsyncValidatorFn {
return (control: AbstractControl): Observable<ValidationErrors | null> => {
const email = control.value;
if (!email) {
return of(null); // Если поле пустое, возвращаем null для валидации
}
return this.checkEmailExists(email).pipe(
map(exists => (exists ? { emailExists: true } : null)),
catchError(() => of(null)) // Возвращаем null в случае ошибки
);
};
}
Асинхронный валидатор для проверки существования email в рамках реактивной формы.
В первой части метода проверяется, существует ли значение email. Если поле пустое (т.е., пользователь ничего не ввел), метод возвращает of(null), что означает отсутствие ошибки валидации. Это важно, чтобы избежать лишних запросов к серверу для пустых полей.
Если email не пустой, метод вызывает this.checkEmailExists(email), которая возвращает Observable<boolean>. Этот метод выполняет HTTP-запрос к API, чтобы проверить, существует ли указанный email в базе данных.
Если все корректно создаем и заносим данные в базу данных json файл. Используя метод
Теперь форма авторизации студента:
Шаблон авторизации
<app-header></app-header>
<div class="login-container">
<div class="login-block">
<h1>Введите свои данные</h1>
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<div class="form-group" [ngClass]="{'has-error': form.get('email')?.invalid && form.get('email')?.touched}">
<div class="input-group">
<span class="input-group-addon"><i class="fa fa-user ti-user"></i></span>
<input type="text" class="form-control" placeholder="Введите почту" formControlName="email">
</div>
<span class="form-help-text" *ngIf="form.get('email')?.invalid && form.get('email')?.touched">
<span *ngIf="form.get('email')?.errors?.['required']">Email не может быть пустым.</span>
<span *ngIf="form.get('email')?.errors?.['email']">Введите корректный email.</span>
</span>
</div>
<div class="form-group" [ngClass]="{'has-error': form.get('password')?.invalid && form.get('password')?.touched}">
<div class="input-group">
<span class="input-group-addon"><i class="fa fa-lock ti-unlock"></i></span>
<input [type]="showPassword ? 'text' : 'password'" class="form-control" placeholder="Введите пароль" formControlName="password">
<span class="password-icon" (click)="showPassword = !showPassword">
<i [ngClass]="showPassword ? 'fa fa-eye' : 'fa fa-eye-slash'"></i>
</span>
</div>
<span class="form-help-text" *ngIf="form.get('password')?.invalid && form.get('password')?.touched">
<span *ngIf="form.get('password')?.errors?.['required']">Пароль не может быть пустым.</span>
<span *ngIf="form.get('password')?.errors?.['minlength']">Пароль должен содержать минимум 8 символов.</span>
</span>
</div>
<button class="btn btn-primary" type="submit" [disabled]="form.invalid">Войти</button>
</form>
<div class="login-links">
<p class="text-center">Еще нет аккаунта? <a class="txt-brand" href="#">Регистрируйся</a></p>
</div>
</div>
</div>
<app-footer></app-footer>
В методе ngOnInit создается реактивная форма с двумя полями: email и password. Они имеют различные валидаторы для проверки на пустоту и формат email.
При отправке формы вызывается метод onSubmit, который выполняет следующие действия:
Проверяет, является ли форма валидной.
Если форма валидна, вызывается метод login из AuthService, передавая email и пароль.
После этого следует подписка на результат (response) вызова метода login:
В случае успешного входа пользователя перенаправляют на страницу студента.
Если вход не удался, пользователю отображается сообщение об ошибке.
Рассмотрим Сервис авторизации:
private apiUrl = 'http://localhost:3000/students';
Этот URL служит для указания конечной точки API, которая отвечает за студентов. Он используется в методе авторизации, чтобы отправить запрос на сервер и получить список студентов для проверки введенных учетных данных.
Метод login:
Выполняет GET-запрос к API для получения списка студентов.
Ищет пользователя с переданными email и паролем.
Если пользователь найден, сохраняет информацию о нем в localStorage, обновляет текущего пользователя BehaviorSubject, и возвращает данные пользователя.
Если пользователь не найден или происходит ошибка, возвращает null.
Аналогично реализована форма авторизации преподавателей и администратора: