How to make ngModel available between components - javascript

I am seriously getting mad. I have tried everything. FormsModules,ReactiveForms,FORMDIRECTIVES,Input,Output i've been searching everywhere on how to make ngModel available between components. I am trying to show in an h1 tag the value which is being typed/deleted in the input tag using string interpolation, however it isn't working, these are the files:
app.component.html:
<div class="container text-center" id="headerCont">
<span style="color: #6E2435" class="header">note</span><span style="color: #6BBFDE" class="header">it</span>
</div>
<div class="container">
<app-input></app-input>
<app-notes></app-notes>
</div>
app.component.ts
import { Component, OnInit, Input, Output } from '#angular/core';
import { NgModule } from '#angular/core';
import { FormsModule, ReactiveFormsModule } from '#angular/forms';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
}
notes.component.html
<div class="col-xs-2">
<h1>{{ TitleInput }}</h1>
<p>{{ noteInput }}</p>
</div>
input.component.html
<div class="container" id="noteCreate">
<form id="titleInputForm">
<input type="text" [(ngModel)]="TitleInput" name="TitleInput">
</form>
<form>
<textarea name="name" rows="8" cols="80">
</textarea>
</form>
</div>
If you can figure it out I would be so grateful.

You actually searched it but did not use it, the solution to your problem is #Input and #Output. I believe you did not use this effectively.
Since the other components is governed by a component called AppComponent you just simply need to put those data in it:
import { Component } from '#angular/core';
// The other imports are not necessary
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent
{
// declare those stuffs
title: string;
note: string;
constructor()
{
this.title = "";
this.note = "";
}
}
then in your AppComponent template
<div class="container">
<app-input [title]="title"
[note]="note"
// you should return $event always or else it will return undefined
(onTitleChange)="title = $event"
(onModelChange)="note = $event">
</app-input>
<app-notes [title]="title"
[note]="note">
</app-notes>
</div>
where [ ] are #Input and ( ) are #Output from the component
so your InputComponent will have:
// add these to your existing InputComponent
import { Input, Output, EventEmitter } from "#angular/core";
export class InputComponent
{
#Input("title") title: string;
#Input("note") note: string;
#Output() onTitleChange = new EventEmitter();
#Output() onNoteChange = new EventEmitter();
}
where #Input is the data your recieve and #Ouput is the data you send.
and your InputComponent template would be:
<div class="container" id="noteCreate">
<form id="titleInputForm">
<input type="text"
name="title"
[(ngModel)]="title"
(ngModelChange)="onTitleChange.emit(title)">
<textarea name="note"
rows="8"
cols="80"
[(ngModel)]="note"
(ngModelChange)="onNoteChange.emit(note)">
</textarea>
</form>
</div>
where setting [(ngModel)] with your #Input and (ngModelChange) to trigger #Output when modal has changed.
In this example you can actually set default values from AppComponent to InputComponent.
If you understand #Input correctly in these example I do not need to put what would be inside NotesComponent
hope that helps.

Related

How do I add a conditional form field through content projection in Angular?

I am trying to add a conditional field through content projection.
The projected input field is toggling correctly in accordance to its sibling checkbox field. When the checkbox is "checked" the conditional field displays. When the checkbox is "unchecked", the input field disappears.
However, when I move the checkbox into its "checked" state, I'm receiving an error:
ERROR Error: Cannot find control with name: 'detail'
I want the projected detail field to be apart of the _formGroup in the child component, not the parent component. How can I achieve this?
The error seems to suggest that the formControlName="detail" is not visible to the _formGroup in the child component. How can I rectify this?
Here is the parent component with the outer parent Form Group.
Parent Component
import { Component, OnInit } from '#angular/core';
import { FormControl, FormGroup } from '#angular/forms';
#Component({
selector: 'ch-parent-comp',
templateUrl: './parent-comp.component.html',
styleUrls: ['./parent-comp.component.scss']
})
export class ParentComponent implements OnInit {
formGroup: FormGroup;
constructor() {
this.formGroup = new FormGroup({
//detail: new FormControl('') --> Adding this will solve the issue, but I don't want this field here. I want it in the child
});
}
ngOnInit(): void {
}
}
Parent Component HTML Template
<form>
<ch-check-box-group [parentFormGroup]="formGroup" controlName="new_property" controlLabel="New Property" [hasDetail]="true" [detailRequired]="true">
<!-- Projected Input Field -->
<ng-template contentHandle>
<mat-form-field>
<mat-label>Purchase Date</mat-label>
<input type="text" matInput name="detail" formControlName="detail" id="">
</mat-form-field>
</ng-template>
</ch-check-box-group>
</form>
Child Component(ch-check-box-group)
export interface CheckboxGroupForm {
value: FormControl<boolean>,
detail?: FormControl<any>
}
#Component({
selector: 'ch-check-box-group',
templateUrl: './check-box-group.component.html',
styleUrls: ['./check-box-group.component.scss']
})
export class CheckBoxGroupComponent implements OnInit, OnDestroy {
private _ks: Subject<void> = new Subject<void>();
_formGroup: FormGroup
_showDetailInput: boolean = false;
#Input() controlLabel!: string;
#Input() controlName!: string;
#Input() parentFormGroup!: FormGroup;
#Input() hasDetail: boolean = false;
#Input() detailRequired: boolean = false;
#ContentChild(ContentHandleDirective) content!: ContentHandleDirective;
constructor() {
this._formGroup = new FormGroup<CheckboxGroupForm>({
value : new FormControl(false, {nonNullable: true}),
});
}
ngOnInit(): void {
if(this.hasDetail){
this._formGroup.addControl('detail', new FormControl(''));
}
this.parentFormGroup.addControl(this.controlName, this._formGroup);
this._formGroup.setParent(this.parentFormGroup);
this._formGroup.valueChanges
.pipe(takeUntil(this._ks))
.subscribe((change) => {
// Toggle visibility of detail input
if(this.hasDetail && this._showDetailInput != change.value){
this._showDetailInput = change.value;
}
// Toggle Validation(if necessary) of detail input
if(this.hasDetail && this.detailRequired && change.value){
setTimeout(() => {
this._formGroup.get('detail')?.addValidators(Validators.required);
});
} else {
this._formGroup.get('detail')?.clearValidators();
if(this._showDetailInput != change.value){
this._formGroup.get('detail')?.updateValueAndValidity();
}
}
console.log(this._formGroup.controls)
});
}
ngOnDestroy(){
this._ks.next();
this._ks.complete();
}
}
Child Component Template
<div class="row">
<form class="col-12" [formGroup]="_formGroup">
<mat-checkbox formControlName="value" i18n>{{ controlLabel }}</mat-checkbox>
<button mat-icon-button color="primary">
<mat-icon>help</mat-icon>
</button>
<div class="conditional-input-box-container" *ngIf="_showDetailInput">
<ng-container [ngTemplateOutlet]="content.templateRef"></ng-container>
</div>
</form>
</div>
To solve this, pass the child components _formGroup to the [ngTemplateOutletContext] like so:
<div class="row">
<form class="col-12" [formGroup]="_formGroup">
<mat-checkbox formControlName="value" i18n>{{ controlLabel }}</mat-checkbox>
<button mat-icon-button color="primary">
<mat-icon>help</mat-icon>
</button>
<div class="conditional-input-box-container" *ngIf="_showDetailInput">
<ng-container [ngTemplateOutlet]="content.templateRef" [ngTemplateOutletContext]="{ $implicit: _formGroup}"></ng-container>
</div>
</form>
</div>
Then use that new variable in the parent component like this:
<form>
<ch-check-box-group [parentFormGroup]="formGroup" controlName="new_property" controlLabel="New Property" [hasDetail]="true" [detailRequired]="true">
<!-- Projected Input Field -->
<ng-template contentHandle let-_formGroup>
<div [formGroup]="_formGroup">
<mat-form-field>
<mat-label>Purchase Date</mat-label>
<input type="text" matInput name="detail" formControlName="detail" id="">
</mat-form-field>
</div>
</ng-template>
</ch-check-box-group>
</form>

Why Parent does not listen to child on Angular?

I cannot make this eventemitter work. Can you please help? I am a beginner and it should be quite simple code for you.
I have a parent component, reading two different emitters from two different children:
<app-van [vans]="vans"></app-van>
<app-modal *ngIf="modalOpen" (closed)="onClick()" (openModal)="onClickTwo($event)"></app-modal>
import { Component, OnInit } from '#angular/core';
import { Van } from '../../interface';
#Component({
selector: 'app-fleet-home',
templateUrl: './fleet-home.component.html',
styleUrls: ['./fleet-home.component.css']
})
export class FleetHomeComponent implements OnInit {
modalOpen = true;
vans: Van [] = [
{ name: 'Ubeddu', description: 'Mercedes Sprinter', plate: 'NH55GKA' },
{ name: 'Abbestia', description: 'Ford Transit', plate: 'DK66HHR' },
{ name: 'Eumulu', description: 'Citroen Berlingo', plate: 'DR55MKL' }
];
constructor( ) { }
ngOnInit() {
}
onClick() {
this.modalOpen = !this.modalOpen;
console.log('modalOpen changed');
}
onClickTwo(event) {
this.modalOpen = event;
console.log('modalOpen changed');
}
}
the parent listened to this child:
<div (click)="onCloseClick()" class="ui dimmer visible active">
<div (click)="$event.stopPropagation()" class="ui modal visible active">
<div class="asuca">
<form class="ui form" >
<h4 class="ui dividing huge header">Van</h4>
<div class="required field">
<label class="ui header">Van Name</label>
<input type="text" placeholder="Van NickName">
</div>
<div class="field">
<label class="ui header">Description</label>
<input type="text"placeholder="Description">
</div>
<div class="field">
<label class="ui header">Plate</label>
<input type="text"placeholder="License Plate">
</div>
<button (click)="onCloseClick()" class="ui button" type="submit">Submit</button>
</form>
</div>
</div>
</div>
import { Component, OnInit, ElementRef, Output, EventEmitter } from '#angular/core';
#Component({
selector: 'app-modal',
templateUrl: './modal.component.html',
styleUrls: ['./modal.component.css']
})
export class ModalComponent implements OnInit {
#Output() closed = new EventEmitter();
constructor(private el: ElementRef) { }
ngOnInit() {
document.body.appendChild(this.el.nativeElement);
}
// tslint:disable-next-line: use-lifecycle-interface
ngOnDestroy() {
this.el.nativeElement.remove();
}
onCloseClick() {
this.closed.emit();
}
}
and doesnt listen to the second child:
<div class="ui fluid four black cards">
<div *ngFor="let van of vans" class="card">
<div class="content">
<div class="header">
{{ van.name }}
</div>
<div class="meta">
{{ van.description }}
</div>
<div class="description">
{{ van.plate }}
</div>
</div>
<div class="extra content">
<div class="ui two buttons">
<div (click)="onEditClick(true)" class="ui basic black button">Edit</div>
<div class="ui basic red button">Delete</div>
</div>
</div>
</div>
</div>
import { Component, OnInit, Input, Output, EventEmitter } from '#angular/core';
#Component({
selector: 'app-van',
templateUrl: './van.component.html',
styleUrls: ['./van.component.css']
})
export class VanComponent implements OnInit {
#Input() vans = [];
#Output() openModal = new EventEmitter<boolean>();
constructor() { }
ngOnInit() {
}
onEditClick(event: boolean) {
this.openModal.emit(event);
}
}
the whole thing is to hide the modal clicking around the screen and show it again clicking a button.
On the console.log, the object emitter by the first child has got an observer, where the object emitter by the second child as none; no idea what means though.
thanks in advance for the help. I can provide the whole folder if needed. I am just trying to learn :)
Seems like you have missed binding the output event in parent template. Please correct like below:
<app-van [vans]="vans" (openModal)="onEditClick($event)"></app-van>

consoling angular form output failed

This is my html view in angular:
<div class="container">
<div class="row">
<div class="col-md-3">
</div>
<div class="col-md-6">
<div class="jumbotron">
<h2>Complete your Profile</h2>
<h5 style="color:royalblue;">Add your updated Resume to get notified by Recruiters</h5>
<form #ResumeForm="ngForm"
(ngSubmit)="submitResume(ResumeForm.value)">
<div class="form-group">
<label>Resume</label>
<input type="file" name="resume" id="resume" class="form-control">
</div>
<input value="submit" type="submit">
</form>
</div>
</div>
</div>
</div>
My .ts file is here
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-candidate-reg-complete',
templateUrl: './candidate-reg-complete.component.html',
styleUrls: ['./candidate-reg-complete.component.css']
})
export class CandidateRegCompleteComponent implements OnInit {
constructor() { }
ngOnInit() {
}
submitResume=function(user){
console.log(user);
}
}
But the console screen shows only object{}. No form content is shown.
I am trying to take file input.
Can anyone help me?
Thanks in advance.
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-candidate-reg-complete',
templateUrl: './candidate-reg-complete.component.html',
styleUrls: ['./candidate-reg-complete.component.css']
})
export class CandidateRegCompleteComponent implements OnInit {
constructor() { }
ngOnInit() {
}
submitResume(user){
console.log(user);
}
}
Put your submitResume(value) function outside ngOnInit. It will be called from the html.
submitResume(value){
console.log(value);
}

Angular2 add HTML to dynamic elements

I have this code:
import { Component, ElementRef, Renderer2 } from '#angular/core';
#Component({
selector: 'my-app',
template: '<button (click)="runR()">Run</button>
<div class="testme">
<div class="somediv">
<div class="dynamically_created_div unique_identifier"></div>
<div class="dynamically_created_div unique_identifier"></div>
<div class="dynamically_created_div unique_identifier"></div>
</div>
</div>',
})
export class AppComponent{
hostEl: any;
constructor(
private el:ElementRef,
private renderer:Renderer2,
) {
this.hostEl = el.nativeElement;
}
runR(){
let change_this;
change_this= this.renderer.createElement('span');
this.renderer.addClass(change_this, 'change_this');
this.renderer.appendChild(this.hostEl, change_this);
}
}
Is there any way in Angular2 to add HTML to the .dynamically_created_div?
Because the above only adds to the end of the HTML of the component.
I also tried with:
import { Component, ElementRef, ViewChild, Renderer, AfterViewInit } from '#angular/core';
#Component({
selector: 'my-app',
template: `<button (click)="runR()">Run</button>
<div class="testme">
<div class="somediv">
<div class="dynamically_created_div">
</div>
</div>
</div>
`,
})
export class AppComponent {
constructor(private renderer:Renderer) {}
runR() {
#ViewChild('dynamically_created_div') d1:ElementRef;
this.renderer.invokeElementMethod(this.d1.nativeElement, 'insertAdjacentHTML', ['beforeend', '<div class="new_div">new_div</div>'] );
}
}
But it's not working because the #ViewChild directive must be outside the function and I can't have control over it anymore
I also tried like this:
<div class="dynamically_created_div" [innerHtml]="newHTML"></div>
this.newHTML = '<div class="new_div">new_div</div>';
Thing I cannot do because my content is dynamic and uses unique IDs and I cannot use [innerHtml] dynamically ( it only works for what I put in themplate for the first time, then anything else that changes can't use innerHtml anymore.
I checked Angular2: Insert a dynamic component as child of a container in the DOM but there is the same problem, the placeholders aren't dynamic
UPDATE:
My code is a little bit more complex:
TS:
import { AfterContentInit, Component, OnInit, OnDestroy, ViewEncapsulation } from '#angular/core';
import { NgForm, FormsModule, ReactiveFormsModule, FormGroup, FormControl, FormBuilder, Validators } from '#angular/forms';
import { SFService } from '../services/sf.service';
import { Injectable, Pipe, PipeTransform } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
providers: [ SFService ],
})
export class AppComponent implements OnInit {
constructor(
private sfservice: SFService,
) {}
ngOnInit(){
this.sfservice.getMembers().subscribe(members => {
this.members = members.members;
});
}
members: Member[];
member_selector: Member[];
member_each: Member;
member_selector_each: Member[];
cases: Case;
runR(){
this.members.forEach(member_each => {
this.member_selector.forEach(member_selector_each => {
if(member_each.Id === member_selector_each.Id){
console.log(member_selector_each.Id);
this.sfservice.getCaseHistory(member_selector_each.Id, "2017-04-25T00:00:00", "2017-04-28T23:59:59").subscribe(cases => {
this.member_each['cases'] = cases;
console.log(this.member_each);
});
}
})
})
}
}
HTML:
<form #myForm="ngForm" novalidate>
<select name="member_selector_name" [(ngModel)]="member_selector" multiple ng-model="selectedValues" style="height:200px;">
<option *ngFor="let member of members" [ngValue]="member">{{member.Name}}</option>
</select>
<button (click)="runR()">Run</button>
</form>
<div id="results">
<div *ngFor="let mem of members" class="member-card-{{mem.Id}}">
<div class="card-container">
<div *ngFor="let case of mem.Cases" class="case-card" id="{{case.Id}}">{{case.Number}}
</div>
</div>
</div>
</div>
I was trying to use only ngFor but now I get
Cannot set property 'cases' of undefined
What's the problem with this approach?
export class AppComponent{
#ViewChild('d1') d1:ElementRef;
#ViewChild('d2') d2:ElementRef;
#ViewChild('d3') d3:ElementRef;
constructor(private renderer:Renderer2) { }
runR(){
let change_this;
change_this= this.renderer.createElement('span');
this.renderer.addClass(change_this, 'change_this');
this.renderer.appendChild(this.d1, change_this);
}
}
Template:
<div class="dynamically_created_div unique_identifier" #d1></div>
<div class="dynamically_created_div unique_identifier" #d2></div>
<div class="dynamically_created_div unique_identifier" #d3></div>
you can use ngfor and create you elements inside it and using index you can create different ids and names.
I do something like this i dont know if you want to do the same but here's my code to create some input's dynamically and add or access their values
<div *ngFor="let comp of templateVals | async;let i=index">
<md-input-container class="example-90" *ngIf="comp.type=='code'">
<textarea rows="4" mdInput name="desc{{i}}" [(ngModel)]="comp.data" placeholder="Description"></textarea>
</md-input-container>
<md-input-container class="example-90" *ngIf="comp.type=='text'">
<textarea rows="4" mdInput name="text{{i}}" [(ngModel)]="comp.data" placeholder="Text"></textarea>
</md-input-container>
<md-input-container class="example-90" *ngIf="comp.type=='title'">
<input mdInput name="title{{i}}" [(ngModel)]="comp.data" placeholder="Title">
</md-input-container>
<span class="example-90" *ngIf="comp.type=='upload'">
<input-file *ngIf="!comp.data" [acceptId]="comp.id" (onFileSelect)="addedFileInfo($event)"></input-file>
<span *ngIf="comp.data">{{comp.data}}</span>
</span>
<span class="example-10">
<button md-mini-fab (click)="removeThis(comp)"><md-icon>remove circle</md-icon></button>
</span>
</div>

Angular 2 scope of Dynamic component

I have been learning about Angular 2 and their new features and i am having trouble when adding a component dynamically.
so i have a dashboard.component.ts
import { Component, OnInit, ViewContainerRef, ComponentFactoryResolver, ViewChild } from '#angular/core';
import { InputTextComponent } from '../input-text/input-text.component'
#Component({
templateUrl: 'dashboard.component.html',
providers: [InputTextComponent],
styleUrls: ['dashboard.component.css']
})
export class DashboardComponent implements OnInit {
constructor( private componentFactoryResolver: ComponentFactoryResolver,
private viewContainerRef: ViewContainerRef,
private inputTextComponent: InputTextComponent
) { }
#ViewChild(InputTextComponent) textComponent: InputTextComponent
attachIntup(){
const factory = this.componentFactoryResolver.resolveComponentFactory(InputTextComponent);
const ref = this.viewContainerRef.createComponent(factory);
ref.changeDetectorRef.detectChanges();
}
alertPop(){
alert(this.textComponent.passingStr);
}
ngOnInit() {
}
}
and it has an html code dashboard.component.html
<button (click)="attachIntup()">Inject Input</button>
<button (click)="alertPop()">Pop String</button>
and my inputTextComponent is as follows
import { Component, AfterViewInit } from '#angular/core';
#Component({
selector: 'app-input-text',
templateUrl: './input-text.component.html',
styleUrls: ['./input-text.component.css']
})
export class InputTextComponent implements AfterViewInit {
public inputType = '';
public passingStr = '';
constructor() { }
popupAlert(){
alert(this.passingStr);
}
ngAfterViewInit() {
this.inputType = 'text'
}
}
with an html:
<div [ngSwitch]="inputType" class="container">
<div class="row" *ngSwitchCase="'textarea'">
<div class="col-md-4">
<label>I am a textarea: </label>
</div>
<div class="col-md-8">
<textarea style="resize: both" class="form-control"></textarea>
</div>
</div>
<div class="row" *ngSwitchCase="'text'">
<div class="col-md-4">
<button (click)="popupAlert()"></button>
<label>I am an input text: </label>
</div>
<div class="col-md-8">
<input type="text" class="form-control" placeholder="Testing text" [(ngModel)]="passingStr">
</div>
</div>
</div>
What i am trying to do is obtain the scope from the inputTextComponent inside the dashboard component. I have read that the ViewChild allows you to access the variables inside a component, but in my case i am not able to do so.
Does anyone know how i can access the variables inside the InputTextComponent after injection; In order to display in the alert from the dashboard whatever information is been passed through the input.
Thanks in advance

Categories

Resources