display modal component from other component on Angular 2 - javascript

I'm pretty new to angular, and I have a modal component with a button, on (click) changes the status of the modal from hide to show and it's displayed. however, I want to include this modal on my main component, so I can create a button which displays the modal component over the main component.
this is my modalLink.ts:
import { Component,Input,trigger,state,style,transition,animate } from '#angular/core';
#Component({
templateUrl: 'modalLink.component.html',
styleUrls: ['modalLink.component.css'],
animations:[
trigger('Modal',[
state("show",style({'display':'flex', 'opacity':'1'})),
state("hide",style({'display':'none', 'opacity':'0'})),
transition("show <=> hide", animate( "200ms" ))
])
]
})
export class ModalLink {
private url:string = '';
private modal:string = 'hide';
private objectArea:any = []
private objectLevel:any = []
showModal(){
this.modal = 'show';
}
hideModal(){
this.modal = 'hide';
}
}
and on my mainComponent.ts this is an excerpt of what I have:
import { ModalLink } from './modalLink.component';
#Component({
templateUrl: 'academy.component.html',
styleUrls: ['academy.component.css']
})
export class AcademyComponent {
#ViewChild(ModalLink) modalLink: ModalLink
asdf() {
this.modalLink.showModal();
}
}
and my mainComponent.html which contains the button that calls the asdf function that calls showModal():
<div class="container-organize resource-content">
<button (click)="asdf()" class="btn-floating btn-large btn-new-resource"><i class="ai ai-plus"></i></button>

For your situation, you should consider of taking advantage of Angular-Material.
Angular Material have implement this for you, just use it this way:
import {MdDialog} from '#angular/material';
constructor(public dialog: MdDialog) {}
openDialog() {
this.dialog.open(DialogOverviewExampleDialog); // DialogOverviewExampleDialog is another component
}
here is simple plunker

Related

Reloading multiple child component from parent in Angular 14

I have a problem with reloading my child component from parent component in Angular.
Here is an example of what I want to do.
This is my child component
import { Component } from "#angular/core";
#Component({
selector: "app-child",
template: `<p> {{ticks}} </p>`,
styleUrls: ["./child.component.css"]
})
export class ChildComponent {
ticks = Date.now().valueOf();
constructor() {}
update(): void {
this.ticks = Date.now().valueOf();
}
}
And here is my parent component:
import { Component, OnInit, ViewChild } from '#angular/core';
import { ChildComponent } from './../child/child.component';
#Component({
selector: 'app-parent',
templateUrl: './parent.component.html',
styleUrls: ['./parent.component.css'],
})
export class ParentComponent implements OnInit {
#ViewChild(ChildComponent, { static: false }) childC: ChildComponent;
showChild: boolean = true;
constructor() {}
ngOnInit() {}
onUpdateChild() {
this.childC.update();
}
}
Parent Component HTML :
<p>
parent works!
<button (click)="onUpdateChild()">update</button>
<app-child *ngIf="showChild"></app-child>
<app-child *ngIf="showChild"></app-child>
</p>
The main problem is that if I use my child component multiple time, and trying to click on “update” button, it just updates one of my child component that is used in parent component, but I want to update all same child component in parent component, here is what happens when you click on “update” button, only first value will change, not both.
How can I fix it ?!?
You can use #ViewChildren and QueryList to do what you are looking for.
#ViewChildren(ChildComponent) childrenC!: QueryList<ChildComponent>;
And your function would look like:
onUpdateChild() { //I would rename it to onUpdateChildren
if(this.childrenC) {
this.childrenC.forEach((childC: ChildComponent) => {
this.childC.update();
});
}
}
If you wanted your child component to update on it's own - as stated by a comment to your original post - this is an example of what you could do so you wouldn't even need the update button:
import { Component, OnInit } from "#angular/core";
#Component({
selector: "app-child",
template: `<p> {{ticks}} </p>`,
styleUrls: ["./child.component.css"]
})
export class ChildComponent implements OnInit {
ticks = Date.now().valueOf();
constructor() {}
ngOnInit(): void {
setInterval(() => {
this.ticks = Date.now().valueOf();
}, 1000); //updates every 1 second
}
}
Another option you can do without using a button is to use #Input with the ticks property that updates the value from the parent through an input.

Angular - How can I toggle the visibility of an element in a component from another component?

I have the following scenario in my Angular app:
A component MainDashboardComponent that is visible when I have the route /. Obviously I have the <router-outlet> tag in my app.component.html file, which looks like this:
<app-side-menu></app-side-menu>
<div class="main-container">
<div class="content">
<router-outlet></router-outlet>
</div>
</div>
As you can see I have a SideMenuComponent I use to have a side menu on all my routes. In MainDashboardComponent I have a method that for some reason needs to toggle a chat element that is situated on the side menu.
Inside the SideMenuComponent I have a method that handles the visibility toggle for the chat element and it works as expected. How can I call this method from my MainDashboardComponent and toggle the chat element from there?
What I tried with no success
I tried to inject the SideMenuComponent inside my MainDashboardComponent but, though the method toggleChat() is called, the element doesn't change it's visibility. Looks like I have a kind of multiple instance of the same component I guess...
Can you please help me with this? Thank you!
MainDashboardComponent
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-main-dashboard',
templateUrl: './main-dashboard.component.html',
styleUrls: ['./main-dashboard.component.scss']
})
export class MainDashboardComponent implements OnInit {
constructor() { }
ngOnInit() {}
setFocus(id) {
// here I'd like to call SideMenuComponent togglechat() ...
}
}
SideMenuComponent
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-side-menu',
templateUrl: './side-menu.component.html',
styleUrls: ['./side-menu.component.scss']
})
export class SideMenuComponent implements OnInit {
showChat: boolean;
constructor() {
this.showChat = false;
}
ngOnInit() {
}
toggleChat() {
this.showChat = !this.showChat;
}
}
To communicate between different components, there are different ways.
If you want to communicate between parent and child component, you can use EventEmitter to emit event from child component and handle the event in your parent component
If you want to communicate between any components, you can use Service and implement communication with the help of EventEmitter or Subject/BehaviorSubject
In your case, we can create a service, myService.ts and declare and eventEmitter
.service.ts
#Injectable()
export class AppCommonService {
toggle : EventEmitter<boolean> = new EventEmitter<boolean>()
}
mainDashboard.component.ts
constructor(private myService : myService){}
chatStatus : boolean = false;
ngOnInit(){
this.myService.toggle.subscribe(status=>this.chatStatus = status);
}
toggleChat(){
this.myService.toggle.emit(!this.chatStatus);
}
sideMenu.component.ts
constructor(private myService : myService){}
chatStatus : boolean = false;
ngOnInit(){
this.myService.toggle.subscribe(status=>this.chatStatus = status);
}
Generally this is the domain of a service!
Just create a service and add the "showCat" property.
Inject the service into both components
Alter SideMenuComponent to:
toggleChat() {
this.myService.showChat = !this.myService.showChat;
}
Alter MainDashboardComponent, also use this.myService.showChat to show / hide your chat window
Service TS
#Injectable()
export class MyService{
showCat:boolean = true
}
MainDashboardComponent
toggleChat() {
this.myService.showChat = !this.myService.showChat;
}
SideMenuComponent
chatVisiblity = this.myService.showCat //<-- bind this to the element attribute
You could efficiently use child to parent communication in this scenario. You'll need to create a custom event using angular's EventEmitter in your SideMenuComponent and use it in your MainDashboardComponent.
So, here is some code that may help you -
// SideMenuComponent
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-side-menu',
templateUrl: './side-menu.component.html',
styleUrls: ['./side-menu.component.scss']
})
export class SideMenuComponent implements OnInit {
#Output() valueChange = new EventEmitter();
showChat: boolean;
constructor() {
this.showChat = false;
}
ngOnInit() {
}
toggleChat() {
this.showChat = !this.showChat;
this.valueChange.emit(this.showChat);
}
}
// MainDashboardComponent
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-main-dashboard',
template: `<app-side-menu (valueChange)='setFocus($event)'></app-side-menu>`
styleUrls: ['./main-dashboard.component.scss']
})
export class MainDashboardComponent implements OnInit {
constructor() { }
ngOnInit() { }
setFocus(event) {
// check for required input value
console.log(event);
}
}
Refer these tutorials if required -
https://dzone.com/articles/understanding-output-and-eventemitter-in-angular,
https://angular-2-training-book.rangle.io/handout/components/app_structure/responding_to_component_events.html

How to add an Angular property to an HTML element

I need to know how to add to an html button the property (click) = function() of angular through Javascript.
Note: I cannot modify the HTML, I can only add the property through JavaScript.
I tested with addEventListener and it works by adding the common JavaScript click = "function" event, but not the (click) of Angular.
I attach the code:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-iframe',
templateUrl: './iframe.component.html',
styleUrls: ['./iframe.component.scss']
})
export class IframeComponent implements OnInit {
constructor() {}
ngOnInit() {
}
capture() {
let button = document.getElementById('cancelButton').addEventListener('(click)', this.cancel.bind(Event));
}
cancel() {
console.log('Cancelled');
}
}
And the HTML here:
<div class="row text-center pad-md">
<button id="acceptButton" mat-raised-button color="primary">OK!</button>
<button id="cancelButton" mat-raised-button>Cancel</button>
</div>
As stated by the author, the event need to be attached dynamically to the DOM element that is created after a request, so you can use Renderer2
to listen for the click event. Your code should look like this:
import { Component, OnInit, Renderer2 } from '#angular/core';
#Component({
selector: 'app-iframe',
templateUrl: './iframe.component.html',
styleUrls: ['./iframe.component.scss']
})
export class AppComponent implements OnInit {
name = 'Angular';
constructor(private renderer: Renderer2) {}
ngOnInit() {}
capture() {
const button = document.getElementById('cancelButton');
console.log(button);
this.renderer.listen(button, 'click', this.cancel);
}
cancel() {
console.log('Cancelled');
}
}
There's a functional example here.

Angular 4/5 Observable - How to update component template depending on observable property?

Ok I am still a newbie. I have successfully created a 'dashboard' component that has a left sidebar with links. On the right is where I have content/components displayed that I want to change dynamically depending on what link was clicked on the left sidebar (see bootstrap sample of what this dashboard looks like here, click on the toggle button to view the sidebar: https://blackrockdigital.github.io/startbootstrap-simple-sidebar/).
I have created a DashboardService that has a Subject and an Observable to allow for sibling component communication. This works great since I have a console.log() that shows this communication working (when I click on link on sidebar in SidebarComponent, I console.log() a value 'emitted' by the DashboardService that is being listened to by the SidebarComponent's sibling, DashboardSectionComponent).
The problem that I am having is that the template in DashboardSectionComponent loads the correct component section ONLY on initial load of page - once I click on a link on the side bar the content is blank and nothing is rendered.
Here is the service that allows the componenent communication:
import { Injectable } from '#angular/core';
import { Subject } from 'rxjs/Subject';
import { Observable } from 'rxjs/Observable';
#Injectable()
export class DashboardService {
private selectedComponentAlias = new Subject<string>();
constructor() {}
setSelectedComponentAlias(alias: string) {
this.selectedComponentAlias.next(alias);
}
getSelectedComponentAlias(): Observable<string> {
return this.selectedComponentAlias.asObservable();
}
}
Here is the SidebarComponent:
import { Component, OnInit } from '#angular/core';
import { DashboardService } from '../dashboard.service';
#Component({
selector: 'app-sidebar',
templateUrl: './sidebar.component.html',
styleUrls: ['./sidebar.component.css']
})
export class SidebarComponent implements OnInit {
constructor(private dashboardService: DashboardService) { }
ngOnInit() {
}
onShowSection(event) {
event.preventDefault();
const componentAlias = event.target.getAttribute('data-componentAlias');
this.dashboardService.setSelectedComponentAlias(componentAlias);
}
}
here is the DashboardSectionComponent (the one that subscribes to the observable and I want to set property that controls the template views depending on the value that was caught)
import { Component, OnInit, OnDestroy } from '#angular/core';
import { Subscription } from 'rxjs/Subscription';
import { DashboardService } from '../dashboard.service';
#Component({
selector: 'app-dashboard-section',
templateUrl: './dashboard-section.component.html',
styleUrls: ['./dashboard-section.component.css']
})
export class DashboardSectionComponent implements OnInit, OnDestroy {
private subscrition: Subscription;
selectedComponentAlias: string = 'user-profile';
constructor(private dashboardService: DashboardService) {
}
ngOnInit() {
this.subscrition = this.dashboardService.getSelectedComponentAlias()
.subscribe((selectedComponentAlias: string) => {
this.selectedComponentAlias = selectedComponentAlias;
console.log('user clicked: ',this.selectedComponentAlias);
});
}
ngOnDestroy() {
this.subscrition.unsubscribe();
}
}
Finally here is the template for DashboardSectionComponent which might have wrong syntax:
<div *ngIf="selectedComponentAlias == 'my-cards'">
<app-cards></app-cards>
</div>
<div *ngIf="selectedComponentAlias == 'user-profile'">
<app-user-profile></app-user-profile>
</div>
<div *ngIf="selectedComponentAlias == 'user-settings'">
<app-user-settings></app-user-settings>
</div>
Again, this works great (selectedComponentAlias is 'user-profile' on page load by default). But it goes blank after I click on a Sidebar link....
Thanks.
this was easy - like #RandyCasburn pointed out, this was a matter of getting the routing working properly.

How to rendered dynamically created array component on template html in Angular2?

I have created dynamic component instances by selecting pre-existing components. For example,
#Component({
selector: 'dynamic-component',
template: `<div #container><ng-content></ng-content></div>`
})
export class DynamicComponent {
#ViewChild('container', {read: ViewContainerRef}) container: ViewContainerRef;
public addComponent(ngItem: Type<WidgetComponent>,selectedPlugin:Plugin): WidgetComponent {
let factory = this.compFactoryResolver.resolveComponentFactory(ngItem);
const ref = this.container.createComponent(factory);
const newItem: WidgetComponent = ref.instance;
newItem.pluginId = Math.random() + '';
newItem.plugin = selectedPlugin;
this._elements.push(newItem);
return newItem;
}
}
My pre-existed components are ChartWidget and PatientWidget which extended the class WidgetComponent that I wanted to add in the container. For example,
#Component({
selector: 'chart-widget',
templateUrl: 'chart-widget.component.html',
providers: [{provide: WidgetComponent, useExisting: forwardRef(() => ChartWidget) }]
})
export class ChartWidget extends WidgetComponent implements OnInit {
constructor(ngEl: ElementRef, renderer: Renderer) {
super(ngEl, renderer);
}
ngOnInit() {}
close(){
console.log('close');
}
refresh(){
console.log('refresh');
}
...
}
chart-widget.compoment.html (using primeng Panel)
<p-panel [style]="{'margin-bottom':'20px'}">
<p-header>
<div class="ui-helper-clearfix">
<span class="ui-panel-title" style="font-size:14px;display:inline-block;margin-top:2px">Chart Widget</span>
<div class="ui-toolbar-group-right">
<button pButton type="button" icon="fa-window-minimize" (click)="minimize()"</button>
<button pButton type="button" icon="fa-refresh" (click)="refresh()"></button>
<button pButton type="button" icon="fa-expand" (click)="expand()" ></button>
<button pButton type="button" (click)="close()" icon="fa-window-close"></button>
</div>
</div>
</p-header>
some data
</p-panel>
data-widget.compoment.html (same as chart-widget using primeng Panel)
#Component({
selector: 'data-widget',
templateUrl: 'data-widget.component.html',
providers: [{provide: WidgetComponent, useExisting: forwardRef(() =>DataWidget) }]
})
export class DataWidget extends WidgetComponent implements OnInit {
constructor(ngEl: ElementRef, renderer: Renderer) {
super(ngEl, renderer);
}
ngOnInit() {}
close(){
console.log('close');
}
refresh(){
console.log('refresh');
}
...
}
WidgetComponent.ts
#Component({
selector: 'widget',
template: '<ng-content></ng-content>'
})
export class WidgetComponent{
}
Now I added the components by selecting a component from the existed components (e.g. chart-widget and data-widget) in the following way and stored the instances into an array.
#Component({
templateUrl: 'main.component.html',
entryComponents: [ChartWidget, DataWidget],
})
export class MainComponent implements OnInit {
private elements: Array<WidgetComponent>=[];
private WidgetClasses = {
'ChartWidget': ChartWidget,
'DataWidget': DataWidget
}
#ViewChild(DynamicComponent) dynamicComponent: DynamicComponent;
addComponent(): void{
let ref= this.dynamicComponent.addComponent(this.WidgetClasses[this.selectedComponent], this.selectedComponent);
this.elements.push(ref);
this.dynamicComponent.resetContainer();
}
}
Now, I am facing problem to render the components using innerHtml in main.component.html. It render the html but I am not able to use button click event or other event on it. I have also tried to render chart using primeng but its also not working.
main.component.html
<dynamic-component [hidden]="true" ></dynamic-component>
<widget *ngFor="let item of elements">
<div [innerHTML]="item._ngEl.nativeElement.innerHTML | sanitizeHtml">
</div>
</widget>
I have also implemented a sanitizeHtml Pipe but its giving still same result. So, as I understand innerHTML is only showing the html data but I can't use any button event as well as the js chart. I have also tried to show the items like this {{item}} under tag. But it display like a text [object object]. So, could anyone give a solution for it? How can I render the components allowing the button events and js chart? Thanks.
EDIT: See my Plunker here https://plnkr.co/edit/lugU2pPsSBd3XhPHiUP1?p=preview
You can see here, it is possible to add chart or data widget dynamically and I am showing it using innerHTML. So, the button events are not working here. If I coding like {{item}} then it shows [object object] text. You can also see in console the component array data. The main Question is, How can I active the button events on it (e.g. if i click close or refresh button then it will call the related functions)?
I would create structural directive like:
view.directive.ts
import { ViewRef, Directive, Input, ViewContainerRef } from '#angular/core';
#Directive({
selector: '[view]'
})
export class ViewDirective {
constructor(private vcRef: ViewContainerRef) {}
#Input()
set view(view: ViewRef) {
this.vcRef.clear();
this.vcRef.insert(view);
}
ngOnDestroy() {
this.vcRef.clear()
}
}
then
app.component.ts
private elements: Array<{ view: ViewRef, component: WidgetComponent}> = [];
...
addComponent(widget: string ): void{
let component = this.dynamicComponent.addComponent(this.WidgetClasses[widget]);
let view: ViewRef = this.dynamicComponent.container.detach(0);
this.elements.push({view,component});
this.dynamicComponent.resetContainer();
}
and
app.component.html
<widget *ngFor="let item of elements">
<ng-container *view="item.view"></ng-container>
</widget>
So i have just moved view from dynamic component container to desired place.
Plunker Example

Categories