how to trigger event click similar to $(' ').trigger('click') does at div by function in angular 5? - angular5

child2.component.html
<div class="child2">child2</div>
app.component.html
<app-child2></app-child2> // child2.component
<div class="app">app</div>
<div (click)="open()">button</div>
app.component.ts
open(){
$('.child2').trigger('click');
$('.app').trigger('click');
}
I want to trigger using :-
let element: HTMLElement = document.querySelector('.child2')as HTMLElement;
element.click();
let element2: HTMLElement = document.querySelector('.app') as HTMLElement;
element2.click();

1 - add on identifier on your div,
<div class="child2" #child2>child2</div>
2 - In your component get the HTMLElement using #ViewChild decorator
import { ViewChild } from '#angular/core';
export class MyChildComponent {
#ViewChild("child2") myChild2;
public externalClick(){
this.myChild2.nativeElement.click();
}
3 - In your AppComponent
#ViewChild(MyChildComponent) myChildComp : MyChildComponent;
open(){
this.myChildComp.externalClick();
}

Related

Bootstrap 5 tooltip || popover

I have a little problem with use tooltip or popover in vue app from bootstrap.
<template>
...
<i :title="person.jobTitle" class="fa fa-tag" data-bs-placement="left" data-bs-toggle="tooltip" #click="showDetails"></i>
...
</template>
method:
<script>
import {Popover} from "bootstrap";
...
methods: {
showDetails(event) {
new Popover(event.target)
}
}
...
</script>
Im 100% sure im doing something wrong :).
I have to click twice to showup popover, and it's not closing.
While I create example:
<button id="tolek" ref="tolek" title="aha" class="btn btn-secondary">
Popover
</button>
and in mounted:
new Popover(this.$refs.tolek)
it's all work beautiful.
Edit:
looks like create directive do the job:
<i title="Job Title" class="fa fa-tag" v-popover:click="person.jobTitle"></i>
directive:
directives: {
popover: {
beforeMount(el, binding) {
new Popover(el, {content: binding.value})
}
}
},
Don't mix the BS data-bs- attributes with Vue. Make sure to use the show() method after the popover is instantiated...
methods: {
showDetails(event) {
this.bsPopover = new Popover(event.target, {placement: 'left', trigger: 'click'})
this.bsPopover.show()
}
}
Demo
For Vue3 and Bootstrap 5 you could use Directives
My answer is inspired on the answer from Sergio Azócar and I used TypeScript instead of JavaScript.
Create file bootstrap.ts
import { Tooltip, Popover } from 'bootstrap'
export const tooltip = {
mounted(el: HTMLElement) {
const tooltip = new Tooltip(el)
}
}
export const popover = {
mounted(el: HTMLElement) {
const popover = new Popover(el)
}
}
In main.ts add
import { tooltip } from 'your_path/tooltip'
app
.directive('tooltip', tooltip)
.directive('popover', popover)
.mount("#app");
And use it
<button v-tooltip title="Hello world from a button!">
I am a button
</button>
<div v-popover title="Hello world from a div!" data-bs-content="And with some amazing content">
I am a div
</div>

Angular undefined value of #input variable

I'm new to Angular and I have some issues , hope you'll help me.
so I'm trying to share a value of a variable from a ProjectComponent to an AcceuilComponent , the value of this variable is displaying correctly into my acceuil.component.html but when I try to use it into my acceuil.component.ts it's undefined !
project.component.html (the parent component)
<app-header-in></app-header-in>
<ng-sidebar-container>
<ng-sidebar [opened]="opened">
<p> Sidebar </p>
<button (click)="Sidebar()">
Close Sidebar
</button>
<ul class="menu">
<li class="hh"
*ngFor="let project of projects"
[class.selected]="project === selectedProject"
(click)="onSelect(project)">
{{project.nomprojet}}</li>
</ul>
</ng-sidebar>
<div ng-sidebar-content >
<br><br><br><br>
<button (click)="Sidebar()">
Open Sidebar
</button>
<app-acceuil [message]="idProject"></app-acceuil>
</div>
</ng-sidebar-container>
project.component.ts
import { Component, OnInit } from '#angular/core';
import { ApiService } from '../api.service';
import {ProjectService} from '../project.service';
import {PartService} from '../part.service';
#Component({
selector: 'app-project',
templateUrl: './project.component.html',
styleUrls: ['./project.component.css']
})
export class ProjectComponent implements OnInit {
opened=true;
projects:any;
idProject;
selectedProject;
constructor(private projectService:ProjectService) { }
ngOnInit(): void {
this.projectService.getProjects().subscribe((result)=>{console.log("result",result)
this.projects=result})
}
Sidebar(){
this.opened=!this.opened;
}
onSelect(pro): void {
this.idProject = pro.id;
}
}
acceuil.component.html (my child component)
<p>{{message}}</p>
<ul >
<li class="hh"
*ngFor="let part of parts">
{{part.nomparti}}
</li>
</ul>
acceuil.component.ts
import { Component, OnInit,Input } from '#angular/core';
import { ApiService } from '../api.service';
import {PartService} from '../part.service';
#Component({
selector: 'app-acceuil',
templateUrl: './acceuil.component.html',
styleUrls: ['./acceuil.component.css']
})
export class AcceuilComponent implements OnInit {
#Input() message;
parts:any;
constructor(private partService:PartService) {
}
ngOnInit(): void{
console.log("id",this.message);
this.partService.getPartsFromIdProject(this.message).subscribe((result)=>{console.log("result",result)
this.parts=result})
}
ngOnChanges() {
if(this.message) {
console.log(this.message)
}
}
}
I'm using the message to call a service and displaying data .
in the acceuil.component.html <p>{{message}}</p> is displaying correctly but console.log("id",this.message); in acceuil.component.ts displays undefined
As message is an input property, you need to get its value in ngOnchanges life cycle.
First time, when it is in ngOnChanges, input value will be undefined. So for the safe side, better to add a check for not undefiled condition like below
ngOnChanges(changes: SimpleChanges) {
if (changes.message) {
// Should be defined now
console.log(this.message);
}
}

Unable to dynamically reload component

I am trying to dynamically load a tab component based on screen resolution. I have a service with an observable with values: xl, md, sm, xs. The component initially loads on xs and then unloads on screen resizes. The problem is when you resize the screen back to under 768 (xs) the component is not fully materialized. I can see that the component is injected into the DOM on the second load, but it appears the directives are not rendered.
Plnkr - Try sizing the display full width, then resize back to smallest size to reload tab component
import {Component, bootstrap, DynamicComponentLoader, ElementRef, ComponentRef} from 'angular2/core'
import {UiTabs, UiPane} from './ui_tabs'
import {TabbedLayout} from './tabbed_layout'
import {ResizeSvc} from './resize_svc';
#Component({
selector: 'my-app',
template: `
<div>
<h2>Hello {{name}}</h2>
<div #location></div>
</div>
`,
providers: [ResizeSvc]
})
export class App implements OnInit{
private resizeSvc: ResizeSvc;
private _children:ComponentRef;
constructor(private _dcl: DynamicComponentLoader, private _e: ElementRef, pResizeSvc:ResizeSvc) {
this.name = 'Angular2'
this.resizeSvc = pResizeSvc;
}
ngOnInit() {
console.log('initialized app.ts');
this.resizeSvc.layout$.subscribe(
value => this.setLayout(value)
);
}
setLayout(pSize:string) {
this.removeAll();
if(pSize === 'xs') {
console.log('loading layout ' + pSize);
//this._dcl.loadIntoLocation(TabbedLayout, this._e, 'location').then((ref) => {
this._dcl.loadNextToLocation(TabbedLayout, this._e).then((ref) => {
ref.instance._ref = ref;
this._children = ref;
});
} else {
}
}
removeAll() {
if(this._children != null) {
console.log('Disposing layout...');
this._children.dispose();
this._children = null;
}
}
}
Here is a picture of the DOM when initially loaded
And here is a picture of the DOM on the second load with missing tabs
The order of <script> imports matters. angular2-polyfills.js has to be after system-polyfills.js. Here is your plunker working
I just moved the import below to the top of the list.
<script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.20/system-polyfills.js"></script>

Aurelia - Watch Dependency Value for Change

Suppose you have a class you are injecting into a another class or component. Is there a way to watch for changes on an attributed of the dependency you are injecting and act upon it?
For example, say you have the following app:
app.html
<template>
<input type="text" value.bind="item">
<button click.trigger="addToList()">Add</button>
<h3>Modded</h3>
<ul>
<li repeat.for="it of modded">${it}</li>
</ul>
<h3>Original</h3>
<ul>
<li repeat.for="it of dep.items">${it}</li>
</ul>
</template>
app.js
import {bindable, inject} from 'aurelia-framework';
import {Dep} from './dep';
#inject(Dep)
export class App {
constructor(dep) {
this.dep = dep;
}
attached() {
this.modifyItems();
}
addToList() {
this.dep.addItem(this.item);
}
modifyItems() {
this.modded = [];
for (let item of this.dep.items) {
this.modded.push(item.toUpperCase());
}
}
}
dep.js
export class Dep {
constructor() {
this.items = ['one', 'two', 'three'];
}
addItem(item) {
this.items.push(item);
}
}
Now, let's say that some other component modifies Dep.items. Is there a way to watch for changes in app.js on this.dep.items and then call modifyItems()?
Assume modifyItems() is more complex than this example so maybe a value converter is not the best option. (unless it is the only option I guess)
Here is working plunker with the above example: http://plnkr.co/edit/rEs9UM?p=preview
Someone pointed me to the BindingEngine.collectionObserver and it appears that is what I needed.
app.js:
import {inject} from 'aurelia-framework';
import {BindingEngine} from 'aurelia-binding';
import {Dep} from './dep';
#inject(Dep, BindingEngine)
export class App {
constructor(dep, bindingEngine) {
this.dep = dep;
let subscription = bindingEngine.collectionObserver(this.dep.items)
.subscribe((newVal, oldVal) => {
console.debug(newVal, oldVal);
this.modifyItems();
});
}
attached() {
this.modifyItems();
}
addToList() {
this.dep.addItem(this.item);
this.item = '';
}
modifyItems() {
this.modded = [];
for (let item of this.dep.items) {
this.modded.push(item.toUpperCase());
}
}
}
Here is the working pluker: http://plnkr.co/edit/Pcyxrh?p=preview

Angular 2 equivalent of ng-bind-html, $sce.trustAsHTML(), and $compile?

In Angular 1.x, we could insert HTML in real-time by using the HTML tag ng-bind-html, combined with the JavaScript call $sce.trustAsHTML(). This got us 80% of th way there, but wouldn't work when Angular tags were used, such as if you inserted HTML that used ng-repeat or custom directives.
To get that to work, we could use a custom directive that called $compile.
What is the equivalent for all of this in Angular 2? We can bind using [inner-html] but this only works for very simple HTML tags such as <b>. It doesn't transform custom angular 2 directives into functioning HTML elements. (Much like Angular 1.x without the $compile step.) What is the equivalent of $compile for Angular 2?
In Angular2 you should use DynamicComponentLoader to insert some "compiled content" on the page. So for example if you want to compile next html:
<div>
<p>Common HTML tag</p>
<angular2-component>Some angular2 component</angular2-component>
</div>
then you need to create component with this html as a template (let's call it CompiledComponent) and use DynamicComponentLoader to insert this component on the page.
#Component({
selector: 'compiled-component'
})
#View({
directives: [Angular2Component],
template: `
<div>
<p>Common HTML tag</p>
<angular2-component>Angular 2 component</angular2-component>
</div>
`
})
class CompiledComponent {
}
#Component({
selector: 'app'
})
#View({
template: `
<h2>Before container</h2>
<div #container></div>
<h2>After conainer</h2>
`
})
class App {
constructor(loader: DynamicComponentLoader, elementRef: ElementRef) {
loader.loadIntoLocation(CompiledComponent, elementRef, 'container');
}
}
Check out this plunker
UPD You can create component dynamically right before the loader.loadIntoLocation() call:
// ... annotations
class App {
constructor(loader: DynamicComponentLoader, elementRef: ElementRef) {
// template generation
const generatedTemplate = `<b>${Math.random()}</b>`;
#Component({ selector: 'compiled-component' })
#View({ template: generatedTemplate })
class CompiledComponent {};
loader.loadIntoLocation(CompiledComponent, elementRef, 'container');
}
}
I personally don't like it, it's look like a dirty hack to me. But here is the plunker
PS Beware that at this moment angular2 is under active development. So situation can be changed at any time.
DynamicComponentLoader is deprecated, you can use ComponentResolver instead
You could use this directive, add pipes if you need additional data manipulation. It also allows for lazy loading, you don't need it in your case, but it's worth mentioning.
Directive(I found this code and made some changes, you can do that too to make it fit your taste or use it as is):
import { Component, Directive, ComponentFactory, ComponentMetadata, ComponentResolver, Input, ReflectiveInjector, ViewContainerRef } from '#angular/core';
declare var $:any;
export function createComponentFactory(resolver: ComponentResolver, metadata: ComponentMetadata): Promise<ComponentFactory<any>> {
const cmpClass = class DynamicComponent {};
const decoratedCmp = Component(metadata)(cmpClass);
return resolver.resolveComponent(decoratedCmp);
}
#Directive({
selector: 'dynamic-html-outlet',
})
export class DynamicHTMLOutlet {
#Input() htmlPath: string;
#Input() cssPath: string;
constructor(private vcRef: ViewContainerRef, private resolver: ComponentResolver) {
}
ngOnChanges() {
if (!this.htmlPath) return;
$('dynamic-html') && $('dynamic-html').remove();
const metadata = new ComponentMetadata({
selector: 'dynamic-html',
templateUrl: this.htmlPath +'.html',
styleUrls: [this.cssPath]
});
createComponentFactory(this.resolver, metadata)
.then(factory => {
const injector = ReflectiveInjector.fromResolvedProviders([], this.vcRef.parentInjector);
this.vcRef.createComponent(factory, 0, injector, []);
});
}
}
Example how to use it:
import { Component, OnInit } from '#angular/core';
import { DynamicHTMLOutlet } from './../../directives/dynamic-html-outlet/dynamicHtmlOutlet.directive';
#Component({
selector: 'lib-home',
templateUrl: './app/content/home/home.component.html',
directives: [DynamicHTMLOutlet]
})
export class HomeComponent implements OnInit{
html: string;
css: string;
constructor() {}
ngOnInit(){
this.html = './app/content/home/home.someTemplate.html';
this.css = './app/content/home/home.component.css';
}
}
home.component.html:
<dynamic-html-outlet [htmlPath]="html" [cssPath]="css"></dynamic-html-outlet>
After reading a lot, and being close of opening a new topic I decided to answer here just to try to help to others. As I've seen there are several changes with the latest version of Angular 2. (Currently Beta9)
I'll try to share my code in order to avoid the same frustration I had...
First, in our index.html
As usual, we should have something like this:
<html>
****
<body>
<my-app>Loading...</my-app>
</body>
</html>
AppComponent (using innerHTML)
With this property you will be able to render the basic HTML, but you won't be able to do something similar to Angular 1.x as $compile through a scope:
import {Component} from 'angular2/core';
#Component({
selector: 'my-app',
template: `
<h1>Hello my Interpolated: {{title}}!</h1>
<h1 [textContent]="'Hello my Property bound: '+title+'!'"></h1>
<div [innerHTML]="htmlExample"></div>
`,
})
export class AppComponent {
public title = 'Angular 2 app';
public htmlExample = ' <div>' +
'<span [textContent]="\'Hello my Property bound: \'+title"></span>' +
'<span>Hello my Interpolated: {{title}}</span>' +
'</div>'
}
This will render the following:
Hello my Interpolated: Angular 2 app!
Hello my Property bound: Angular 2 app!
Hello my Interpolated: {{title}}
AppComponent Using DynamicComponentLoader
There is a little bug with the docs, documented in here. So if we have in mind that, my code should look now like this:
import {DynamicComponentLoader, Injector, Component, ElementRef, OnInit} from "angular2/core";
#Component({
selector: 'child-component',
template: `
<div>
<h2 [textContent]="'Hello my Property bound: '+title"></h2>
<h2>Hello my Interpolated: {{title}}</h2>
</div>
`
})
class ChildComponent {
title = 'ChildComponent title';
}
#Component({
selector: 'my-app',
template: `
<h1>Hello my Interpolated: {{title}}!</h1>
<h1 [textContent]="'Hello my Property bound: '+title+'!'"></h1>
<div #child></div>
<h1>End of parent: {{endTitle}}</h1>
`,
})
export class AppComponent implements OnInit{
public title = 'Angular 2 app';
public endTitle= 'Bye bye!';
constructor(private dynamicComponentLoader:DynamicComponentLoader, private elementRef: ElementRef) {
// dynamicComponentLoader.loadIntoLocation(ChildComponent, elementRef, 'child');
}
ngOnInit():any {
this.dynamicComponentLoader.loadIntoLocation(ChildComponent, this.elementRef, 'child');
}
}
This will render the following:
Hello my Interpolated: Angular 2 app!
Hello my Property bound: Angular 2 app!
Hello my Property bound: ChildComponent title
Hello my Interpolated: ChildComponent title
End of parent: Bye bye!
I think all you have to do is set the element you want to have compiled html with the [innerHTML]="yourcomponentscopevar"
Angular provided DynamicComponentLoader class for loading html dynamically. DynamicComponentLoader have methods for inserting components. loadIntoLocation is one of them for inserting component.
paper.component.ts
import {Component,DynamicComponentLoader,ElementRef,Inject,OnInit} from 'angular2/core';
import { BulletinComponent } from './bulletin.component';
#Component({
selector: 'paper',
templateUrl: 'app/views/paper.html'
}
})
export class PaperComponent {
constructor(private dynamicComponentLoader:DynamicComponentLoader, private elementRef: ElementRef) {
}
ngOnInit(){
this.dynamicComponentLoader.loadIntoLocation(BulletinComponent, this.elementRef,'child');
}
}
bulletin.component.ts
import {Component} from 'angular2/core';
#Component({
selector: 'bulletin',
templateUrl: 'app/views/bulletin.html'
}
})
export class BulletinComponent {}
paper.html
<div>
<div #child></div>
</div>
Few things you need to take care of :
Don't call loadIntoLocation inside the constructor of class . Component view is not yet created when component constructor is called. You will get error -
Error during instantiation of AppComponent!. There is no component
directive at element [object Object]
Put anchorName #child in html otherwise you will get error.
Could not find variable child
Have a look at this module https://www.npmjs.com/package/ngx-dynamic-template
After a long research, only this thing helped me. The rest of the solutions seems to be outdated.