I have one div and inside div i have some html now i want to initilize script for that html after content loaded for once.
<div class="m-stack m-stack--ver m-stack--desktop m-header__wrapper" *ngIf="isLogin == 'true'">
Put the script inside the lifecycle hook for ngAfterViewInit()
export class YourClass implements AfterViewInit {
// Add this method
ngAfterViewInit() {
// this code will execute after the ngif has been rendered
this.logIt('AfterViewInit');
this.doSomething();
}
}
From the docs: https://angular.io/guide/lifecycle-hooks#afterview
For this issue You need to create one Flag AfterViweInit
this.isLogin = false;
ngOnInit() {
var data = this._userService.verify().subscribe(res => {
if (res) {
this.isLogin = 'true';
} else {
this.isLogin = 'false';
}
});
}
ngAfterViewInit() {
mLayout.initHeader();
setTimeout(()=>{
this.isLogin = true;
},100);
}–
in your html page
<div class="m-stack m-stack--ver m-stack--desktop m-header__wrapper" *ngIf="isLogin">
This might help you
There are 2 divs. div1 and div2.
Initialy, div1 is shown and div2 is hidden.
Onclick of a button, div1 has to be hidden and div2 should be displayed in the place of div1.
Create a state to indicate whether div1 is to be shown or div2 is to be shown. Then, add a onClick handler function to the button. Finally, conditionally render which component is to be shown according to that state.
Code:
class TwoDivs extends React.Component {
state = {
div1Shown: true,
}
handleButtonClick() {
this.setState({
div1Shown: false,
});
}
render() {
return (
<div>
<button onClick={() => this.handleButtonClick()}>Show div2</button>
{
this.state.div1Shown ?
(<div className="div1">Div1</div>)
: (<div className="div2">Div2</div>)
}
</div>
);
}
}
You can achieve it by adding a new property inside component's state. Clicking the button will simply toggle that state, and the component will re-render, due to setState method. Please notice that this will toggle between the two divs. If you only want to show the second one, set the new state like this: this.setState({firstDivIsActive: false}
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
firstDivIsActive: true
};
}
render() {
let activeDiv;
if (this.state.firstDivIsActive) {
activeDiv = <div>I am one</div>;
} else {
activeDiv = <div>I am two</div>;
}
return (
<div>
<button
onClick={() => {
this.setState({
firstDivIsActive: !this.state.firstDivIsActive
});
}}
>
Toggle Div
</button>
{activeDiv}
</div>
);
}
}
I would have done like below in simple HTML and JQuery as below:
<input id="DivType" type="hidden" value="div1" class="valid" />
<div id="div1" />
<div id="div2" />
function ToggleDiv() {
var divType = $("#DivType").val();
if (divType === "div1") {
$("#div1").show();
$("#div2").hide();
$('#DivType input').value = "div2";
}
else if (divType === "div2") {
$("#div1").show();
$("#div2").hide();
$('#DivType input').value = "div1";
}
}
ToggleDiv will be called on OnClick event of button.
Not sure whether there is any better way to do it in React.
class ToggleDivs extends React.Component {
state = {
showDiv1: true,
}
handleButtonClick() {
this.setState({
showDiv1: !this.state.showDiv1
});
}
render() {
const { showDiv1 } = this.state;
const buttonTitle = showDiv1 ? 'Div2' : 'Div1';
const div1 = (<div className="div1">Div1</div>);
const div2 = (<div className="div2">Div2</div>);
return (
<div>
<button onClick={() => this.handleButtonClick()}>Show {buttonTitle}</button>
{showDiv1 ? div1 : div2}
</div>
);
}
}
I am trying to implement a menu that shows up on hover. The problem is that the menu hides while hovering between the DropdownOption child elements, so whenever I hover between options 1 and 2. Each child is a div and has a margin of 0px
openDropdown = () => {
this.setState({showList: true})
}
closeDropdown = () => {
this.setState({showList: false})
}
showList = () => {
return (
<DropdownContainer onMouseEnter={this.openDropdown} onMouseLeave={this.closeDropdown}>
<DropdownOption>Option1</DropdownOption>
<DropdownOption>Option2</DropdownOption>
</DropdownContainer>
)
}
render () {
return (
<div onMouseEnter={this.openDropdown}>
<MenuButtonContainer>
Title
</MenuButtonContainer>
{this.state.showList ? this.showList() : null}
</div>
)
}
This sounds like an event bubbling issue.
Where you have your event listener callbacks, e.g.:
openDropdown = () => {
this.setState({showList: true})
}
Try accessing the event object by including it as a parameter to the function. Then on the event object, call stopPropagation() E.g:
openDropdown = (event) => {
event.stopPropagation()
this.setState({showList: true})
}
I'm trying to write a note taking/organizing app and I've run into a frustrating bug.
Here's my component:
import React from 'react';
const Note = (props) => {
let textarea, noteForm;
if (props.note) {
return (
<div>
<button onClick={() => {
props.handleUpdateClick(props.selectedFolderId, props.selectedNoteId, textarea.value);
}}>
Update
</button>
<textarea
defaultValue={props.note.body}
ref={node => {textarea = node;}}
/>
</div>
);
} else {
return <div></div>;
}
};
export default Note;
As it currently stands, whenever I switch between notes and rerender the note component with new content from the note.body prop, the textarea does not change and retains the content from the previous note. I've tried using the value attribute instead of the defaultValue attribute for the text area which doe fix the problem of the text area content not changing when the component rerenders, but when I do that I'm longer able to type in the textarea field to update the note
Doe anyone know a way I can both allow for users to type in the text field to update the note as well as have the textarea content change when I render different notes?
Thank you
The problem is that setting the value to your prop will cause all re-renders of the component to use the same prop, so new text is obliterated. One solution is to preserve the text in the local state of the component. To simultaneously listen to prop changes, you can set the state when you receive new props.
const Note = React.createClass({
getInitialState() {
return {
text : this.props.note.body
}
},
componentWillReceiveProps: function(nextProps) {
if (typeof nextProps.note != 'undefined') {
this.setState({text: nextProps.note.body });
}
},
render() {
if (this.props.note) {
return (
<div>
<button onClick={(e) => {
// Fire a callback that re-renders the parent.
// render(this.textarea.value);
}}>
Update
</button>
<textarea
onChange={e => this.setState({ text : e.target.value })}
value={this.state.text}
ref={node => {this.textarea = node;}}
/>
</div>
);
} else {
return <div></div>;
}
}
});
https://jsfiddle.net/69z2wepo/96238/
If you are using redux, you could also fire an action on the change event of the input to trigger a re-render. You could preserve the input value in a reducer.
Because componentWillReceiveProps is now unsafe Max Sindwani's answer is now a little out date.
Try these steps:
convert your component to a class
now you can include the shouldComponentUpdate() lifecycle hook
create your event handler and pass it into onChange
in <textarea> you can swap out defaultValue attribute for value (just use event.preventDefault() in the handler so that a user can continue to update text if required)
import React from 'react';
export class Note extends React.Component {
constructor(props) {
super(props);
this.state={text: this.props.note.body}
}
shouldComponentUpdate(nextProps) {
if(nextProps.note.body !== this.state.text) {
this.setState({text: nextProps.note.body})
return true;
}
return false;
}
updateText = (event) => {
event.preventDefault();
this.setState({text: nextProps.note.body});
}
render() {
if (this.props.note) {
return (
<div>
<textarea
onChange={this.updateText}
value={this.state.text}
name={'display'}
/>
</div>
);
} else {
return <div></div>;
}
}});
How can I detect clicks outside a component in Angular?
import { Component, ElementRef, HostListener, Input } from '#angular/core';
#Component({
selector: 'selector',
template: `
<div>
{{text}}
</div>
`
})
export class AnotherComponent {
public text: String;
#HostListener('document:click', ['$event'])
clickout(event) {
if(this.eRef.nativeElement.contains(event.target)) {
this.text = "clicked inside";
} else {
this.text = "clicked outside";
}
}
constructor(private eRef: ElementRef) {
this.text = 'no clicks yet';
}
}
A working example - click here
An alternative to AMagyar's answer. This version works when you click on element that gets removed from the DOM with an ngIf.
http://plnkr.co/edit/4mrn4GjM95uvSbQtxrAS?p=preview
private wasInside = false;
#HostListener('click')
clickInside() {
this.text = "clicked inside";
this.wasInside = true;
}
#HostListener('document:click')
clickout() {
if (!this.wasInside) {
this.text = "clicked outside";
}
this.wasInside = false;
}
Binding to a document click through #Hostlistener is costly. It can and will have a visible performance impact if you overuse it (for example, when building a custom dropdown component and you have multiple instances created in a form).
I suggest adding a #Hostlistener() to the document click event only once inside your main app component. The event should push the value of the clicked target element inside a public subject stored in a global utility service.
#Component({
selector: 'app-root',
template: '<router-outlet></router-outlet>'
})
export class AppComponent {
constructor(private utilitiesService: UtilitiesService) {}
#HostListener('document:click', ['$event'])
documentClick(event: any): void {
this.utilitiesService.documentClickedTarget.next(event.target)
}
}
#Injectable({ providedIn: 'root' })
export class UtilitiesService {
documentClickedTarget: Subject<HTMLElement> = new Subject<HTMLElement>()
}
Whoever is interested for the clicked target element should subscribe to the public subject of our utilities service and unsubscribe when the component is destroyed.
export class AnotherComponent implements OnInit {
#ViewChild('somePopup', { read: ElementRef, static: false }) somePopup: ElementRef
constructor(private utilitiesService: UtilitiesService) { }
ngOnInit() {
this.utilitiesService.documentClickedTarget
.subscribe(target => this.documentClickListener(target))
}
documentClickListener(target: any): void {
if (this.somePopup.nativeElement.contains(target))
// Clicked inside
else
// Clicked outside
}
Improving J. Frankenstein's answer:
#HostListener('click')
clickInside($event) {
this.text = "clicked inside";
$event.stopPropagation();
}
#HostListener('document:click')
clickOutside() {
this.text = "clicked outside";
}
The previous answers are correct, but what if you are doing a heavy process after losing the focus from the relevant component? For that, I came with a solution with two flags where the focus out event process will only take place when losing the focus from relevant component only.
isFocusInsideComponent = false;
isComponentClicked = false;
#HostListener('click')
clickInside() {
this.isFocusInsideComponent = true;
this.isComponentClicked = true;
}
#HostListener('document:click')
clickout() {
if (!this.isFocusInsideComponent && this.isComponentClicked) {
// Do the heavy processing
this.isComponentClicked = false;
}
this.isFocusInsideComponent = false;
}
ginalx's answer should be set as the default one imo: this method allows for many optimizations.
The problem
Say that we have a list of items and on every item we want to include a menu that needs to be toggled. We include a toggle on a button that listens for a click event on itself (click)="toggle()", but we also want to toggle the menu whenever the user clicks outside of it. If the list of items grows and we attach a #HostListener('document:click') on every menu, then every menu loaded within the item will start listening for the click on the entire document, even when the menu is toggled off. Besides the obvious performance issues, this is unnecessary.
You can, for example, subscribe whenever the popup gets toggled via a click and start listening for "outside clicks" only then.
isActive: boolean = false;
// to prevent memory leaks and improve efficiency, the menu
// gets loaded only when the toggle gets clicked
private _toggleMenuSubject$: BehaviorSubject<boolean>;
private _toggleMenu$: Observable<boolean>;
private _toggleMenuSub: Subscription;
private _clickSub: Subscription = null;
constructor(
...
private _utilitiesService: UtilitiesService,
private _elementRef: ElementRef,
){
...
this._toggleMenuSubject$ = new BehaviorSubject(false);
this._toggleMenu$ = this._toggleMenuSubject$.asObservable();
}
ngOnInit() {
this._toggleMenuSub = this._toggleMenu$.pipe(
tap(isActive => {
logger.debug('Label Menu is active', isActive)
this.isActive = isActive;
// subscribe to the click event only if the menu is Active
// otherwise unsubscribe and save memory
if(isActive === true){
this._clickSub = this._utilitiesService.documentClickedTarget
.subscribe(target => this._documentClickListener(target));
}else if(isActive === false && this._clickSub !== null){
this._clickSub.unsubscribe();
}
}),
// other observable logic
...
).subscribe();
}
toggle() {
this._toggleMenuSubject$.next(!this.isActive);
}
private _documentClickListener(targetElement: HTMLElement): void {
const clickedInside = this._elementRef.nativeElement.contains(targetElement);
if (!clickedInside) {
this._toggleMenuSubject$.next(false);
}
}
ngOnDestroy(){
this._toggleMenuSub.unsubscribe();
}
And, in *.component.html:
<button (click)="toggle()">Toggle the menu</button>
Alternative to MVP, you only need to watch for Event
#HostListener('focusout', ['$event'])
protected onFocusOut(event: FocusEvent): void {
console.log(
'click away from component? :',
event.currentTarget && event.relatedTarget
);
}
Solution
Get all parents
var paths = event['path'] as Array<any>;
Checks if any parent is the component
var inComponent = false;
paths.forEach(path => {
if (path.tagName != undefined) {
var tagName = path.tagName.toString().toLowerCase();
if (tagName == 'app-component')
inComponent = true;
}
});
If you have the component as parent then click inside the component
if (inComponent) {
console.log('clicked inside');
}else{
console.log('clicked outside');
}
Complete method
#HostListener('document:click', ['$event'])
clickout(event: PointerEvent) {
var paths = event['path'] as Array<any>;
var inComponent = false;
paths.forEach(path => {
if (path.tagName != undefined) {
var tagName = path.tagName.toString().toLowerCase();
if (tagName == 'app-component')
inComponent = true;
}
});
if (inComponent) {
console.log('clicked inside');
}else{
console.log('clicked outside');
}
}
You can use the clickOutside() method from the ng-click-outside package; it offers a directive "for handling click events outside an element".
NB: This package is currently deprecated. See https://github.com/arkon/ng-sidebar/issues/229 for more info.
Another possible solution using event.stopPropagation():
define a click listener on the top most parent component which clears the click-inside variable
define a click listener on the child component which first calls the event.stopPropagation() and then sets the click-inside variable
You can call an event function like (focusout) or (blur); then you would put in your code:
<div tabindex=0 (blur)="outsideClick()">raw data </div>
outsideClick() {
alert('put your condition here');
}
nice and tidy with rxjs.
i used this for aggrid custom cell editor to detect clicks inside my custom cell editor.
private clickSubscription: Subscription | undefined;
public ngOnInit(): void {
this.clickSubscription = fromEvent(document, "click").subscribe(event => {
console.log("event: ", event.target);
if (!this.eRef.nativeElement.contains(event.target)) {
// ... click outside
} else {
// ... click inside
});
public ngOnDestroy(): void {
console.log("ON DESTROY");
this.clickSubscription?.unsubscribe();
}