Call a Component method in Directive Angular 2 - angular2-directives

I have a directive which is supposed to call a method from component, but it fails doing this, what should be wrong ?
Here I will put the Directive, and a fragment of Component for understanding the problem..
Directive
import { Directive, EventEmitter, HostListener, Input, OnInit, Output } from '#angular/core';
import { GridComponent } from '../components/grid/grid.component';
import { Cell } from '../cell';
import { KEY_CODE } from '../keyCode.enum';
#Directive({
selector: '[appControl]',
})
export class GridDirective {
constructor(public gridComponent: GridComponent) {}
#HostListener('window:keydown', ['$event'])
handleKeyDown(event: KeyboardEvent) {
console.log(event.key);
const ITEMS = JSON.parse(localStorage.getItem('Grid'));
let key;
switch (event.key) {
case 'ArrowLeft': key = KEY_CODE.LEFT_ARROW;
break;
case 'ArrowUp': key = KEY_CODE.UP_ARROW;
break;
case 'ArrowRight': key = KEY_CODE.RIGHT_ARROW;
break;
case 'ArrowDown': key = KEY_CODE.DOWN_ARROW;
break;
}
this.gridComponent.move(ITEMS, key);
}
}
And here's the component method which it is supposed to call
move(array: Cell[][], key: KEY_CODE) {
localStorage.setItem('lastMove', JSON.stringify(key));
const DATA = this.gridService.move(array, this.score, key);
array = DATA.dataSheet;
this.score = DATA.rating;
this.best = this.gridService.scoreSender(this.score, this.best);
localStorage.setItem('Grid', JSON.stringify(array));
}

it's a wrong way to use a component as a service,
you should pass the "this" value from html and then assign it to gridComponent variable and for passing parameter to directive you can use input decorator
gridComponent :GridComponent;
#Input('appControl') set setGridComponent(gridComponent) {
this.gridComponent = gridComponent;
}
/// in html use property binding to pass the value to it
[appControl]="this"

Related

Angular 7 application, I want to do an if() condition that checks the Input() value passed from parent

I am passing data from parent to child. In the HTML, i can see the value of the Input() variable. However, on my TS file, when I try to do a conditional to check the value of Input() it is always an empty string. Here is my code for the child:
#Input() checkDbStatus = '';
ngOnInit() {
this.initForm();
this.dbStatusCheck();
}
// disables all controls in a form group
disableControl(group: FormGroup){
Object.keys(group.controls).forEach((key: string) => {
const abstractControl = group.get(key);
abstractControl.disable();
})
}
// disable form controls if dbStatus !== update
dbStatusCheck() {
if(this.checkDbStatus !== 'update') {
this.disableControl(this.demographicsSectionOne);
this.disableControl(this.demographicsSectionTwo);
this.disableControl(this.demographicsSectionThree);
this.disableControl(this.demographicsSectionFour);
this.disableControl(this.demographicsSectionFive);
}
}
I think you need to use the ngChange lifecycle.
https://angular.io/api/core/OnChanges
export class YourComponent implements OnChanges
ngOnChanges(changes: SimpleChanges) {
if (changes.checkDbStatus.currentValue !== changes.checkDbStatus.previousValue) {
this.doStatusCheck();
}
}
Try set and get input() function
https://angular.io/guide/component-interaction
_checkDbStatus: any;
#Input() set checkDbStatus(data: any) {
this._checkDbStatus = data;
this.dbStatusCheck(data)
}
get checkDbStatus(){return this._checkDbStatus }

Angular 5 - Event emitter (Property 'update' does not exist on type ....)

I've got a component that I want to update when a person's name changes by emitting an event. My problem is the code doesn't compile because of an error. This is my code
ApplicationFormComponent
#Output() nameChange = new EventEmitter();
closeAccordion(isComplete: string, accordionToClose: string, accordion: NgbAccordion) {
if (accordionToClose === 'personal-details-panel') {
this.applicationStatusFlags.personalDetailsStatus = (isComplete === 'true');
this.nameChange.emit({ personId: this.personId });
}
}
ApplicationFormComponent.html
<name-display
[personId]="personId"
[placeHolderText]="'Hello'"
(nameChange)="update($event)">
</name-display>
NameDisplayComponent
import { Component, Input, OnChanges, SimpleChanges } from '#angular/core';
import { PersonService } from "../../../service/person.service";
#Component({
selector: 'name-display',
templateUrl: './NameDisplay.component.html',
providers: [PersonService]
})
export class NameDisplayComponent implements OnChanges {
constructor(private readonly personService: PersonService) { }
#Input() personId;
#Input() placeHolderText: string = "";
forename: string = "";
ngOnChanges(changes: SimpleChanges): void {
if (changes["personId"]) {
this.personService.getPersonDetails(this.personId).subscribe((res: IPersonDetails) => {
this.forename = res.forenames;
});
}
};
update(personId: number) {
alert("update name");
this.personService.getPersonDetails(personId).subscribe((res: IPersonDetails) => {
this.forename = res.forenames;
});
}
}
My problem is basically when I use angular cli with the command ng server --aot, it doesn't compile because of this error:
ERROR in src\app\component\ApplicationForm\ApplicationForm.component.html(42,9): : Property 'update' does not exist on type 'ApplicationFormComponent'.
I've written a similar component that uses an event emitter which doesn't have this problem, so I'm stuck with how to fix the error.
Any ideas?
It is because you are passing $event to method.
(nameChange)="update($event)"
But it accepts number.
update(personId: number) {
alert("update name");
}
Please change the method as below.
update(event:any) {
const personId = event as number
alert("update name");
}

How to use pathlocationstrategy in angular 5 to set base href or APP_BASE_HREF?

Below is the code snippet:
import {
Router
} from "#angular/router";
import {
HttpClient
} from "#angular/common/http";
import {
environment
} from "../../environments/environment";
import {
Location,
LocationStrategy,
PathLocationStrategy
} from '#angular/common';
#Injectable()
export class CommonServicesService {
PathLocation: Location;
referralCode: any = localStorage.getItem('referenceCode');
constructor(
location: Location,
) {
this.PathLocation = location;
}
redirectAfterSuccessfulLogin() {
if (localStorage.getItem("redirectUrl")) {
let url = localStorage.getItem("redirectUrl");
localStorage.removeItem("redirectUrl");
this.PathLocation.prepareExternalUrl("'/'+this.referralCode"); //is this the correct way?
console.log(this.PathLocation);
this.router.navigate([url]);
} else {
this.PathLocation.prepareExternalUrl("'/'+this.referralCode");
console.log(this.PathLocation);
this.router.navigate(["/"]);
}
}
}
you can do some thing like that :
Create a baseUrl function that takes code as a parameter.
Call that function according to the condition after login,this will
gives you the updated URL
like this:
getBaseUrl = function(code){
return `localhost:3000/${code}`
}
now you can use it as :
getBaseURl(1234)
it returns : "localhost:3000/1234"
now you can add further path after this URL.
OR
you can further use same function for both the URL like this :
getBaseUrl = function(code){
if(code == 0000) return localhost:3000
else return `localhost:3000/${code}`
}
now whenever you are calling the function you have to pass 0000 for non-login condition Base Url and 4 digit code to get the after login base URL
getBaseURl(1234) // for login one
getBaseURl(0000) // for non-login one

ngx-chart error "TypeError: Object(...) is not a function"

I am trying to implements some statistics in my develepping platform and I try to use ngx-charts to display them. However I get an error and I can't figure out why.
I am using storedProcedures for MySQL statistics which I call from Java Restful Backend and return them in Angular 5 front-end. The returned table has the following two fields: Date and number of incidents per day. So the table returned by the backend has those two columns.
My code for the component rendering the chart is the following:
import {Component, OnInit} from '#angular/core';
import {StatisticsService} from '../../statistics.service';
class Data {
private _name: string;
private _value: number;
get name(): string {
return this._name;
}
set name(value: string) {
this._name = value;
}
get value(): number {
return this._value;
}
set value(value: number) {
this._value = value;
}
}
#Component({
selector: 'app-daily-incidents-statistics',
templateUrl: './daily-incidents-statistics.component.html',
styleUrls: ['./daily-incidents-statistics.component.css']
})
export class DailyIncidentsStatisticsComponent implements OnInit {
view: any[] = [700, 400];
data: any[] = [];
// options
showXAxis = true;
showYAxis = true;
gradient = false;
showLegend = false;
showXAxisLabel = true;
xAxisLabel = 'Ημέρα';
showYAxisLabel = true;
yAxisLabel = 'Αρ. Περιστατικών';
constructor(private statisticsService: StatisticsService) {
// Object.assign(this, { single })
// Object.assign(this, { data } );
}
colorScheme = {
domain: ['#5AA454', '#A10A28', '#C7B42C', '#AAAAAA']
};
onSelect(event) {
console.log(event);
}
async ngOnInit() {
console.log('NG ON INIT EXECUTION');
await this.getIncidentsByDay();
}
getIncidentsByDay() {
this.statisticsService.getIncidentsByDay()
.subscribe(
(results) => {
let temp = new Data();
for (let i in results) {
console.log(results[i][0] + '>>=====>> ' + results[i][1]);
temp.name = results[i][0];
temp.value = results[i][1];
this.data.push(temp);
}
const test = this.data;
// for (let i = 0; i < this.data.length; i++) {
// console.log('wtf: ' + this.data[i][0] + '::::' + this.data[i][1]);
// }
// console.log(results);
// console.log(JSON.stringify(results));
// Object.assign(this, {test});
}
);
}
}
However when I run the above code I get in JavaScript console the error:
ERROR TypeError: Object(...) is not a function
at BarVerticalComponent../src/common/base-chart.component.ts.BaseChartComponent.bindWindowResizeEvent (index.js:7818)
at BarVerticalComponent../src/common/base-chart.component.ts.BaseChartComponent.ngAfterViewInit (index.js:7730)
at callProviderLifecycles (core.js:12689)
at callElementProvidersLifecycles (core.js:12656)
at callLifecycleHooksChildrenFirst (core.js:12639)
at checkAndUpdateView (core.js:13794)
at callViewAction (core.js:14136)
at execComponentViewsAction (core.js:14068)
at checkAndUpdateView (core.js:13791)
at callViewAction (core.js:14136)
My Html Template File:
<div>
lalalal <br/>
ante pali... <br/>
kala ti na pw... <br/>
Gamiete pali... <br/>
<ngx-charts-bar-vertical
[view]="view"
[scheme]="colorScheme"
[results]="data"
[gradient]="gradient"
[xAxis]="showXAxis"
[yAxis]="showYAxis"
[legend]="showLegend"
[showXAxisLabel]="showXAxisLabel"
[showYAxisLabel]="showYAxisLabel"
[xAxisLabel]="xAxisLabel"
[yAxisLabel]="yAxisLabel"
(select)="onSelect($event)">
</ngx-charts-bar-vertical>
</div>
While the service for retreiving the values is:
import {Injectable} from '#angular/core';
import {HttpClient} from '#angular/common/http';
import {catchError} from 'rxjs/operators';
import {ErrorHandler} from '../shared/lib/error-handler';
import {Observable} from 'rxjs/Observable';
#Injectable()
export class StatisticsService {
constructor(private http: HttpClient) {
}
public getIncidentsByDay(): Observable<any> {
console.log("FEtching Incidents All By Day");
const url = 'statistics/incidents/day';
return this.http.get(url)
.pipe(catchError(ErrorHandler.handleError));
}
}
What am I doing wrong?
I am using Angular version 5.3 and ngx-charts 8.0 which is compatible with Angular 6 and not Angular 5. I installed ngx-charts version 7.4 and everything works fine.
I fixed the problem for me by downgrading to version 7.3.0
yarn add #swimlane/ngx-charts#7.3.0
I think I see the same with ngx-charts-bar-horizontal, whereas before this was not the case. The documentation page seems to be broken at the moment as well, so I assume the software has recently been updated in a broken way.
If you really need to use the 8.0 version, you can upgrade to angular 6 to solve the problem. Here is how you can do the upgrade from v5 to v6 https://stackoverflow.com/a/49474334
You can also think that the documention page is broken by now but ou can find it here https://swimlane.gitbook.io/ngx-charts/v/docs-test/installing

Access an element's Binding

I have a custom attribute that processes authentication data and does some fun stuff based on the instructions.
<div auth="disabled: abc; show: xyz; highlight: 123">
There's a lot of complicated, delicate stuff happening in here and it makes sense to keep it separate from semantic bindings like disabled.bind. However, some elements will have application-logic level bindings as well.
<div auth="disabled.bind: canEdit" disabled.bind="!editing">
Under the covers, my auth attribute looks at the logged in user, determines if the user has the correct permissions, and takes the correct action based on the result.
disabledChanged(value) {
const isDisabled = this.checkPermissions(value);
if (isDisabled) {
this.element.disabled = true;
}
}
This result needs to override other bindings, which may or may not exist. Ideally, I'd like to look for an existing Binding and override it ala binding behaviors.
constructor(element) {
const bindings = this.getBindings(element); // What is the getBindings() function?
const method = bindings['disabled']
if (method) {
bindings['disabled'] = () => this.checkPermission(this.value) && method();
}
}
The question is what is this getBindings(element) function? How can I access arbitrary bindings on an element?
Edit: Gist here: https://gist.run/?id=4f2879410506c7da3b9354af3bcf2fa1
The disabled attribute is just an element attribute, so you can simply use the built in APIs to do this. Check out a runnable example here: https://gist.run/?id=b7fef34ea5871dcf1a23bae4afaa9dde
Using setAttribute and removeAttribute (since the disabled attribute does not really have a value, its mere existence causes the element to be disabled), is all that needs to happen:
import {inject} from 'aurelia-framework';
#inject(Element)
export class AuthCustomAttribute {
constructor(element) {
this.el = element;
}
attached() {
let val = false;
setInterval(() => {
if(this.val) {
this.el.setAttribute('disabled', 'disabled');
} else {
this.el.removeAttribute('disabled');
}
this.val = !this.val;
}, 1000);
}
}
NEW RESPONSE BELOW
You need to work directly with the binding engine. A runnable gist is located here: https://gist.run/?id=b7fef34ea5871dcf1a23bae4afaa9dde
Basically, you need to get the original binding expression, cache it, and then replace it (if auth === false) with a binding expression of true. Then you need to unbind and rebind the binding expression:
import {inject} from 'aurelia-framework';
import {Parser} from 'aurelia-binding';
#inject(Element, Parser)
export class AuthCustomAttribute {
constructor(element, parser) {
this.el = element;
this.parser = parser;
}
created(owningView) {
this.disabledBinding = owningView.bindings.find( b => b.target === this.el && b.targetProperty === 'disabled');
if( this.disabledBinding ) {
this.disabledBinding.originalSourceExpression = this.disabledBinding.sourceExpression;
// this expression will always evaluate to true
this.expression = this.parser.parse('true');
}
}
bind() {
// for some reason if I don't do this, then valueChanged is getting called before created
this.valueChanged();
}
unbind() {
if(this.disabledBinding) {
this.disabledBinding.sourceExpression = this.disabledBinding.originalSourceExpression;
this.disabledBinding.originalSourceExpression = null;
this.rebind();
this.disabledBinding = null;
}
}
valueChanged() {
if(this.disabledBinding ) {
if( this.value === true ) {
this.disabledBinding.sourceExpression = this.disabledBinding.originalSourceExpression;
} else {
this.disabledBinding.sourceExpression = this.expression;
}
this.rebind();
} else {
if( this.value === true ) {
this.el.removeAttribute('disabled');
} else {
this.el.setAttribute('disabled', 'disabled');
}
}
}
rebind() {
const source = this.disabledBinding.source;
this.disabledBinding.unbind();
this.disabledBinding.bind(source);
}
}
It is important that the attribute clean up after itself, as I do in the unbind callback. I'll be honest that I'm not sure that the call to rebind is actually necessary in the unbind, but it's there for completeness.