How to share the data up from transcluded content angular 4 - javascript

I have a generic modal
<div class="modal-header">
<h1 mat-dialog-title [innerHTML]="data.title"></h1>
</div>
<div mat-dialog-content>
<ng-content></ng-content>
</div>
<mat-dialog-actions>
<button></button>
</mat-dialog-actions>
and i have the content for the modal
<modal>
<form #form="ngForm" (ngSubmit)="addNewPerson(person)">
<mat-form-field>
<input type="text" [(ngModel)]="person.firstName" name="firstName"
placeholder="First Name" matInput required>
</mat-form-field>
</form>
</modal>
How can i send the data from this form in generic modal component? Thanks a lot
Sending data from child up to generic modal with #Output() doesn't work in this case. Also will be nice to see how to send data down in component from the generic modal, as we can do with #Input() in simple components hierarchy.

You can add a custom directive that grabs a reference to the NgForm directive
<form myFormDirective #form="ngForm" (ngSubmit)="addNewPerson(person)">
#Directive({
selector: '[myFormDirective]',
})
export class MyFormDirective {
constructor(form:NgForm, myService:MyService) {
console.log(form.value);
myService.theForm = form;
}
}
then you can use a shared service to pass the NgForm reference along and on demand read the form value from theForm.value or initiate some other actions.;

Related

Add dynamic component in the DOM

I have a project in Angular in which I have created a dialog with several fields for a component. What I want is to take advantage of this dialog for other components, but the amount of data varies.
This is my dialog.component.html:
<div class="dialog-container">
<mat-form-field>
<input matInput placeholder="Name" [(ngModel)]="data.name">
</mat-form-field>
<mat-form-field>
<input matInput placeholder="Phone" [(ngModel)]="data.phoneNumber">
</mat-form-field>
<mat-form-field>
<input matInput placeholder="Email" [(ngModel)]="data.email">
</mat-form-field>
</div>
I want to know if it is possible to create only one mat-form component and display as many as necessary, passing it the number of fields that I want to generate.
This is what I want the dialog.component.html to contain, to achieve the same as above (I don't know if this is possible):
<div class="dialog-container">
<mat-form-field>
<input matInput placeholder={{place}} [(ngModel)]={{data}}>
</mat-form-field>
</div>
From only one mat-form-field is it possible to show the same result as the first example?
You could create a custom directive :
import { Directive, Input } from '#angular/core';
import { NgForOf } from '#angular/common';
#Directive({
selector: '[ngFor][ngForRepeat]'
})
export class NgForRepeat<T> extends NgForOf<T> {
#Input() set ngForRepeat(repeat: number) {
this.ngForOf = new Array(repeat >= 0 ? repeat : 0);
}
}
And use it as follow in you template :
<ng-container *ngForRepeat="3">
<mat-form-field>
<input matInput placeholder={{place}} [(ngModel)]={{data}}>
</mat-form-field>
</ng-container>

Grab template form input in Vue 3 class components (like done in Angular)

I started vue 3 today, and opted for the class-based approach in the cli. I'm from the Angular background, so forgive me for thinking like Angular. Every example I see (even in the docs) is still using the Vue({...}) thing, however, I wanna do something like this (still thinking Angular-ish)
In angular, I can do this
<form #formData="ngForm" (ngSubmit)="onSubmit(formData.value)">
<input (ngModel)="name" name="name" placeholder="name">
</form>
Then in component
...
export class AppComponent {
public name!: string;
onSubmit(formData: string) {
console.log(formData)
}
}
What would be the vue 3 class components approach like the above?
I currently have this in vue 3
export default class Welcome extends Vue {
name!: string;
onSubmit(formData: any) {
console.log(formData)
}
}
<template>
<div>
<form #submit.prevent="onSubmit">
<p>
<label for="name">Name</label> <br>
<input type="text" placeholder="name" id="name" name="name" v-model="name"/>
</p>
<button type="submit">Send</button>
</form>
</div>
</template>
What changes do I need to do to the above to connect the form in template to the component?
Without a third party library there isn't the same type of functionality that angular provides. Angular is doing a bunch of additional things to enhance the form object for validation and value tracking, and Vue natively does not do that. However you could instead put your data properties in an object to group them together. That way when you need to access them in something like the submit event to perhaps send all the values to an API, you can simply refer to that object instead of having to handle/build each property separately:
Class:
export class AppComponent {
// create object with bound form properties
public values: { name: string; } = { name: '' };
onSubmit() {
console.log(this.values); // { name: '' }
// axios.post('/api', this.values).then(res => console.log(res.data));
}
}
Template:
<template>
<div>
<form v-on:submit.prevent="onSubmit">
<p>
<label for="name">Name</label> <br>
<input type="text" placeholder="name" id="name" name="name" v-model="values.name"/>
</p>
<button type="submit">Send</button>
</form>
</div>
</template>
That being said, if you do need advanced form features like validation, sanitization, and similar there are plenty of libraries that do it well.
Hopefully that helps!
The native FormData constructor takes a <form> element, which creates the values of the form for all named inputs (<input>s or <textarea>s with name attribute).
So you could update your onSubmit method to retrieve the form data values:
export default class Welcome extends Vue {
name!: string;
onSubmit(formData: any) {
const form = e.target
const formData = new FormData(form)
}
}
Example using the Options API:
<template>
<div>
<form #submit.prevent="onSubmit">
<p>
<label for="name">Name</label> <br>
<input type="text" placeholder="name" id="name" name="name" v-model="name"/>
</p>
<button type="submit">Send</button>
</form>
</div>
</template>
<script>
export default {
methods: {
onSubmit(e) {
const form = e.target
const formData = new FormData(form)
console.log({ formData: Array.from(formData.entries()) })
}
}
}
</script>
demo

How do I create popup with data user enters into a form?

<!--Currently making web app that is a bunch of forms.
I created using Visual Studio 2017 ASP.NET Core 2.2 with Angular 7.
When user fills out form not all form data is required.
After user enters desired form information I want there to be a button that brings up a popup that contains all user entered data and corresponding questions.
If they leave a textbox on the form blank or enter n/a I do not want the information to appear in the popup.
I want the popup to have a button to copy data that will be pasted into form on another site.
I have created all my forms and also know how to create a popup.
My only issue is I have no idea how to populate/transfer user entered information from form into the popup and excluding the text boxes not filled out by user.-->
<!-- this is th .html -->
<form [formGroup]="Form" (ngSubmit)="onSubmit()">
<div class="form-row align-items-center">
<div class="col-auto">
<br />
//
<div class="form-group">
<label for="Select1">Select your...</label>
<select class="form-control" formControlName="Select1">
<option>...</option>
<option>...</option>
<option>Other</option>
</select>
</div>
<div class="form-group">
<label for="Number">some info </label>
<textarea class="form-control" formControlName="Number" rows="1" placeholder="Separate case numbers by comma"></textarea>
</div>
<div class="form-group">
<label for="Purpose">Purpose of info: </label>
<textarea class="form-control" formControlName="Purpose" rows="1"></textarea>
</div>
<div class="form-group">
<label for="name">Name of info: </label>
<textarea class="form-control" formControlName="name" rows="1"></textarea>
</div>
<
</div>
</div>
<p>
Form Value: {{ Form.value | json }}
</p>
<p>
Form Status: {{ Form.status }}
</p>
<button type="submit" class="btn btn-default" [disabled]="!labForm.valid">Build Lab Request</button>
<!-- Trigger the modal with a button -->
<button type="button" class="btn btn-info btn-lg" data-toggle="modal" data-target="#myModal">Open Template</button>
<!-- Modal -->
<div id="myModal" class="modal fade" role="dialog">
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h4 class="modal-title text-left">Lab Access Template</h4>
</div>
<div class="modal-body">
<p>Want user entered form data to appear on popup.</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Copy Template</button>
</div>
</div>
</div>
</div>
</form>
<!--this is the .ts-->
import { Component } from '#angular/core';
import { FormBuilder, FormControl, Validator, FormGroup, Validators, FormArray } from '#angular/forms';
#Component({
selector: 'app-',
templateUrl: './.component.html',
styleUrls: ['./.component.scss']
})
/** component*/
export class Component {
Form = new FormGroup({
Select1: new FormControl(''),
Number: new FormControl('', Validators.required),
Purpose: new FormControl(''),
name: new FormControl('', Validators.required),
});
onSubmit() {
console.warn(this.labForm.value);
}
/** LabAccess ctor */
constructor(private fb: FormBuilder) { }
}
What you're attempting to do is pretty straightforward, and you've got a lot of things already in place (like reactive forms) to make it happen.
Your modal you've got is fine, but you probably ought to use a separate component for the popup, and to pass information to it, it would look like this:
Within your calling template, include some template that will have a template for the popup or modal or whatever, which contains the component, see below:
<ng-template #formPopup>
<form-popup
[select1]="Form.get( 'Select1' ).value"
[number]="Form.get( 'Number' ).value"
[purpose]="Form.get( 'Purpose' ).value"
[name]="Form.get( 'name' ).value">
</form-popup>
</ng-template>
This will pass these values in as inputs as they are into your component when it's created:
export class FormPopupComponent {
#Input() select1 : any;
#Input() number : number;
#Input() purpose : string;
#Input() name : string;
// Do things with these inputs as needed in this component
}
On click, as you pointed out, you'll also have to open your dialog or popup or whatever, which is described in detail here. But here's a quick summary - in your component for your original form-containing template, you'll need a reference to your popup, which we've already created:
#ViewChild( 'formPopup' ) formPopupRef : TemplateRef<any>;
constructor(public dialog: MatDialog) {}
openDialog(): void {
const dialogRef = this.dialog.open( this.formPopupRef );
}

Angular ReferenceError : Calling typescript function from html form or button

So I'm pretty new to using Angular and I have a new Angular component, one TS file and one HTML file. In the HTML File, I have a form that is supposed to call a function in the corresponding typescript file when the submit button is pressed. Seems simple, but I'm constantly getting the following error :
Uncaught ReferenceError: validateLogin is not defined
at :4200/HTMLInputElement.onclick (http:/localhost:4200/validateLogin();?email=email%40lol.com&password=password:13:283)
onclick # validateLogin();?email=email%40lol.com&password=password:13
VM569:1 Uncaught ReferenceError: validateLogin is not defined
at :1:1
Two ReferenceErrors, one for the attempt with onclick and once for the attempt with the action attribute in form. On submit, I want the div to disappear, and the text "Success" to display. Here is the two files :
import { Component } from '#angular/core';
import { NullAstVisitor } from '#angular/compiler';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'TaManagementApp';
toggleLoginHTML = true;
constructor() {
this.toggleLoginHTML = true;
}
validateLogin() {
// if ()
this.toggleLoginHTML = false;
}
}
And
<!--The content below is only a placeholder and can be replaced.-->
<div *ngIf=toggleLoginHTML>
<div style="text-align:center">
<h1>
TA Management System
</h1>
</div>
<button onclick='validateLogin()'></button>
<div style="text-align:center">
<form action="javascript:validateLogin()">
Email: <input type="email" name="email" value="email"><br>
Password: <input type="password" name="password" value="password"><br>
<input type="submit" value="Submit" onclick='validateLogin()'>
</form>
</div>
</div>
<div *ngIf=!toggleLoginHTML>
<h1>
Success
</h1>
</div>
Any insight for a beginner would be appreciated, thank you!
Like user184994 and qiAlex said, you should use Angular built in click binding by using (click)="validateLogin()" instead of onclick.
However I wanted to suggest you to try an take a look at the Angular Reactive Form Guide in which they explain fairly well how to implement a straightforward binding between your form and your model and possibly build very complex forms.
Also take a look at the Form Validation Guide for some deeper information on how to validate your form inputs.
Instead of
<button onclick='validateLogin()'></button>
...
<input type="submit" value="Submit" onclick='validateLogin()'>
Try
<button (click)="validateLogin()"></button>
...
<input type="submit" value="Submit" (click)="validateLogin()">

How to disable a Dynamic forms in Angular2

I have created a dynamic form based on data[fields] from JSON, but I need the form to be initially disabled, so that when we click on Edit then only form becomes editable.
Here is my code for form:
<div class="col-md-8 " [ngSwitch]="fieldInfo.dataTypeName">
<input *ngSwitchCase="'Text'"
class="form-control"
[(ngModel)]="pageInfoBeans.nameValueMap[fieldInfo.name]"
name="{{fieldInfo.name}}"
[required]="fieldInfo.preRequiredInd"
[maxLength]="fieldInfo.fieldSize">
<input *ngSwitchCase="'Email Address'"
type="email"
class="form-control"
[(ngModel)]="pageInfoBeans.nameValueMap[fieldInfo.name]"
name="{{fieldInfo.name}}"
[required]="fieldInfo.preRequiredInd"
[maxLength]="fieldInfo.fieldSize">
and in my component HTML which populates from above switch case :
<app-form class="" [fieldInfo]="fieldItem.fieldInfo" [pageInfoBeans]="pageInfoBeans"></app-form>
Initially set the form to disabled.
component.ts
showForm?:boolean = false;
component.html
<button (click)="showForm = !showForm">Edit</button>
<form *ngIf="showForm">
...form markup
</form>
You need to do something like this:
<button class='form-control' (click)='isEditable = !isEditable'>Edit Mode</button>
<div class="col-md-8 " *ngIf='isEditable' [ngSwitch]="fieldInfo.dataTypeName">
<input *ngSwitchCase="'Text'"
class="form-control"
[(ngModel)]="pageInfoBeans.nameValueMap[fieldInfo.name]"
name="{{fieldInfo.name}}"
[required]="fieldInfo.preRequiredInd"
[maxLength]="fieldInfo.fieldSize" />
<input *ngSwitchCase="'Email Address'"
type="email"
class="form-control"
[(ngModel)]="pageInfoBeans.nameValueMap[fieldInfo.name]"
name="{{fieldInfo.name}}"
[required]="fieldInfo.preRequiredInd"
[maxLength]="fieldInfo.fieldSize" />
</div>
[disabled]="!isEditable" where initialize isEditable = 'disabled' this could be added in both the text and email input fields.
Also in your edit button you can add a callback for click where you can set isEditable = ''.
#Directive({
selector : ["canbedisabled"]
})
export class Canbedisabled{
constructor(private el: ElementRef) {
}
#Input()
set blocked(blocked : boolean){
this.el.nativeElement.disabled = blocked;
}
}
<input formControlName="first" canbedisabled [blocked]="isDisabled">
You can solve it with a Directive. A directive named Canbedisabled and a property "blocked", for example. Write a setter for blocked and set it to nativelement.disabled property.
refer: https://github.com/angular/angular/issues/11271

Categories