I'm trying to update boolean value when mouseover/mouseout (it should change dynamically), to use it later with if else statement and assign some functions based on true/false. But it shows only false and never true. Can someone help me out?
ts:
mouseEv: boolean;
mouseOut(e) {
this.mouseEv = false;
}
mouseOver(e) {
this.mouseEv = true;
}
ngOnInit(): void {
if(this.mouseEv == false){ func(); }
else if(this.mouseEv == true) { otherFunc();};
}
html:
<div (mouseover)=" mouseOver($event)" (mouseout)="mouseOut($event)"></div>
EDIT:
I need to change boolean value dynamically, because I will use it with object that has functions in it and I can't call them from another function.
Create a function for example MouseHandlerEv in wich you recive the boolean value:
.HTML file
<div (mouseover)="mouseEvHandler(true)" (mouseout)="mouseEvHandler(false)"></div>
.TS file
mouseEvHandler(status){
status ? FunctionTrue() : FunctionFalse();
}
Example:
function mouseEvHandler(status){
status ? sayHi() : sayBye();
}
function sayHi() {
console.log('HI');
}
function sayBye() {
console.log('Bye');
}
<div onmouseover="mouseEvHandler(true)" onmouseout="mouseEvHandler(false)">MESSAGE ON CONSOLE</div>
Extrapolate it to angular
You can simply use it like below without creating a function for event:
<div (mouseover)="mouseEv=true" (mouseout)="mouseEv=false"></div>
You can also try passing true and false directly as method argument in mouseOver(true) and receive its value in component.
At the moment, the values are checked only once at beginning of the component since you're checking the values in ngOnInit() hook. You could instead try to make it reactive using RxJS fromEvent and use it to trigger events.
Try the following
Template
<div #mouseControl></div>
import { Component, ViewChild, ElementRef, AfterViewInit } from '#angular/core';
#Component({ ... })
export class AppComponent implements AfterViewInit {
#ViewChild('mouseControl') mouseControl: ElementRef<any>;
ngAfterViewInit() {
fromEvent(this.mouseControl.nativeElement, 'mouseover').subscribe(
_ => otherFunc();
);
fromEvent(this.mouseControl.nativeElement, 'mouseout').subscribe(
_ => func();
);
}
}
Related
This question related to Syntactically anonymous/Arrow Function/add-hoc/factory DP functions:
I have a component which is embedded in the Html.
The component has a click event which is binded to a function. This function content depend on another component which has a reference to this component.
This is the component with the click event:
HTML:
<div (click)="doSomething()">Content.....</div> \\ Should it be with a brackets ?
In the component I just want to define the function signature:
#Component({
selector: 'app-embedded'
})
export class className
{
constructor() { }
ngOnInit() { }
doSomething:(booleanparams: boolean) => any; //The function get a boolean parameter as input and return void or any
}
Now this is where the component is embedded:
<div >
<app-embedded #emb></app-embedded>
</div>
This is the component of the container of the embedded component, which has a reference to the embedded component:
#Component({
selector: 'app-container',
})
export class container
{
#ViewChild('emb') private emb: ElementRef;
booleanParam : booelan;
constructor()
{
emb.doSomething = containerFunction(true);
}
containerFunction(booleanParam : boolean)
{
// do something in this context
}
}
The idea is that this embedded component is embedded in many other containers and whenever the click event triggered a function that was set in the doSomething function variable should be executed.
What changes in the code I need to do in order to accomplish this ?
The best way i see of doing this would be to simply use an event emitter and capture the event on the other side? so embedded would have this:
#Component({
selector: 'app-embedded'
})
export class className
{
#Output()
public something: EventEmitter<boolean> = new EventEmitter<boolean>();
constructor() { }
ngOnInit() { }
doSomething:(booleanparams: boolean) {
this.something.emit(booleanparams);
}; //The function get a boolean parameter as input and return void or any
}
Then where it is called:
<div >
<app-embedded #emb (something)="doSomething($event)"></app-embedded>
</div>
Other solution that would allow a return
#Component({
selector: 'app-embedded'
})
export class className
{
#Input()
public somethingFunc: (boolean)=>any;
constructor() { }
ngOnInit() { }
doSomething:(booleanparams: boolean) {
let w_potato = this.somethingFunc(booleanparams);
//Do whatever you want with w_potato
}; //The function get a boolean parameter as input and return void or any
}
in this case the view would be
<div >
<app-embedded #emb [somethingFunc]="doSomething"></app-embedded>
</div>
I hope this helps! Passing the function or emitting an event will be much more angular than trying to modify an instance of a component. On top of that, a constructor is only called once when Angular starts up so #emb at that time will not be defined to be anything. If you wanted to do it that way you would have to bind yourself in something ngAfterViewInit.
But again, I think that passing it through attributes will be much more angular looking.
Good Luck let me know if this doesn't suit your answer.
I was just wondering if there's any way, shape or form to reference a function that's created within ngOnInit(), or some sort of closure you can create to do so?
Basically:
component(){
somefunc()
//be able to call the function that's created in ngOnInit from the component via
//click event after the component renders
ngOnInit() {
function somefunc(){ ...whatever }
}
}
Is there any way to do this?
It can be done by assigning the method to a class member property in ngOnInit. In the code below, I define the method as an arrow function, to make sure that this refers to the instance of the component in the body of the method. See this stackblitz for a demo.
export class AppComponent implements OnInit {
public onButtonClick: (event: Event) => void; // Member property will refer to the method
ngOnInit() {
let data = "And this comes from the closure!"; // Can be used inside of onButtonClick
// Assign the method to the member property
this.onButtonClick = (event: Event): void => {
console.log("The button was clicked!", data);
...
};
}
}
The method can then be used as an event handler:
<button (click)="onButtonClick($event)">Click me!</button>
Your pseudo syntax is a bit confusing.
You can call a created function like this:
import { Component, OnInit } from '#angular/core';
#Component({
templateUrl: './product-list.component.html',
styleUrls: ['./product-list.component.css']
})
export class ProductListComponent implements OnInit {
showImage = false;
constructor() {
}
ngOnInit(): void {
this.toggleImage();
}
toggleImage(): void {
this.showImage = !this.showImage;
}
}
You can also call it from a click event on a button like this:
<button class='btn btn-primary'
(click)='toggleImage()'>
Show Image
</button>
Is this what you are asking?
I am using 'angular2-virtual-scroll' to implement load on demand. The items used to be driven by observable's using the async pipe triggered by the parent component. Now i am trying to call my service from the child. The call is successful and i get my data, i need to use the subscribe event to apply other logic. The issue is change detected does not appear to be working when i update my arrays in the subscribe function. I have read other similar issues but i have had no luck finding a solution.
This is the main component where the service calls are used. The inital request is done from the onInit. And then when you scroll down fetchMore is called.
import { Component, OnInit, Input, OnDestroy } from '#angular/core';
import { Store } from '#ngrx/store';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import { User } from './../models/user';
import { Role } from './../../roles/models/role';
import { UsersService } from './../services/users.service';
import { ChangeEvent } from 'angular2-virtual-scroll';
import { promise } from 'selenium-webdriver';
import { VirtualScrollComponent } from 'angular2-virtual-scroll';
import { Subscription } from 'rxjs/Subscription';
#Component({
selector: 'app-users-list',
template: `
<div class="status">
Showing <span class="">{{indices?.start + 1}}</span>
- <span class="">{{indices?.end}}</span>
of <span class="">{{users?.length}}</span>
<span>({{scrollItems?.length}} nodes)</span>
</div>
<virtual-scroll [childHeight]="75" [items]="users" (update)="scrollItems = $event" (end)="fetchMore($event)">
<div #container>
<app-user-info *ngFor="let user of scrollItems" [roles]="roles" [user]="user">
<li>
<a [routerLink]="['/users/edit/', user.id]" class="btn btn-action btn-edit">Edit</a>
</li>
</app-user-info>
<div *ngIf="loading" class="loader">Loading...</div>
</div>
</virtual-scroll>
`
})
export class UsersListComponent implements OnInit, OnDestroy {
users: User[] = [];
#Input() roles: Role[];
currentPage: number;
scrollItems: User[];
indices: ChangeEvent;
readonly bufferSize: number = 20;
loading: boolean;
userServiceSub: Subscription;
constructor(private usersService: UsersService) {
}
ngOnInit() {
this.reset();
}
ngOnDestroy() {
if(this.userServiceSub) {
this.userServiceSub.unsubscribe();
}
}
reset() {
this.loading=true;
this.currentPage = 1;
this.userServiceSub = this.usersService.getUsers(this.currentPage).subscribe(users => {
this.users = users;
});
}
fetchMore(event: ChangeEvent) {
if (event.end !== this.users.length) return;
this.loading=true;
this.currentPage += 1;
this.userServiceSub = this.usersService.getUsers(this.currentPage).subscribe(users => {
this.users = this.users.concat(users);
});
}
}
From what i have read this could be a context issue but i am not sure. Any suggestions would be great.
"EDIT"
Looking at the source code for the plugin component i can see where the change event is captured.
VirtualScrollComponent.prototype.ngOnChanges = function (changes) {
this.previousStart = undefined;
this.previousEnd = undefined;
var items = changes.items || {};
if (changes.items != undefined && items.previousValue == undefined || (items.previousValue != undefined && items.previousValue.length === 0)) {
this.startupLoop = true;
}
this.refresh();
};
If i put a breakpoint in this event it fires on the initial load, so when we instantiate the array to []. It fires when i click on the page. But it does not fire when the array is update in the subscribe event. I have even put a button in that sets the array to empty, and that updates the view so the subscribe function must be breaking the change detection.
So when you say the change detection does not appear to be working, I assume you are referring to this: *ngFor="let user of scrollItems"?
I have not used that particular component nor do I have any running code to work with ... but I'd start by taking a closer look at this:
<virtual-scroll [childHeight]="75"
[items]="currentBuffer"
(update)="scrollItems = $event"
(end)="fetchMore($event)">
Maybe change the (update) to call a method just to ensure it is emitting and that you are getting what you expect back from it.
EDIT:
Here is an example subscription that updates the primary bound property showing the data for my page:
movies: IMovie[];
getMovies(): void {
this.movieService.getMovies().subscribe(
(movies: IMovie[]) => {
this.movies = movies;
this.performFilter(null);
},
(error: any) => this.errorMessage = <any>error
);
}
The change detection works fine in this case. So there is most likely something else going on causing the issue you are seeing.
Note that your template does need to bind to the property for the change detection to work. In my example, I'm binding to the movies property. In your example, you'd need to bind to the users property.
So change detection was not firing. I had to use "ChangeDetectorRef" with the function "markForCheck" to get change detection to work correctly. I am not sure why so i definitely have some research to do.
Trying to show a popup after routing event I'm facing this issues :
the angular routing event is firing many times after one routerLink click, so I open many popup .
To resolve the first problem I created a boolean to check if it is the first event.
But when I try to change the boolean after the first routing events call it always take the same value in events following.
My question is : is the value of this in subscribe Method a copy of my component ?, otherwise what can cause this issue and how can I solve it.
thanks a lot.
Code :
#Component({
template ='<a routerLink ="/something" >'
})
export class MyComponent implements OnInit{
firstcall : boolean = true;
constructor(private _router : Router){
this._router.events.filter(event => event instanceof NavigationStart && something)
.subscribe( (event) => {
if(this.firstcall) {
this.showPopup()
this.firstcall=false
}
}
}
}
Update
even with this._router.events.distinct(event => event['url']).subscribe not working
Add a variable of "this" (of the class) before the subscribe call and use that variable inside subscribe, else it use the "this" instance of the subscribe method.
Try this,
#Component({
template ='<a routerLink ="/something" >'
})
export class MyComponent implements OnInit{
firstcall : boolean = true;
that : any = this;
constructor(private _router : Router){
this._router.events.filter(event => event instanceof NavigationStart && something)
.subscribe( (event) => {
if(that.firstcall) {
that.showPopup()
that.firstcall=false
}
}
}
}
Hope this helps!
I tried to change a value in component file, but the current and updated value not rendered in the template file. Here is my Code.
import { Component } from '#angular/core';
#Component({
selector: 'cr-content-upload',
templateUrl: './app/template.html'
})
export class ContentUploadComponent {
constructor( ) { }
toggleContent:boolean = false;
updateContent(data:any) {
if(data == "streaming") {
this.toggleContent = true;
console.log(this.toggleContent);
}
}
ngOnInit() {
}
}
and here is my template.html
<a href="javascript:void(0)" (click)="updateContent('streaming')">
<div *ngIf="toggleContent">
This is sample Content
</div>
Note: When the value is logged to console, it prints the updated value. But, the updated value doesn't get rendered into template.html file.
And also i get this issue in many occasion. So, kindly provide solution and also reason for the issue.
Your code looks fine, but if your template isn't detecting the changes, you could try and use ChangeDetectorRef.
import {ChangeDetectorRef} from '#angular/core'
and in your component inject it to your constructor and use it after setting your boolean to true.
constructor(private changeDetectorRef: ChangeDetectorRef)
updateContent(data:any) {
if(data == "streaming") {
this.toggleContent = true;
this.changeDetectorRef.detectChanges();
}
}