i have a page with main parent element and two child elements quite like this.
how can i focus on the text box in the child2 component, when clicked on the button on child 1 component.
child 1 component is actually the header section of my page. so there may also be cases like child1 is always visible, but instead of child2, there is some different component on display. then when i click on the button on child1, it should route to child2 and then scroll down and focus on the required text box of child 2..
how to implement this function in angular.
First of All you have to add the routing to your Child2(Assuming it as a class name) So that when user clicks the button on your header it is routed to Child2.
1.<button class="header" [routerLink]="['Child2']">Child2</button>
//Child2 is the name of child2 Component
2.And then use Angular life cycle hooks -> ngOnInit() {} ngOnInit is a life cycle hook called by Angular to indicate that the Angular is done creating the component.
And place some typescript code to make the textbox in focus.
<input type="text">Go to bottom</input>
<input type="text" id="textbox"></footer>
You can build some approach over the above example.
or
document.getElementById("textbox").click();
This will bring in focus into the textbox that is present in the component child2
Here is how you can implement this
Create one service will have to methods
Put in app.module.ts providers
Now inject this SharedService to child-one-component.ts and call setData() method of this service where we are setting value in observable
Now inject same SharedService to child-two-component.ts and call getData() method of service onInit() will subscribe to observable at the same time when you click on setData()
app.component.html
<app-child-one></app-child-one>
<app-child-two></app-child-two>
child-one.html
<div style="border: 2px solid black; padding:15px; margin-bottom:3px">
<p>child-one </p>
<button (click)="setFocus()">Set Focus</button>
<button (click)="removeFocus()" style="margin-left:10px;">Remove Focus</button>
</div>
child-one.ts
import { Component, OnInit, EventEmitter } from '#angular/core';
import { SharedService } from '../shared.service';
#Component({
selector: 'app-child-one',
templateUrl: './child-one.component.html',
styleUrls: ['./child-one.component.css']
})
export class ChildOneComponent implements OnInit {
constructor(private sharedService: SharedService) { }
ngOnInit() {
}
setFocus() {
this.sharedService.setData(true);
}
removeFocus() {
this.sharedService.setData(false);
}
}
child-two.html
<div style="border: 2px solid black; padding:25px 15px;">
<p>child-two </p>
<input type="text" placeholder="Enter text" #input/>
</div>
child-two.ts
import { Component, OnInit, ViewChild, ElementRef } from '#angular/core';
import { SharedService } from '../shared.service';
#Component({
selector: 'app-child-two',
templateUrl: './child-two.component.html',
styleUrls: ['./child-two.component.css']
})
export class ChildTwoComponent implements OnInit {
#ViewChild('input', {static: true}) private input: ElementRef;
constructor(private sharedService: SharedService) { }
ngOnInit() {
// will get called when user click on Set Focus button
this.sharedService.getData().subscribe((response: any)=> {
if(response) {
this.input.nativeElement.focus(); // will set focus into input here
} else {
this.input.nativeElement.blur();
}
})
}
}
app.module.ts
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { FormsModule } from '#angular/forms';
import { AppComponent } from './app.component';
import { ChildOneComponent } from './child-one/child-one.component';
import { ChildTwoComponent } from './child-two/child-two.component';
import { SharedService } from './shared.service';
#NgModule({
imports: [ BrowserModule, FormsModule ],
declarations: [ AppComponent, ChildOneComponent, ChildTwoComponent ],
bootstrap: [ AppComponent ],
providers: [SharedService]
})
export class AppModule { }
shared.service.ts will use this service to send message from one component to another.
import { Injectable } from '#angular/core';
import { Observable, Subject } from 'rxjs';
#Injectable()
export class SharedService {
private subject$ = new Subject<any>();
constructor() { }
setData(data) {
this.subject$.next(data);
}
getData() {
return this.subject$.asObservable();
}
}
Here is working demo https://stackblitz.com/edit/santosh-angular-set-focus-in-input-from-another-component
Related
I have an application where my requirement is upon a button click (present in header component) the form field in another component will get updated. But the problem is the value is getting set in the form control variable but is not reflecting in UI. This is the sample code I've created. You can see on button click in console log that the form has the value but is not rendered in UI. Below I am showing my code what I have done so far :
Child Component
// In TS
test = this.fb.group({
sample: [""]
});
// In HTML
<form [formGroup]="test">
<input placeholder="Sample" formControlName="sample" />
</form>
Header Component
// In TS
import { ChildComponent } from "../child/child.component";
constructor(private child: ChildComponent) {}
set() {
this.child.test.patchValue({
sample: "fetched value"
});
console.log(this.child.test.value);
}
// In HTML
<button (click)="set()">ABC</button>
app.module.ts
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { FormsModule, ReactiveFormsModule } from '#angular/forms';
import { AppComponent } from './app.component';
import { HeaderComponent } from './header/header.component';
import { ChildComponent } from './child/child.component';
import { AppRouterModule } from './router/router.module';
#NgModule({
imports: [ BrowserModule, FormsModule, AppRouterModule, ReactiveFormsModule ],
declarations: [ AppComponent, HeaderComponent, ChildComponent ],
providers: [ ChildComponent ], // Specified child component to access through different component
bootstrap: [ AppComponent ]
})
export class AppModule { }
app.component.html
<app-header></app-header>
<router-outlet></router-outlet>
So this is my code. Can anyone tell me where the problem is lying ?
That isn't how the component interaction works in Angular. To share data among components, you could either use EventEmitter for related components or a singleton service for unrelated components.
Here is an illustration using a singleton service
share.service.ts
import { Injectable } from "#angular/core";
import { ReplaySubject } from "rxjs";
#Injectable()
export class ShareService {
public headerClickSrc = new ReplaySubject<any>(1);
public headerClick$ = this.headerClickSrc.asObservable();
constructor() {}
}
header.component.ts
import { Component } from "#angular/core";
import { ShareService } from "../share.service";
#Component({
selector: "app-header",
templateUrl: "./header.component.html"
})
export class HeaderComponent {
constructor(private share: ShareService) {}
set() {
this.share.headerClickSrc.next("fetched value");
}
}
child.component.ts
export class ChildComponent implements OnInit, OnDestroy {
closed$ = new Subject<any>();
constructor(private fb: FormBuilder, private share: ShareService) {}
test = this.fb.group({
sample: [""]
});
ngOnInit() {
this.share.headerClick$.pipe(
takeUntil(this.closed$)
).subscribe({
next: value => {
this.test.patchValue({
sample: value
});
console.log(this.test.value);
}
});
}
ngOnDestroy() {
this.closed$.next(); // <-- close open subscriptions
}
}
I've modified your Stackblitz
I have a form which allows me to create a new record or update an existing one. Right now the text of the button is submit. Based on if I call the form with an optional parameter which in my case is the id of the record to edit I set my form mode as create or update. What I want is to be able to set the text displayed as either create or update but I have not found a way to change the text from my controller. I can set all field values and enable or disable the submit button but have not found a way to set the Text on button. Can this be done and if not is there a work around
In your ts file.
export class AppComponent {
buttontext="Create"
name = 'Angular';
}
In your HTML File
<button>{{buttontext}}</button>
You can change button text accordingly.In your functions
See here-https://stackblitz.com/edit/angular-wsxkz9
If you are looking for some other solution you have to create your own directive
CustomDirective.ts file
import { Directive, ElementRef, Input, Renderer2 } from '#angular/core';
#Directive({ selector: '[myHidden]' })
export class HiddenDirective {
constructor(public el: ElementRef,private renderer: Renderer2) {}
#Input() myHidden: boolean;
ngOnInit(){
// Use renderer to render the emelemt with styles
console.log(this.myHidden)
if(this.myHidden) {
this.renderer.setProperty(this.el.nativeElement, 'innerHTML', 'Create');
}
else{
this.renderer.setProperty(this.el.nativeElement, 'innerHTML', 'Delete');
}
}
}
app.module.ts
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { FormsModule } from '#angular/forms';
import { AppComponent } from './app.component';
import { HelloComponent } from './hello.component';
import { HiddenDirective } from './customdirective';
#NgModule({
imports: [ BrowserModule, FormsModule ],
declarations: [ AppComponent, HelloComponent,HiddenDirective ],
bootstrap: [ AppComponent ]
})
export class AppModule { }
app.component.ts
import { Component } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
val="true";
name = 'Angular';
}
app.component.html
<hello name="{{ name }}"></hello>
<p>
Start editing to see some magic happen :)
</p>
<button [myHidden]="val"></button>
See this- https://stackblitz.com/edit/angular-ktf7e3
I'm trying to pass a variable that is set on a component, to the parent component via a getter/setter in a service. The setter is applied correctly, but the getter returns undefined.
The below code was pulled out of another project I work on that does just fine with this code so I'm not sure why it isn't working here.
I simply just need to pass the pageTitle that is set on the child component, pass it to the parent component to display in its HTML.
Parent Component
TS: styleguide.component.ts
import { Component } from '#angular/core';
import { Router, ActivatedRoute } from '#angular/router';
import { StyleguideService } from './styleguide.service';
#Component({
selector: 'styleguide',
templateUrl: './styleguide.component.html',
host: {'class': 'route'},
})
export class StyleguideComponent {
constructor( private ss: StyleguideService ) {}
}
Relevant HTML: styleguide.component.html
<a [routerLink]="[]" aria-current="page" class="crumbs__link crumbs__link--active" [title]="ss.pageTitle">{{ss.pageTitle}}</a>
Parent Module: styleguide.module.ts
import { NgModule } from '#angular/core';
import { CommonModule } from '#angular/common';
import { FormsModule } from '#angular/forms';
import { StyleguideService } from './styleguide.service';
import { StyleguideComponent } from './styleguide.component';
import { TemplatesComponent } from './templates/templates.component';
...
#NgModule({
imports: [
CommonModule,
FormsModule,
...
],
declarations: [
StyleguideComponent,
TemplatesComponent,
...
],
exports: [
...
],
providers: [
StyleguideService
]
})
export class StyleguideModule {}
Service: styleguide.service.ts
import { Injectable } from '#angular/core';
#Injectable()
export class StyleguideService {
pageTitleS: string;
get pageTitle(): string {
console.log('get title: ', this.pageTitleS); // <-- Returns undefined
return this.pageTitleS;
}
set pageTitle(s: string) {
console.log('set title: ', s);
this.pageTitleS= s;
}
}
Child Component: templates.component.ts
import { Component } from '#angular/core';
import { StyleguideService } from '../styleguide.service';
#Component({
selector: 'templates',
templateUrl: './templates.component.html',
host: {'class': 'route__content'}
})
export class TemplatesComponent {
constructor( private ss: StyleguideService ) {
this.ss.pageTitle = "Templates";
}
}
You should implement the Service with Observables. A quick example would be something like this:
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
import {Injectable} from '#angular/core'
#Injectable()
export class Service {
private value: BehaviorSubject<string>;
constructor() {
this.value = <BehaviorSubject<string>>new BehaviorSubject();
}
setValue(value=""){
this.value.next(value);
}
getValue() {
return this.value.asObservable();
}
}
The Parent Component would subscribe to it like so:
import {Component, OnInit} from '#angular/core'
import { Service } from './service';
#Component({
selector: 'parent-component',
template: `
<div>
<h2>Value {{value}}</h2>
<child-component></child-component>
</div>
`,
})
export class ParentComponent implements OnInit {
value:string;
constructor(private service: Service) {
}
ngOnInit(){
this.service.getValue().subscribe((newValue)=>{
this.value = newValue;
})
}
}
And the Child Component would set the value and also subscribe to it like so:
import {Component, OnInit} from '#angular/core'
import { Service } from './service';
#Component({
selector: 'child-component',
template: `
<div>
<h2>Child Value {{value}}</h2>
</div>
`,
})
export class ChildComponent implements OnInit {
value:string;
constructor(private service: Service) {
this.service.setValue('New Value');
}
ngOnInit(){
this.service.getValue().subscribe((newValue)=>{
this.value = newValue;
})
}
}
Your setter is never called. You instantiate the service using StyleguideComponent, not the TemplatesComponent which does call the setter, and the constructor of StyleguideComponent does not call the setter on the service which is why the value remains undefined.
The TemplatesComponent has an element selector templates which I do not see in the styleguide.component.html you have in the question which is why I believe TemplatesComponent is never being created.
You are not calling the setter function in your child.component.ts instead you are setting the value of variable but I think you are accessing it wrongly as you are missing the last S in the variable name. You should be doing
export class TemplatesComponent {
constructor( private ss: StyleguideService ) {
this.ss.pageTitle("Templates");
// Now to get it you should call
this.ss.pageTitle(); // Should console.log the value
}
}
Okay so it was related to my routing setup, I didn't have my child routes setup correctly so this really had nothing to do with the getter/setter after all.
I recently ran into a problem and can't really figure out what's wrong with my code at this point, hopefully someone of you can help me.
All I am trying to do is changing the value of my BehaviorSubject with a function but it isn't working out.
chat.service.ts
import { Injectable } from '#angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
#Injectable()
export class ChatService {
chatId = new BehaviorSubject<number>(0);
constructor() {
this.chatId.next(1);
}
changeChatId(chatId: number) {
console.log(chatId);
this.chatId.next(chatId);
}
}
So the subscribers get the default as well as the changed chatId from the constructor. But as soon as I try to change it with the changeChatId function nothing happens at all. The right id's get passed into the function I already debugged that but the line this.chatId.next(chatId) doesn't seem to do anything.
ADD
These are the other components the service is currently used in.
chat-message-list
import { Component, OnInit, Input} from '#angular/core';
import { ChatService } from "../../../shared/services/chat.service";
#Component({
selector: 'app-chat-message-list',
templateUrl: './chat-message-list.component.html',
styleUrls: ['./chat-message-list.component.css'],
providers: [ChatService]
})
export class ChatMessageListComponent implements OnInit {
chatId: number;
constructor(private chat: ChatService) { }
ngOnInit() {
this.chat.chatId.subscribe(
chatId => this.updateMessageList(chatId)
);
}
}
chat-item
import { Component, OnInit, Input} from '#angular/core';
import { User } from '../../../shared/models/user.model';
import { ChatService } from '../../../shared/services/chat.service';
#Component({
selector: 'app-chat-list-item',
templateUrl: './chat-list-item.component.html',
styleUrls: ['./chat-list-item.component.css'],
providers: [ChatService]
})
export class ChatListItemComponent implements OnInit {
#Input()
user: User;
constructor(private chat: ChatService) { }
ngOnInit() {
}
onChatItemSelected(){
this.chat.changeChatId(this.user.id);
}
}
You need to make your ChatService a singleton (shared) service. Add it to the providers of your ngModule. This allows all the components that use the ChatService to share the same service instance.
#NgModule({
providers: [ChatService]
})
And remove it from your components providers. When you are adding it to your components providers, that component gets its own instance of ChatService which can not be used by other components.
I am trying to add the components dynamically in angular4.
I checked with other questions, But i cant find solution.
I got the error
ERROR TypeError: Cannot read property 'createComponent' of undefined
on dynamic components.
adv.component.ts
import { Component, OnInit, AfterContentInit, ViewChild, ViewContainerRef, ComponentFactoryResolver } from '#angular/core';
import { SampleComponent } from '../sample/sample.component';
#Component({
selector: 'app-adv',
templateUrl: './adv.component.html',
styleUrls: ['./adv.component.css']
})
export class AdvComponent implements OnInit, AfterContentInit {
#ViewChild('container', {read:'ViewContainerRef'}) container;
constructor(private resolver : ComponentFactoryResolver) { }
ngOnInit() {
}
ngAfterContentInit(){
const sampleFactory = this.resolver.resolveComponentFactory(SampleComponent);
this.container.createComponent(sampleFactory);
}
}
adv.component.html
<div #container></div>
app.module.ts
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { AppComponent } from './app.component';
import { AdvComponent } from './adv/adv.component';
import { SampleComponent } from './sample/sample.component';
#NgModule({
declarations: [
AppComponent,
AdvComponent,
SampleComponent
],
entryComponents:[
SampleComponent
],
imports: [
BrowserModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
About ngAfterViewInit in docs:
Respond after Angular initializes the component's views and child
views.
while ngAfterContentInit:
Respond after Angular projects external content into the component's
view.
So the child view is not ready in ngAfterContentInit, so move this part
ngAfterContentInit(){
const sampleFactory = this.resolver.resolveComponentFactory(SampleComponent);
this.container.createComponent(sampleFactory);
}
to
ngAfterViewInit() {
const sampleFactory = this.resolver.resolveComponentFactory(SampleComponent);
this.container.createComponent(sampleFactory);
}
Also change:
#ViewChild('container', {read:'ViewContainerRef'}) container;
to
#ViewChild('container', { read: ViewContainerRef }) container: ViewContainerRef