Angular2 Output/emit() not working - javascript

For the life of me I cannot figure out why I cannot either emit or capture some data. The toggleNavigation() fires, but I'm not sure if the .emit() is actually working.
Eventually I want to collapse and expand the navigation, but for now I just want to understand how to send data from the navigation.component to the app.component.
Here is a Plunker link
app.component
import { Component } from '#angular/core';
import { PasNavigationComponent } from './shared/index';
#Component({
moduleId: module.id,
selector: 'pas-app',
template: `
<pas-navigation
(toggle)="toggleNavigation($event);">
</pas-navigation>
<section id="pas-wrapper">
<pas-dashboard></pas-dashboard>
</section>
`,
directives: [PasNavigationComponent]
})
export class AppComponent {
toggleNavigation(data) {
console.log('event', data);
}
}
pas-navigation.component
import { Component, Output, EventEmitter } from '#angular/core';
#Component({
moduleId: module.id,
selector: 'pas-navigation',
template: `
<nav>
<div class="navigation-header">
<i class="fa fa-bars" (click)="toggleNavigation()"></i>
</div>
</nav>
`
})
export class PasNavigationComponent {
#Output('toggle') navToggle = new EventEmitter();
toggleNavigation() {
this.navToggle.emit('my data to emit');
}
}
EDIT
I added Pankaj Parkar's suggestions.

You just need to specify $event parameter on method, so whatever emitted on navToggle output value, that data will be available inside AppComponent 'stoggleNavigationmethod$event` parameter.
<pas-navigation
(toggle)="toggleNavigation($event);">
</pas-navigation>
AppComponent
toggleNavigation(data) {
// you can get emitted data here by `pas-navigation` component
console.log('event', data);
}
Forked Plunkr

Related

preprocess: ERROR could not parse #Component() metadata path/to/my.component.ts

I have updated my angular project to Angular v11 which successfully updated. This brought a warning that TSLint is deprecated and following the steps outlined Here - Migrating from Codelyzer and TSLint I managed to migrate this.
After this update, I received several errors which I have an idea of how to solve, except this one error
Error: Debug Failure. False expression: position cannot precede the beginning of the file
at computeLineOfPosition (C:\Path\to\Project\node_modules\typescript\lib\typescript.js:8934:22)
at computeLineAndCharacterOfPosition (C:\Path\to\Project\node_modules\typescript\lib\typescript.js:8912:26)
at Object.getLineAndCharacterOfPosition C:\Path\to\Project\node_modules\typescript\lib\typescript.js:8953:16)
at SourceFileObject.getLineAndCharacterOfPosition (C:\Path\to\Project\node_modules\typescript\lib\typescript.js:143309:23)
at preprocess (C:\Path\to\Project\node_modules\#angular-eslint\eslint-plugin-template\dist\index.js:1:1430)
at Linter._verifyWithProcessor (C:\Path\to\Project\node_modules\eslint\lib\linter\linter.js:1292:30)
at Linter._verifyWithConfigArray (C:\Path\to\Project\node_modules\eslint\lib\linter\linter.js:1264:25)
at Linter.verify (C:\Path\to\Project\node_modules\eslint\lib\linter\linter.js:1226:25)
at Linter.verifyAndFix (C:\Path\to\Project\node_modules\eslint\lib\linter\linter.js:1416:29)
at verifyText (C:\Path\to\Project\node_modules\eslint\lib\cli-engine\cli-engine.js:239:48)
preprocess: ERROR could not parse #Component() metadata C:\Path\to\Project\src\app\components\label-star-required\label-star-required.component.ts
Error: Debug Failure. False expression: position cannot precede the beginning of the file
at computeLineOfPosition (C:\Path\to\Project\node_modules\typescript\lib\typescript.js:8934:22)
at computeLineAndCharacterOfPosition (C:\Path\to\Project\node_modules\typescript\lib\typescript.js:8912:26)
at Object.getLineAndCharacterOfPosition (C:\Path\to\Project\node_modules\typescript\lib\typescript.js:8953:16)
at SourceFileObject.getLineAndCharacterOfPosition (C:\Path\to\Project\node_modules\typescript\lib\typescript.js:143309:23)
at preprocess (C:\Path\to\Project\node_modules\#angular-eslint\eslint-plugin-template\dist\index.js:1:1430)
at Linter._verifyWithProcessor (C:\Path\to\Project\node_modules\eslint\lib\linter\linter.js:1292:30)
at Linter._verifyWithConfigArray (C:\Path\to\Project\node_modules\eslint\lib\linter\linter.js:1264:25)
at Linter.verify (C:\Path\to\Project\node_modules\eslint\lib\linter\linter.js:1226:25)
at Linter.verifyAndFix (C:\Path\to\Project\node_modules\eslint\lib\linter\linter.js:1416:29)
at verifyText (C:\Path\to\Project\node_modules\eslint\lib\cli-engine\cli-engine.js:239:48)
preprocess: ERROR could not parse #Component() metadata C:\Path\to\Project\src\app\components\skip-link\skip-link.component.ts
Below are the two components throwing this error
label-star-required.component.ts
import {Component} from '#angular/core';
#Component({
selector: 'app-label-star-required',
templateUrl: './label-star-required.component.html',
styleUrls: ['./label-star-required.component.css']
})
export class LabelStarRequiredComponent {
constructor() {
}
}
#Component({
selector: 'app-star-required',
template: `
<span class='icon-star required'></span>
`,
styleUrls: ['./label-star-required.component.css']
})
export class StarRequiredComponent {
constructor() {
}
}
skip-link.component.ts
import { Component, OnInit, OnDestroy } from '#angular/core';
import { Router, NavigationStart } from '#angular/router';
import { filter, takeWhile, map } from 'rxjs/operators';
#Component({
selector: 'app-skip-link',
styleUrls: ['./skip-link.component.css'],
template: `
<a [href]="skipLinkPath" class="skip-main">Skip to main content</a>
`
})
export class SkipLinkComponent implements OnInit, OnDestroy {
skipLinkPath: string;
componentIsActive: boolean;
constructor(
private router: Router
) { }
ngOnInit() {
this.componentIsActive = true;
this.skipLinkPath = `${this.router.url.replace('#main', '')}#main`;
this.router.events.pipe(filter(event => event instanceof NavigationStart))
.pipe(takeWhile(() => this.componentIsActive))
.pipe(map(event => (event as any).url ))
.subscribe(url => {
if (!/(.)#main$/.test(url)) {
this.skipLinkPath = `${url}#main`;
}
});
}
ngOnDestroy() {
this.componentIsActive = false;
}
}
From the above two components, I can see that the common similarity is that both the files are using
inline template: . Might this be causing the error while running ng lint ? How do I solve this?
My workaround was to move the html code in the component template to templateUrl.
#Component({
selector: 'app-star-required',
template: `<span class='icon-star required'></span>`,
styleUrls: ['./label-star-required.component.css']
})
Becomes
#Component({
selector: 'app-star-required',
templateUrl: './label-star-required.component.html',
styleUrls: ['./label-star-required.component.css']
})
Seems to be a bug with ESlint/Angular configuration.
As a workaround, i added "endOfLine": "lf" to my prettier config which fixed the issue after reformatting the project.
See https://github.com/angular-eslint/angular-eslint/issues/185
I noticed this error too with templates that had escaped double quotes in them:
template: "<div class=\"my-class\"></div><ng-content></ng-content>"
I solved it by switching to single quotes:
template: "<div class='my-class'></div><ng-content></ng-content>"

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.

Angular 2 dynamically loading tabs when header click

I have developed a custom tab component using Angular 2 which is working fine.
This is the usage of it.
<us-tab-verticle>
<vtab-content [tabTitle]="'Basic Information'"><basic-info> </basic-info></vtab-content>
<vtab-content [tabTitle]="'VAT Settings'"><vat-settings> </vat-settings></vtab-content>
<vtab-content [tabTitle]= "'Economy Settings'"><economy-settings> </economy-settings></vtab-content>
<vtab-content [tabTitle]="'Access Profiles'"><access-profiles> </access-profiles></vtab-content>
</us-tab-verticle>
The problem is all the tab components are loading when the view load.
My tab implementation is as follows.
us-tab-verticle.component.html
<div class="v-tabs">
<div class="v-tabs-col v-tabs-left">
<ul class="nav nav-v-tabs flex-column" role="tablist">
<li class="nav-v-item" *ngFor="let tab of tabs" (click)="selectTab(tab)">
<a [class.active]="tab.active" class="nav-v-link" data-toggle="tab" href="#" role="tab">{{tab.title}}</a>
</li>
</ul>
</div>
<div class="v-tabs-col v-tabs-fill">
<div class="v-tabs-content">
<div>
<ng-content></ng-content>
</div>
</div>
</div>
us-tab-verticle.component.ts
import { Component, ContentChildren, QueryList, AfterContentInit } from '#angular/core';
import { VtabContentComponent } from './vtab-content/vtab-content.component';
#Component({
selector: 'us-tab-verticle',
templateUrl: './us-tab-verticle.component.html',
styleUrls: ['./us-tab-verticle.component.scss']
})
export class UsTabVerticleComponent implements AfterContentInit {
#ContentChildren(VtabContentComponent) tabs: QueryList<VtabContentComponent>;
// contentChildren are set
ngAfterContentInit() {
// get all active tabs
const activeTabs = this.tabs.filter((tab) => tab.active);
// if there is no active tab set, activate the first
if (activeTabs.length === 0) {
this.selectTab(this.tabs.first);
}
}
selectTab(tab: VtabContentComponent) {
// deactivate all tabs
this.tabs.toArray().forEach(tab => tab.active = false);
// activate the tab the user has clicked on.
tab.active = true;
}
}
vtab-content.component.html
<div class="tab-content v-tab-content align-content-stretch">
<div [hidden]="!active" class="tab-pane active" role="tabpanel">
<ng-content></ng-content>
</div>
</div>
vtab-content.component.ts
import { Component, Input } from '#angular/core';
import { NgComponentOutlet } from '#angular/common';
#Component({
selector: 'vtab-content',
templateUrl: './vtab-content.component.html',
styleUrls: ['./vtab-content.component.scss']
})
export class VtabContentComponent {
#Input('tabTitle') title: string;
#Input() active = false;
}
I need to load each component when I click the header of the each tabs.
I sow that NgComponentOutlet can use to this kind of situations. But could not get any idea how to implement.
There are many solutions, if you want to improve your solution i suggest the usage of *ngIf. But notice that you destroy and create a new Component every time the *ngIf state changes.
I suggest you to take a look at RouterModule with the use of children attribue.
A little sample : (not sure you can make it work right away but I use similar stuff in my app)
// Router.ts
import { NgModule } from '#angular/core';
import { Routes, RouterModule } from '#angular/router';
const routes: Routes = [
{
path: '',
children: [
{ path: 'tab1', component: Tab1Component },
{ path: 'tab2', component: Tab2Component },
]
},
{ path: '**', redirectTo: '' } // modify for default tab?
];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
export const routedComponents = [Tab1Component, Tab2Component];
// AppModule.ts
imports: [AppRoutingModule]
declarations: [
routedComponents
],
// main.html
<app-header>
<us-tab-verticle>
<div class="tab" [routerLink]="['/tab1']"><a>
<div class="tab" [routerLink]="['/tab2']"><a>
<router-outlet></router-outlet> // the content of your tab components will be displayed below
<app-footer>
This way in my opinion make your app easier to read (declarative routing) instead of having an external service and component for managing the states of your tabs.
I try to reproduce your structure with the usage of the ngComponentOutlet directive. Here is the tab-container:
#Component({
selector: 'tab',
template: ''
})
export class TabComponent {
#Input() title: string;
#Input() contentRef: any;
active = false;
}
This is a very simple component which knows its own tab name, an active state and the body component reference which should be loaded when somebody selects the tab.
Then we create several body components which will be loaded dynamically:
#Component({
selector: 'tab-content',
template: `<p>Hey</p>`
})
export class TabContentComponent {}
#Component({
selector: 'tab-content-alternative',
template: `<p>Hey, this is an alternative content</p>`
})
export class TabContentAlternativeComponent {}
Here is the tabs-container component with tabs rendering and an empty placeholder for dynamic body components:
#Component({
selector: 'tab-container',
template: `
<div class="tab-header">
<div class="tab" *ngFor="let tab of tabs" (click)="selectTab(tab)" [class.active]="tab.active">{{tab.title}}</div>
</div>
<div class="tab-content">
<ng-container *ngComponentOutlet="content | async"></ng-container>
</div>
`,
})
export class TabContainerComponent implements AfterContentInit {
#ContentChildren(TabComponent) tabs: QueryList<TabComponent>;
private contentSbj = new BehaviorSubject<BasicContent>(null);
content = this.contentSbj.asObservable();
ngAfterContentInit() {
const activeTabs = this.tabs.filter((tab) => tab.active);
if (activeTabs.length === 0) {
this.selectTab(this.tabs.first);
}
}
selectTab(tab: TabComponent) {
this.tabs.toArray().forEach(tab => tab.active = false);
tab.active = true;
this.contentSbj.next(tab.contentRef);
}
}
And this is how it can be used in some parent component:
import {TabContentComponent} from './tab/tab-content.component'
import {TabContentAlternativeComponent} from './tab/tab-content-alternative.component'
...
#Component({
selector: 'my-app',
template: `
<tab-container>
<tab title="Tab 1" [contentRef]="normalContent"></tab>
<tab title="Tab 2" [contentRef]="alternativeContent"></tab>
</tab-container>
`,
})
export class App {
normalContent = TabContentComponent;
alternativeContent = TabContentAlternativeComponent;
}
Here is working Plunkr

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>

display modal component from other component on Angular 2

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

Categories