I have some difficulties with figuring out how to make my modals draggable. I have reusable modals with its own service which is called to create one inside components.
confirm.modal.service.ts
import { Injectable } from "#angular/core";
import { NgbModal } from "#ng-bootstrap/ng-bootstrap";
import { Observable, from, EMPTY, throwError } from "rxjs";
import { catchError, tap } from "rxjs/operators";
import { ConfirmModalComponent } from "./confirm-modal.component";
export interface ConfirmOptions {
title: string;
subtitle?: string;
errorOnClose?: boolean;
}
#Injectable({ providedIn: "root" })
export class ConfirmModalService {
constructor(private modalService: NgbModal) {}
confirm(options: ConfirmOptions): Observable<boolean> {
const modalRef = this.modalService.open(ConfirmModalComponent, {
centered: true
});
modalRef.componentInstance.title = options.title || "Are you sure?";
modalRef.componentInstance.subtitle = options.subtitle || null;
return from(modalRef.result).pipe(
tap(),
catchError(err =>
options.errorOnClose
? throwError(err || "not confirmed")
: EMPTY
)
);
}
}
confirm.modal.module.ts
import { NgModule } from "#angular/core";
import { CommonModule } from "#angular/common";
import { DragDropModule } from "#angular/cdk/drag-drop";
import { ConfirmModalComponent } from "./confirm-modal.component";
#NgModule({
imports: [
CommonModule,
DragDropModule
],
declarations: [ConfirmModalComponent],
exports: [ConfirmModalComponent]
})
export class ConfirmModalModule {}
confirm.modal.component.ts
import { Component, Input } from "#angular/core";
import { NgbActiveModal } from "#ng-bootstrap/ng-bootstrap";
#Component({
selector: "app-confirm-modal",
templateUrl: "./confirm-modal.component.html",
styleUrls: ["./confirm-modal.component.scss"]
})
export class ConfirmModalComponent {
#Input() title: string;
#Input() subtitle: string;
constructor(public activeModal: NgbActiveModal) {}
public accept(): void {
this.activeModal.close(true);
}
public dismiss(): void {
this.activeModal.close(false);
}
}
confirm.modal.component.html
<div class="modal-body">
<div class="modal-body__header">
<span>{{ title }}</span>
</div>
<div *ngIf="subtitle" class="modal-body__text">
<span>{{ subtitle }}</span>
</div>
<div class="modal-body__button-row">
<button class="btn btn-primary" (click)="accept()">Yes</button>
<button class="btn btn-light" (click)="dismiss()">Cancel</button>
</div>
</div>
So I want to make the whole modal be draggable with Angular built-in DragDropModule, hence I should add cdkDrag inside element with class='modal-content' but I don't how to achieve that with current setup. NgbModalOptions provides functionality to add class only but not attribute directive.
I know that there is easier solution with JQuery draggable, but I would like to avoid that.
I was thinking about using #ViewChildren for each page but it doesn't seem to the best solution for me.
Thanks for any help!
Just wrap your modal inside a container and add the cdkDragRootElement to it as per the documentation. You will also have to add this class as an option when you open the dialog from the component.ts.
<ng-template #content
let-modal>
<div
cdkDrag
cdkDragRootElement=".your-custom-dialog-class">
<div class="modal-header">
</div>
<div class="modal-body">
</div>
<div class="modal-footer">
</div>
</div>
</ng-template>
The code for the component.ts
const options: NgbModalOptions = {
windowClass: 'your-custom-dialog-class'
};
this.modalService.open(this.content, options);
Related
I created all-question component it is visible but when i click on particular question view that redirect to that id but not got details on the id page. here i attached some files,
Admin Service file
Model file
Detail question component file
Detail question ts file
All question component file
All question ts file
admin.service.ts - this is admin service file
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { Observable } from 'rxjs';
import { Admin } from "../model/admin";
#Injectable({
providedIn: 'root'
})
export class AdminService {
private ROOT_URL = "http://localhost:3300/questions";
private httpOptions = {
headers: new HttpHeaders().set("Content-Type", "application/json")
};
constructor(private http: HttpClient) { }
getQuestion(): Observable<Admin[]> {
return this.http.get<Admin[]>(this.ROOT_URL);
}
getQue(id: string){
return this.http.get<Admin>(`${this.ROOT_URL}/${id}`);
}
addQue(admin){
return this.http.post<any>(this.ROOT_URL, admin, this.httpOptions);
}
}
**model=>admin.ts** - this is model file
export interface Admin {
description: String,
// alternatives: String
alternatives: [
{
text: {
type: String,
required: true
},
isCorrect: {
type: Boolean,
required: true,
default: false
}
}
]
}
**que-detail.comp.html** - this is question detail component file
<div class="card">
<div class="card-body">
<h4>Question: {{admin.description}}</h4>
<h4>Options: {{admin.alternatives}}</h4>
</div>
</div>
**que-detail.comp.ts** - this is question details ts file
import { Component, OnInit } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
import { Subscription } from 'rxjs';
import { Admin } from '../model/admin';
import { AdminService } from '../service/admin.service';
#Component({
selector: 'app-question-detail',
templateUrl: './question-detail.component.html',
styleUrls: ['./question-detail.component.css']
})
export class QuestionDetailComponent implements OnInit {
id: string;
admin: Admin;
adminSub$: Subscription;
constructor(private adminService: AdminService, private route: ActivatedRoute ) { }
ngOnInit(): void {
this.id = this.route.snapshot.paramMap.get("id");
this.adminSub$ = this.adminService.getQue(this.id).subscribe(admin => {
this.admin = admin;
});
}
}
**all-que.comp.html** - this is all question component file
<div class="card bg-dark text-white my-4 dashboard-bg">
<div class="card-body text-center">
<ul class="questions" *ngIf="admin$ | async as questions">
<li *ngFor="let question of questions">
<h4 class="card-title my-2">{{question.description}}</h4>
<h5 *ngFor="let x of question.alternatives">{{x.text}}</h5>
<a [routerLink]="question._id">View</a>
</li>
</ul>
</div>
</div>
**all-que.comp.ts** - this is all question ts file
import { Component, OnInit } from '#angular/core';
import { Observable } from 'rxjs';
import { Admin } from '../model/admin';
import { AdminService } from '../service/admin.service';
#Component({
selector: 'app-all-question',
templateUrl: './all-question.component.html',
styleUrls: ['./all-question.component.css']
})
export class AllQuestionComponent implements OnInit {
admin$: Observable<Admin[]>;
constructor(private adminService: AdminService) { }
ngOnInit(): void {
this.admin$ = this.adminService.getQuestion()
}
}
Your Question Details Component should be like,
<div class="card">
<div class="card-body">
<h4>Question: {{admin.description}}</h4>
<h5>Options:</h5>
<ng-container *ngFor="alternative of admin.alternatives">
<h6>{{alternative.text}}</h6>
</ng-container>
</div>
You are just using the undefined questions variable ig.
All files are attached as below,
component.html - This is component file
component.ts - This is component ts file
admin-service - This is service file
admin.ts - This is model file
add-que.html - This is component html file.
<div>
<form [formGroup]="adminForm" (ngSubmit)="newQuestion()">
<div class="form-group">
<label for="exampleInputEmail1">Question</label>
<input formControlName="description" type="description" placeholder="Enter question"
class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp">
<small class="text-danger"
*ngIf="!adminForm.get('description').valid && adminForm.get('description').touched">
Please Enter a Question</small>
</div>
<div class="form-group m-auto">
<div class="col-6">
(a)<input formControlname="alternatives" type="text">
(b)<input formControlname="alternatives" type="text">
</div>
<div class="col-6">
(c)<input formControlname="alternatives" type="text">
(d)<input formControlname="alternatives" type="text">
</div>
</div>
<div class="m-auto">
<input type="submit" value="Add" class="btn btn-primary"/>
</div>
</form>
</div>
**add-que.ts** - This is component ts file.
import { Component, OnInit } from '#angular/core';
import { FormControl, FormGroup, Validators } from '#angular/forms';
import { Router } from '#angular/router';
import { AdminService } from '../service/admin.service';
#Component({
selector: 'app-add-question',
templateUrl: './add-question.component.html',
styleUrls: ['./add-question.component.css']
})
export class AddQuestionComponent implements OnInit {
adminForm = new FormGroup({
description: new FormControl("", [Validators.required]),
alternatives: new FormControl("", [Validators.required])
});
constructor(private adminService: AdminService, private router: Router) { }
ngOnInit(): void {
}
newQuestion(){
if(this.adminForm.valid){
this.adminService.addQue(this.adminForm.value).subscribe(res => {
this.adminForm.reset();
this.router.navigate(["/admin"]);
})
}
}
}
**admin-service.ts** - This is service ts file.
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { Observable } from 'rxjs';
import { Admin } from "../model/admin";
#Injectable({
providedIn: 'root'
})
export class AdminService {
private ROOT_URL = "http://localhost:3300/questions";
private httpOptions = {
headers: new HttpHeaders().set("Content-Type", "application/json")
};
constructor(private http: HttpClient) { }
getQuestion(): Observable<Admin[]> {
return this.http.get<Admin[]>(this.ROOT_URL);
}
getQue(id: string){
return this.http.get<Admin>(`${this.ROOT_URL}/${id}`);
}
addQue(admin){
return this.http.post<any>(this.ROOT_URL, admin, this.httpOptions);
}
}
**admin.ts** - This is model ts file.
export interface Admin {
description: String,
alternatives: [
{
text: {
type: String,
required: true
},
isCorrect: {
type: Boolean,
required: true,
default: false
}
}
]
}
I did created add-question component with form as above. I did tried to add new question through form in angular but not get anything. i did attached my files as above.
you sould'nt use this.router.navigate(["/admin"]); to try "refresh a component". That's no work (there are no changes). In general you has a component with a ngOnInit. Simply get out the code under a ngOnInit in a function and call this function in subscribe, e.g.
ngOnInit()
{
this.init()
}
init()
{
..here you initialize your variables
}
submit(){
...
.subscribe(res=>{
....
this.init()
})
}
Hey I am trying to display a simple error message on my login page if the login fails. Following is my login.component.html:
<div class="container shadow login-container">
<div class="row">
<div class="col-sm-12 text-center">
<div class="error-message">
<app-server-error [errorMessage]="error" ></app-server-error> -----> not displaying on screen
</div>
<div class="login-form-container">
<div class="login-input-container">
<input [(ngModel)]="user.email" type="email" placeholder="Enter your email" class="buddha-input"/>
</div>
<div class="login-input-container">
<input [(ngModel)]="user.password" type="password" placeholder="Enter password" class="buddha-input"/>
</div>
<div class="login-input-container">
<button (click)="tryLogin()" class="login-form-button">Login</button>
</div>
</div>
</div>
</div>
</div>
Following is my server-error.component.html:
<p>
{{errorMessage}}
</p>
Following is my server-error.component.ts
import { Component, OnInit, Input } from '#angular/core';
#Component({
selector: 'app-server-error',
templateUrl: './server-error.component.html',
styleUrls: ['./server-error.component.css']
})
export class ServerErrorComponent implements OnInit {
#Input() public errorMessage: string;
constructor() { }
ngOnInit() {
}
}
"Error" is not showing up on the screen and I am not getting errors on console either. Please do let me know how I can fix this? Thank you
#Input() public errorMessage: string; expects error to be a string.
Define error like this in .ts
error = 'error message'
Working Demo
With [errorMessage]="error", error should be a variable.
So you need to define it in your component.
But if you want to display the string "error",
then pass it like this [errorMessage]="'error'" or errorMessage="error"
The other posted answer may solve your problem but I would go with Service which will be responsible for showing an error from everywhere in the Application and also you can have a full control on the error component as it is centralized at a one place:
error.service:
import { Injectable } from '#angular/core';
import { BehaviorSubject } from 'rxjs';
#Injectable()
export class ErrorService {
constructor() { }
private errorMessages = new BehaviorSubject('');
errorMessage = this.errorMessages.asObservable();
showError(message: string) {
this.errorMessages.next(message)
}
}
Error.Component.ts:
import { Component, OnInit, Input } from '#angular/core';
import {ErrorService } from "../error.service";
#Component({
selector: 'app-server-error',
templateUrl: './server-error.component.html',
styleUrls: ['./server-error.component.css']
})
export class ServerErrorComponent implements OnInit {
private errorMessage : any;
constructor(public errorService : ErrorService) { }
ngOnInit() {
this.errorService.errorMessage.subscribe(value => {
this.errorMessage = value;
})
}
}
App.Component.html:
// show it in app.component
<div class="error-message">
<app-server-error></app-server-error>
</div>
Use (in Login Component):
import { Component, OnInit } from "#angular/core";
import {ErrorService } from "../error.service";
#Component({
selector: "app-login",
templateUrl: "./login.component.html",
styleUrls: ["./login.component.css"]
})
export class LoginComponent implements OnInit {
constructor(public errorService: ErrorService) {}
user = {};
ngOnInit() {}
tryLogin(){
this.errorService.showError('An error');
}
}
I have a problem with Angular 2 routing. When I click on my link to get the team details, it takes the right route and loads the component specified (TeamComponent). But, immediately "gets back" to the previous component (TeamsComponent), which is the teams list.
This is the structure of my project:
/app
|_shared
|_team
|_team.component.css
|_team.component.html
|_team.component.ts
|_team.model.ts
|_team.service.ts
|_team-list
|_team-list.component.css
|_team-list.component.html
|_team-list.component.ts
|_teams
|_teams.component.css
|_teams.component.html
|_teams.component.ts
|_teams.module.ts
|_teams-routing.module.ts
First, I set the routes on teams-routing.module.ts:
import { NgModule } from '#angular/core';
import { RouterModule, Routes } from '#angular/router';
import { TeamsComponent } from './teams.component';
import { TeamComponent } from '../shared/team/team.component';
const teamsRoutes: Routes = [
{
path: 'team/:id',
component: TeamComponent
},{
path: 'teams',
component: TeamsComponent
}
];
#NgModule({
imports: [
RouterModule.forChild(teamsRoutes)
]
})
export class TeamsRoutingModule { }
Load the team list from the teamService on teams.component.ts and send it to team-list on teams.component.html:
import { Component, OnInit } from '#angular/core';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/from';
import { TeamService } from '../shared/team/team.service';
import { Team } from '../shared/team/team.model';
#Component({
selector: 'app-teams',
templateUrl: './teams.component.html',
styleUrls: ['./teams.component.css']
})
export class TeamsComponent implements OnInit {
teamList: Team[] = [];
constructor(private teamService: TeamService) { }
ngOnInit() {
this.teamService.getTeams().subscribe(teams => {
Observable.from(teams).subscribe(team => {
this.teamList.push(team);
});
});
}
}
teams.component.html
<section class="teams">
<app-team-list [teams]="teamList"></app-team-list>
</section>
Then, with my teams list, I set the HTML list on team-list.component.html:
<section *ngFor="let team of teams" class="team_list">
<div class="card" style="width: 20rem;">
<img class="card-img-top" src="/assets/logos/{{team.logo}}" alt="Team Logo">
<div class="card-block">
<h4 class="card-title">{{team.name}}</h4>
<p class="card-text">{{team.location}}</p>
<a routerLink="/team/{{team.id}}" class="btn btn-primary">Team Info</a>
</div>
</div>
</section>
Finally, I get the team info from param "id" and the service in team.component.ts:
import { Component, Input, OnInit } from '#angular/core';
import { Router, ActivatedRoute, Params } from '#angular/router';
import { Team } from './team.model';
import { TeamService } from "./team.service";
import 'rxjs/add/operator/switchMap';
#Component({
selector: 'app-team',
templateUrl: './team.component.html',
styleUrls: ['./team.component.css']
})
export class TeamComponent implements OnInit {
team: Team = null;
constructor(private teamService: TeamService,
private activatedRoute: ActivatedRoute,
private router: Router
) {}
ngOnInit() {
let teamId: number = this.activatedRoute.snapshot.params['id'];
console.log("Vamos a buscar el equipo");
this.teamService.getTeamById(teamId).subscribe(team => this.team = team);
}
}
It loads the TeamComponent HTML with the team data, but gets back to /teams direction (and doesn't print the team list). I tried to change the routes names (/detail/:id for example) but still doesn't work. Any suggestions? Thanks in advance.
Ok, got it. Your request will be exuted async, so at the creation-time of your component, team is null. I think you have a binding like this in your TeamComponent:
{{ team.name }}
If team is null, name cannot be accessed and it crashes. To be sure the html will be rendered without errors, use the elvis-operator like this:
{{ team?.name }}
This will only access name if team is not null or undefined
Update: The getTeamById service
getTeamById(id: number): Observable<Team> {
let team: Team = null;
return this.http.get(this.urlTeams+'/'+id)
.map(response => {
let dbTeam: any = response.json();
for(let i in dbTeam) {
team = new Team(dbTeam[i].teamId,dbTeam[i].teamName,dbTeam[i].teamLocation,dbTeam[i].teamFoundation,dbTeam[i].teamDivision,dbTeam[i].teamConference,dbTeam[i].teamStadium,dbTeam[i].teamAttendance,dbTeam[i].teamLogo,dbTeam[i].teamStadiumPhoto);
}
return team;
})
.catch(this.handleError);
}
I'm currently using Angular Forms version 2.0.0 and trying to make a contact us modal with a contact form inside.
Immediately after the ContactComponent loads, I get:
EXCEPTION: this.form._updateTreeValidity is not a function
I've already seen some other stack posts suggesting that using FormGroup instead of FormBuilder to init the form object in the component constructor is now standard with the new API so I've updated that.
I import ReactiveFormsModule and FormsModule along with all the form related components and the error doesn't seem to be module related.
My TypeScript isn't throwing errors in compile time and Visual Studio Intellisense seems to be able to find all FormGroup functions just fine so why is this happening at runtime?...
My code:
contact.component.ts:
import { Component, Input, ViewChild } from '#angular/core';
import { ApiService } from '../../../services/api.service';
import { ModalComponent } from 'ng2-bs3-modal/ng2-bs3-modal';
import { Router, ActivatedRoute, Params } from '#angular/router';
import { FormsModule, ReactiveFormsModule, FormGroup, FormControl, Validators } from '#angular/forms';
import 'rxjs/Rx';
declare var jQuery: any;
#Component({
selector: 'my-contact',
templateUrl: 'app/modules/footer/contact/contact.html'
})
export class ContactComponent {
private contactForm: FormGroup;
private invalidEmail: boolean;
private invalidSubject: boolean;
private invalidMessage: boolean;
constructor(private apiService: ApiService, private router: Router, private route: ActivatedRoute) {
this.contactForm = new FormGroup({
emailControl: new FormControl('', <any>Validators.required),
subjectControl: new FormControl('', <any>Validators.required),
messageControl: new FormControl('', <any>Validators.required)
});
}
submit() {
if (this.contactForm.valid) {
this.apiService.sendMessage(this.contactForm.controls['emailControl'].value, this.contactForm.controls['subjectControl'].value, this.contactForm.controls['messageControl'].value);
}
if (!this.contactForm.controls['emailControl'].valid) {
this.invalidEmail = true;
}
if (!this.contactForm.controls['subjectControl'].valid) {
this.invalidSubject = true;
}
if (!this.contactForm.controls['messageControl'].valid) {
this.invalidMessage = true;
}
}
ngOnInit() {
this.invalidEmail = false;
this.invalidSubject = false;
this.invalidMessage = false;
}
}
contact.html:
<modal-header class="c-no-border" [show-close]="true">
<h4 class="modal-title text-uppercase">Send us a message</h4>
</modal-header>
<form novalidate #contactForm [formGroup]="contactForm" (ngSubmit)="submit()">
<div class="modal-body">
<div class="form-group">
<label for="email" class="control-label">Email</label>
<input name="email" formControlName="emailControl" placeholder="" type="text" class="c-square form-control c-margin-b-20" id="email">
<div class="c-font-red-1" *ngIf="invalidEmail" style="position: absolute;">*Required</div>
<label for="subject" class="control-label">Subject</label>
<input name="subject" formControlName="subjectControl" placeholder="" type="text" class="c-square form-control c-margin-b-20" id="subject">
<div class="c-font-red-1" *ngIf="invalidSubject" style="position: absolute;">*Required</div>
<textarea formControlName="messageControl" style="resize: vertical;" class="c-square form-control c-margin-b-20" id="content" (keyup.enter)="submit()"></textarea>
<div class="c-font-red-1" *ngIf="invalidMessage" style="position: absolute;">*Required</div>
</div>
</div>
<modal-footer class="c-no-padding">
<button type="button" class="btn c-btn-square c-btn-bold c-btn-uppercase pull-right">Cancel</button>
<button type="submit" class="btn c-theme-btn c-btn-square c-btn-bold c-btn-uppercase pull-right" style="margin-right: 10px;">Send</button>
</modal-footer>
</form>
app.module.ts:
import { NgModule, enableProdMode } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { AppComponent } from './app.component';
import { FormsModule, ReactiveFormsModule } from '#angular/forms';
import { Ng2Bs3ModalModule } from 'ng2-bs3-modal/ng2-bs3-modal';
import { QueuesModule } from './modules/queues/queues.module';
import { OrderModule } from './modules/order/order.module';
import { AccountModule } from './modules/account/account.module';
import { AdminModule } from './modules/admin/admin.module';
import { routing } from './app.routing';
import { GridModule } from '#progress/kendo-angular-grid';
import { SplashComponent } from './modules/splash/splash.component';
import { ContactComponent } from './modules/footer/contact/contact.component';
import { SharedModule } from './shared/shared.module';
import { EmailValidator } from './shared/utilities/custom-validators'
import { CookieService } from 'angular2-cookie/services/cookies.service';
import { HttpModule, Response } from '#angular/http';
import { StringService } from './services/string.service';
import { ApiService } from './services/api.service';
import { UserService } from './services/user.service';
import { OrderService } from './services/order.service';
import { OrderGuard } from './services/order-guard.service';
import { FooterComponent } from './modules/footer/footer.component';
import { ErrorComponent } from './modules/error/error.component';
import { CustomFormsModule } from "ng2-validation";
#NgModule({
imports: [
BrowserModule,
FormsModule,
ReactiveFormsModule,
HttpModule,
QueuesModule,
OrderModule,
AccountModule,
AdminModule,
routing,
GridModule,
SharedModule,
Ng2Bs3ModalModule,
CustomFormsModule
],
declarations: [
AppComponent,
SplashComponent,
FooterComponent,
ErrorComponent,
ContactComponent
],
providers: [
StringService,
ApiService,
UserService,
CookieService,
OrderService,
OrderGuard
],
bootstrap: [AppComponent],
exports: [
]
})
export class AppModule {
}
Binding the template variable #contactForm appears to cause a name conflict and blow up the template processor as it tries to turn the attached template variable into an NgForm on the backend. Everywhere I have seen model driven forms used there is no template variable binding on the form, whereas there is a #tv="ngForm" utilized in template driven forms. It appears there was a miss on the mixing of the two forms approaches which resulted in the error.
Simply removing it will resolve the issue.
When you incorrectly add to your template formGroup="..." instead of [formGroup]="..." you'll also get this error message.
It's been a while, but for me the problem was passing the formGroup to the template instead of referencing it directly.
E.g. this is not working
<ng-template #selectField
let-field
let-group
let-frmName="frmName">
<ng-container [formGroup]="group">
<mat-form-field fxFlex="32"
floatLabel="always">
<mat-label>{{field.label}}</mat-label>
<mat-select [formControlName]="frmName"
[required]="field.required">
<mat-option *ngFor="let option of field.options"
[value]="option.value">
{{option.description}}
</mat-option>
</mat-select>
</mat-form-field>
</ng-container>
</ng-template>
If I use directly my formGroup instance it works just fine.
Hope this helps.
I had a dynamic form. I got above error since I didn't init it inside the ngOnInit()
Solution:
checkInForm: FormGroup;
ngOnInit(){
this.checkInForm = this.formBuilder.group({});
}
Sometime this error is caused when you use a [formGroup] to pass the string of the formGroup name. Use formGroupName instead.
This can happen when you're using the input name formControl or formGroup on a custom component which isn't a form control.
Change your custom component to accept a different input name:
Change this
#Input()
formGroup: string
// ^ Causes issues
To this
#Input()
group: string
I forgot to pass my form to the reusable input <app-input [form]="myForm"></app-input>
app-input.component.html
<form [formGroup]="form">
<input [type]="type" [placeholder]="placeholder [formControlName]="formControlName" />
</form>
app-input.component.ts
#Input() type: string = '';
#Input() placeholder: string = '';
#Input() formControlName: string = ''
#Input() form: FormGroup = {} as FormGroup;