Angular2 fire click event on unknown html element - javascript

I want to handle a click event of x3dom's shape html element in my parent component. The onclick event is fired by x3dom. I can only delegate it with an ugly hack (see below).
Because shape is not a known Html tag by Angular 2 I have to define ashape component? or Not?
In Parent Component:
<shape (click)="doStuffInParent()" ></shape> <!-- click is not fired by x3dom -->
Shape component so far:
import {Component} from '#angular/core';
import { Input, Output, EventEmitter} from '#angular/core';
#Component({
selector: 'Shape',
providers: [],
directives: [],
pipes: []
})
export class Shape {
#Output() notify: EventEmitter<string> = new EventEmitter<string>(); // wrong!
constructor() {}
}
Edit: Maybe I don't need the component? The onclick event is fired by X3dom and not by me.
another solution for me would be if I can just call my component method from a regular onclick event what I asked here.
Update I hacked a solution what solved at least my problem:
call Angular 2 component method from html event

Working Demo : https://plnkr.co/edit/qQudgi9touIFOe52m4JY?p=preview
<Shape (myClick)="doStuffInParent($event)"></Shape>
in Component code,
doStuffInParent(value){
console.log(value); //Angular2
}
import {Component} from '#angular/core';
import { Input, Output, EventEmitter} from '#angular/core';
#Component({
selector: 'Shape',
providers: [],
directives: [],
pipes: [],
template:`<div (click)="click()">Shap Component</div>`
})
export class Shape {
#Output() myClick: EventEmitter<string> = new EventEmitter<string>();
click(){
this.myClick.next('Angular2');
}
}

Related

Listen to Custom DOM Events in Angular

I use an Angular library, which has a component, which uses CustomEvents to dispatch something, like this:
const domEvent = new CustomEvent('unselect', {
bubbles: true
});
this.elementRef.nativeElement.dispatchEvent(domEvent);
How can I listen to this Event in the parent component?
I know it is discouraged and I should normally use EventEmitters. But I have no access to overwrite the child component and there is no #Output Event defined. So this is the only thing I could use.
You can use HostListener to listen for this custom event. The following example triggers the custom event from a child component with the parent component listening for the event. You can even use args (second argument) such as ['$event.target'] to determine what element triggered the event.
This uses ngAfterViewInit() lifecycle hook, but it's just for demonstration and just to ensure the element ref is ready.
Parent:
import { Component, HostListener } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
name = 'Angular';
#HostListener('unselect', ['$event.target'])
onUnSelect(el) {
console.log(el); // element that triggered event, in this case HTMLUnknownElement
console.log('unselect triggered');
}
}
Child:
import { Component, Input, ElementRef, AfterViewInit } from '#angular/core';
#Component({
selector: 'hello',
template: `<h1>Hello {{name}}!</h1>`,
styles: [`h1 { font-family: Lato; }`]
})
export class HelloComponent {
#Input() name: string;
constructor(private el: ElementRef) {}
ngAfterViewInit() {
const domEvent = new CustomEvent('unselect', { bubbles: true });
this.el.nativeElement.dispatchEvent(domEvent);
}
}
Here is an example in action.
Hopefully that helps!

How to call other component function from different component?

I've html structure like this :
Parent Component where both Comp1 and Comp2 resides :
now in comp1 I've some elements if that changes then I've to reflect values in comp2 but there's no connection between them.
Comp1 :
import { Component } from '#angular/core';
import { Comp2Component } from 'comp2.component';
#Component({
selector: 'comp1',
templateUrl: './comp1.html'
})
export class Comp1Component {
sortBy(value)
{
this.FeedSortBy = value;
this.SortByLabel = this.SortByLabelList[value];
Comp2Component.filterActivities();
}
}
Comp2
import { Component } from '#angular/core';
#Component({
selector: 'comp2',
templateUrl: './comp2.html'
})
export class Comp2Component {
filterActivities()
{
//call this function on comp1 sort function call so it will update value of comp2
}
}
As per Rahul and Sajit's answer I try using with EventEmitter and change my structure to parent child :
In my parent component I use :
import { Component,EventEmitter, Input, Output, OnInit } from '#angular/core';
import { FeedsComponent } from '../feeds/feeds.component';
#Component({
selector: 'my-desk',
styleUrls: ['../../assets/css/style.min.css'],
templateUrl: './my-desk.component.html'
})
export class MyDeskComponent {
#Output() FeedSortBy = new EventEmitter<string>();
sortBy(value)
{
this.FeedSortBy.emit(value);
}
}
and in my child component I use :
import { Component, OnInit, Input, Output } from '#angular/core';
import { DataService } from '../data.service';
declare var $: any;
#Component({
selector: 'feeds',
styleUrls: ['../../assets/css/style.min.css'],
templateUrl: './feeds.component.html'
})
export class FeedsComponent {
constructor(private dataService:DataService)
{
}
#Input() FeedSortBy:number = 2;
}
Child component HTML :
{{FeedSortBy}}
But it always output 2 it doesn't change can I get any trigger as well to know if value is change so I call function there
You cannot do that, There are two possible ways you could achieve this,
use angular service to pass the data between two components
use Event Emitters to pass the value among the components.
You can call method of another component from a different component but it will not update the value of the calling component without some tweaking like
Event Emitters if they have a parent child relationship or Shared Services or using ngrx redux pattern
How to Call a different component method be like
Component1
test(){
console.log("Test");
}
Component 2
working(){
let component = new Component1();
component.test();
}
Now to update the value in component 2 you might have to use any of the above.
For Event Emitters follow this link
For Shared services follow this link
For ngrx follow this link

Angular #Output not working

Trying to do child to parent communication with #Output event emitter but is no working
here is the child component
import { Component, OnInit, Output, Input, EventEmitter } from '#angular/core';
#Component({
selector: 'app-emiter',
templateUrl: './emiter.component.html',
styleUrls: ['./emiter.component.css']
})
export class EmiterComponent implements OnInit {
#Output() emitor: EventEmitter<any>
constructor() { this.emitor = new EventEmitter()}
touchHere(){this.emitor.emit('Should Work');
console.log('<><><><>',this.emitor) // this comes empty
}
ngOnInit() {
}
}
this is the html template
<p>
<button (click)=" touchHere()" class="btn btn-success btn-block">touch</button>
</p>
The console.log inside the touchHere it shows nothing
even if I put this inside the parent component it show nothing as well
parent component
import { Component , OnInit} from '#angular/core';
// service I use for other stuff//
import { SenderService } from './sender.service';
// I dont know if I have to import this but did it just in case
import { EmiterComponent } from './emiter/emiter.component'
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'app';
user: any;
touchThis(message: string) {
console.log('Not working: ${message}');
}
constructor(private mySessionService: SenderService) { }
}
and here is the html template
<div>
<app-emiter>(touchHere)='touchThis($event)'</app-emiter>
</div>
Parent component template:
<app-emitor (emitor)='touchThis($event)'></app-emiter>
In parent template #Output should be 'called', not the child method.
Also, see: https://angular.io/guide/component-interaction#parent-listens-for-child-event
Here’s an example of how we write a component that has outputs:
#Component({
selector: 'single-component',
template: `<button (click)="liked()">Like it?</button>`
})
class SingleComponent {
#Output() putRingOnIt: EventEmitter<string>;
constructor() {
this.putRingOnIt = new EventEmitter();
}
liked(): void {
this.putRingOnIt.emit("oh oh oh");
}
}
Notice that we did all three steps: 1. specified outputs, 2. created an EventEmitter that we attached
to the output property putRingOnIt and 3. Emitted an event when liked is called.
If we wanted to use this output in a parent component we could do something like this:
#Component({
selector: 'club',
template: `
<div>
<single-component
(putRingOnIt)="ringWasPlaced($event)"
></single-component>
</div>`
})
class ClubComponent {
ringWasPlaced(message: string) { console.log(`Put your hands up: ${message}`);
} }
// logged -> "Put your hands up: oh oh oh"
Again, notice that:
putRingOnIt comes from the outputs of SingleComponent
ringWasPlaced is a function on the ClubComponent
$event contains the thing that wasemitted, in this case a string
<app-emiter (emitor)="touchThis($event)" ></app-emiter>
By using #Output() you should apply the event you need to emit in the directive of the emitter component.Adding the name of the variable to the the directive and but the emitted over function inside the quotation passing the $event.
touchHere() is the method from which you are binding some value to emit with your EventEmitter. And your EventEmitter is 'emitor'.
So your code will work if you simply do the below:
<app-emiter (emitor)='touchThis($event)'></app-emiter>

How to animate the on enter and on leave of an updating Observable's values in an Angular+ template binding?

Plnkr:
https://plnkr.co/edit/ka6ewGPo1tgnOjRzYr3R?p=preview
I have a subscription that will change overtime:
ngOnInit(){
this.route.snapshot.data.remote.subscribe(x => {
this.obs$ = x[0];
});
}
I have a template work-post.component.html that showcases these changes:
<h1>{{obs$.title}}</h1>
<p>
{{obs$.paragraph}}
</p>
Question:
When obs$ gets each next/new value, Is it possible to animate the enter and leave animations of these values. Can obs$.title obs$.paragraph bindings have a "crossfade" e.g. fade out old text, fade in the new text on change? if so, how could I implement this animation inside of my component and template:
component:
import { Component, ChangeDetectionStrategy, Input, OnInit } from '#angular/core';
import { Observable } from 'rxjs';
import { ActivatedRoute } from '#angular/router';
#Component({
selector: 'work-post',
templateUrl: 'work-post.component.html',
styleUrls: ['work-post.component.scss'],
})
export class WorkPostComponent implements OnInit{
public obs$;
constructor(private route: ActivatedRoute){}
ngOnInit(){
this.route.snapshot.data.remote.subscribe(x => {
this.obs$ = x[0];
});
}
}
Here's a video of how the UI currently looks.
If I understood well, you want to apply X method (animations) when your obs$ changes value. So, in my opinion, you need a listener.
For this, I would go for DoCheck.
In component:
import { Component, DoCheck, ElementRef, OnInit } from '#angular/core';
export class MyClass implements OnInit, DoCheck {
ngDoCheck() {
if(this.obs$ !== 'whateverValue') {
// Apply an animation because this.obs$ changed
}
}
Documentation: https://angular.io/docs/ts/latest/api/core/index/DoCheck-class.html
Update 1:
I suggest that you store an initial version of this.obs$ in, for example, this.obs_initial.
And inside the logic in the listener you compare both, if this.obs$ is different from its previous value (this.obs_initial) then means that this.obs$ changed and therefore we trigger X animation.

Angular2 - using child component as value of an attribute

I want to show a popover as the user clicks on the input field which works fine but I want the data-content attribute of that popover be coming from the template of a child component. Here is an example:
parent.ts
import {Component,AfterViewInit} from 'angular2/core';
import {bootstrap} from 'angular2/platform/browser';
import {ChildComponent} from './child_test.ts';
#Component({
selector: 'app',
template: `<input type='text' data-toggle="popover" data-trigger="focus" data-placement="bottom" [attr.data-content]="getPopoverContent()" />`,
providers: [ChildComponent]
})
class AppComponent implements AfterViewInit{
constructor(private _child: ChildComponent) {}
getPopoverContent(){
return this._child; //returning empty object instead of child template
}
ngAfterViewInit(){
$("input").popover();
}
}
bootstrap(AppComponent);
child.ts
import {Component} from 'angular2/core';
#Component({
selector: "child-component",
template: "<div>Popover content from child.</div>"
})
export class ChildComponent{};
Should I use DynamicComponentLoader instead of dependency injection? if so then how can I achieve this?
Here's a workaround:
Assign the a temporary variable to the component you want to display
<transaction-filter #popoverComponent></transaction-filter>
Important: The component above must expose an ElementRef in its definition
constructor(public el: ElementRef) {}
Create the element that will show the popover
<button class="btn-sm btn-link text-muted"
data-animation="true"
data-placement="bottom"
title="Create Rule"
[popover]="popoverComponent">
Create Rule...
</button>
Now the popover directive itself:
/// <reference path="../../typings/tsd.d.ts"/>
import 'bootstrap'
import { Directive, ElementRef, Input} from 'angular2/core'
declare var $: JQueryStatic;
#Directive({
selector: '[popover]',
})
export class PopoverDirective {
#Input('popover') _component: any
_popover: JQuery
constructor(private _el: ElementRef) { }
ngOnInit() {
// Hide the component
this._component.el.nativeElement.style.display = "none"
// Attach it to the content option
this._popover = $(this._el.nativeElement)
.popover({
html: true,
content: this._component.el.nativeElement
})
// When the below event fires, the component will be made visible and will remain
this._popover.on('shown.bs.popover', () => {
this._component.el.nativeElement.style.display = "block"
})
}
}
One problem is that binding to an attribute stringifies the value
[attr.data-content]
therefore this approach won't work.
It seems the Bootstrap popover expects a string, therefore this would be fine but stringifying an Angular component won't you give it's HTML.

Categories