I found when I use the service within forEach,the parameter I send to the service beacome the last one of the circulation.
I thought it should be the closure cause this problem,so I try to use the anonymous function to solve it,but didn't work
Pass the obj in Component
this function is triggered in ngOnInit may cause the problem
when I put this code into a click event,it works fine
for (var y = 0; y < this.model.data.length;y++) {
if (this.model.data[i].to[x].id === this.model.data[y].id) {
let obj = {
selectedItem : this.model.data[i],
item : this.model.data[y]
};
(function(_obj,a) {
console.log ('obje in component:');
console.log (_obj.item.id) //each obj over here is correct now
a._drawLineService.drawLine(_obj);
a._dragLineService.dragLine(_obj.item);
})(obj,this)
}
}
Get the obj in directive
this.subscription = this._drawLineService.drawLine$
.subscribe(obj => {
console.log ('drawLine:')
console.log (obj.item.id) //each obj over here become the last one of array
});
I use the observable to pass the event and parameter from component to directive
My service
import {Injectable} from '#angular/core'
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
#Injectable()
export class DrawLineService {
private _drawLine = new BehaviorSubject<any>('');
drawLine$ = this._drawLine.asObservable();
drawLine(item) {
this._drawLine.next(item);
}
}
Console result:
How to solve this?
change var obj=... to let obj=...
let define variable to block scope while var define variable to function or global scope.
You are actually keep overwriting the obj variable with your loop.
Related
I wanted to store the return value of an function into an variable. I tried this simple example.
drawChart(){
var data = this.prepareData();
}
prepareData() {
return 1;
}
But I got the error message:
Cannot read properties of undefined (reading 'prepareData')
Thank you for every help.
You have to call drawChart() method in ngOnInit().
So this method gets call and you can get the return value from the prepareData() method
Here I'm attaching stackblitz url for your ref : stackblitz code
Hope that solve your problem.
You need to place drawChart() and prepareData() outside the ngInit(), constructor() and inside the component.
eg :
export class YourComponent implements OnInit {
ngOnInit() {
this.drawChart();
}
drawChart(){
var data = this.prepareData();
}
prepareData() {
return 1;
}
}
"this" keyword will point to the variables and functions inside the class, If you have declare these function inside a function, that won't work.
I have a segment of code where I am getting some weird output. The parameter being used in the function is changing when I would not think it would.
entry point to the code.
handleAction(action : HAction){
this.openForm("marksForm","Form");
}
method to open the form.
public openForm(name : string, type : string){
console.log("Name",name)
let cb = this.createComponentInitCallback(this.compService.getComponentType(type),
name);
let itemconfig ={
type: 'row',
content: [{
type: 'component',
title: 'Form Test',
componentName: 'h-form',
componentState: {}
}]
}
let tryRegister = false;
try{
this.goldenLayout.getComponent(name);
}catch(e){console.log("registering component",name); tryRegister=true;}
if(tryRegister)
this.goldenLayout.registerComponent(name,cb);
if(this.goldenLayout.root.contentItems[0])
this.goldenLayout.root.contentItems[ 0 ].addChild(itemconfig);
else
this.goldenLayout.root.addChild(itemconfig);
}
This method creates the defined callback function.
public createComponentInitCallback(componentType: Type<any>, name : string ): ComponentInitCallback {
console.log("1Name",name);
let f = (container: GoldenLayout.Container, componentState: any) => {
console.log("2Name",name);
this.ngZone.run(() => {
console.log("3Name",name);
// Create an instance of the angular component.
const factory = this.componentFactoryResolver.resolveComponentFactory(componentType);
const injector = this._createComponentInjector(container, componentState);
const componentRef = this.viewContainer.createComponent(factory, undefined, injector);
console.log("4Name",name)
componentRef.instance.name=name;
// Bind the new component to container's client DOM element.
container.getElement().append($(componentRef.location.nativeElement));
this._bindEventHooks(container, componentRef.instance);
// Store a ref to the compoenentRef in the container to support destruction later on.
(container as any)[COMPONENT_REF_KEY] = componentRef;
});
};
return f;
}
You will see my log statements. This callback gets executed inside the GoldenLayout library. However, I was pretty sure this should work.
Below are the outputs:
Name marksForm
1Name marksForm
2Name h-form
3Name h-form
4Name h-form
The first console output is logging what is passed into this method. You can see that it is obviously changing on me so I have to be doing something wrong. Oddly enough, the componentType parameter is working perfectly fine.
What am I doing wrong here?
A function that relies on lexical this and is supposed to be passed as callback should always be bound to the context.
createComponentInitCallback method can be bound to the context, either with bind or an arrow function (see this explanation on bound prototype methods vs arrow instance methods):
constructor() {
this.createComponentInitCallback = this.createComponentInitCallback.bind(this);
}
Or resulting callback can be bound to the context:
let cb = this.createComponentInitCallback(this.compService.getComponentType(type),
name).bind(this);
Considering there are no scenarios where this should differ from current class instance, the first option is preferable.
As for function scope, it cannot be lost under no circumstances. If name was passed as an argument in parent function, it will remain unchanged in nested function.
Unless you are using Angular HttpModule to make calls, any async call made with an external library will result in running your call back out of the original scope.
To mitigate this you need to assign this to a local variable the callback can use.
public createComponentInitCallback(componentType: Type<any>, name : string ): ComponentInitCallback {
console.log("1Name",name);
let self = this;
let f = (container: GoldenLayout.Container, componentState: any) => {
console.log("2Name",name);
this.ngZone.run(() => {
console.log("3Name",name);
// Create an instance of the angular component.
const factory = self.componentFactoryResolver.resolveComponentFactory(componentType);
const injector = self._createComponentInjector(container, componentState);
const componentRef = self.viewContainer.createComponent(factory, undefined, injector);
console.log("4Name",name)
componentRef.instance.name=name;
// Bind the new component to container's client DOM element.
container.getElement().append($(componentRef.location.nativeElement));
self._bindEventHooks(container, componentRef.instance);
// Store a ref to the compoenentRef in the container to support destruction later on.
(container as any)[COMPONENT_REF_KEY] = componentRef;
});
};
return f;
}
I have a model object representing "players" in DB. in it's implementation there is an array of players, which i would like to bind to from different VM's in my app. for example:
import {Players} from './models/players';
import {inject, BindingEngine} from 'aurelia-framework';
#inject(Players,BindingEngine)
export class App {
constructor(playersProvider,bindingEngine) {
this._playersProvider = playersProvider;
this._bindingEngine = bindingEngine;
this._subscription = this._bindingEngine.propertyObserver(this,this._playersCount)
.subscribe(this.objectValueChanged);
}
async activate() {
await this._playersProvider.initialize();
this._playersCount = this._playersProvider.players.length;
}
objectValueChanged(newVal,oldVal) {
console.log("new : " + newVal + ", old val : " + oldVal);
}
deactivate() {
this._subscription.dispose();
}
}
unfortunately, when a change is made to the players array (from other parts in the app) the change is not reflected in _playersCount property. e.g. - UI label bound to this property is not refreshed, and objectValueChanged never gets called.
U have the same issue in a different VM with a collectionObserver on the same array.
any help?
Have you tried to declare _playersCount in the constructor before subscribing to it?
Also the synthax does not seem correct, it should be according to this article:
import {BindingEngine, inject} from 'aurelia-framework';
#inject(BindingEngine)
class MyClass {
constructor(bindingEngine) {
this.bindingEngine = bindingEngine;
this.observeMe = 'myvalue'; // the property is first initialized in the constructor
let subscription = this.bindingEngine
.propertyObserver(this, 'observeMe') // <= you wrote this._bindingEngine.propertyObserver(this,this.observeMe)
.subscribe(this.objectValueChanged);
// Dispose of observer when you are done via: subscription.dispose();
}
objectValueChanged(newValue, oldValue) {
console.log(`observeMe value changed from: ${oldValue} to:${newValue}`);
}
}
The async keyword might affect the behaviour.
If it still does not work, you can use an event aggregator to broadcast the change.
Im using Angular 1.5.3 with typescript.
I have a outer and a inner component where the outercomponents holds an array named arrayValue which I pass to the innercomponent via <binding:
class InnerComponent {
controller: Function = InnerController;
bindings: any = {
arrayValue : "<"
};
...
}
The InnerController uses the $onChanges method to track any changes from one-way bindings (e. g. arrayValue):
public $onChanges(changes: any){
this.onChangesCalledCounter++;
console.log(changes);
}
If I now change the arrayValue within the outer component using:
public switchArrayValue(): void {
if(this.arrayValue === this.fruits){
this.arrayValue = this.vegetables;
} else {
this.arrayValue = this.fruits;
}
}
The $onChanges within the innercomponent gets called. However, If I change the switchArrayValue method to perform a push instead of a reassignment of the array, the $onChanges method won't get called:
public switchArrayValue(): void {
this.arrayValue.push("hello, world!");
}
Can anyone tell me why the push don't trigger the $onChanges and maybe show a workaround for that?
Here is a plnkr (that I forked).
Hi guys I am writing some code using the object literal pattern, I have function that returns a value:
'currentLocation': function() {
var cL = 0;
return cL;
},
I then need to update the variable 'cL' from another function like this:
teamStatus.currentLocation() = teamStatus.currentLocation() + teamStatus.scrollDistance();
This part is part of another function - however I get an error back stating: invalid assignment left-hand side
I am guessing I can not update the variable in this way, could anyone suggest a better method or point me in the right direction.
Any help would be greatly appreciated.
Going to add more code to highlight what I am trying to do:
'currentLocation': function() {
var cL = 0;
return cL;
},
'increaseTable': function() {
if (teamStatus.currentLocation() <= teamStatus.teamStatusTableHeight() ) {
teamStatus.currentLocation = teamStatus.currentLocation() + teamStatus.scrollDistance();
$("#tableTrackActual").animate({scrollTop: (teamStatus.currentLocation)});
$("#tableMembers").animate({scrollTop: (teamStatus.currentLocation) });
//console.log(teamStatus.currentLocation());
teamStatus.buttonRevealer();
}
}
As you can see increaseTable should update the value of currentLocation - help this sheds more light on what I am trying to achieve.
You're writing teamStatus.currentLocation() =, which calls the function teamStatus.currentLocation and tries to assign to the return value. That isn't valid. You want just teamStatus.currentLocation = — no function call.
The variable inside your function is completely private to that function (and any functions defined within it). If you need to create a number of functions that share a set of private variables, you can do that with a closure. For instance:
var Thing = (function() {
var thingWideData;
function getData() {
return thingWideData;
}
function setData(newData) {
thingWideData = newData;
}
return {
getData: getData,
setData: setData
};
})();
What that does is create a Thing object which has getData and setData functions available for it, which get and set the completely private thingWideData variable contained by the anonymous closure. More about this pattern here and here, although the latter of those is more about private methods than private data.
What your code produces is:
0 = 0 + <some number>
Which variable do you want to update? cL? You are declaring it in the function, you cannot assign a value to it from outside. Depending on the rest of your code, you might be better off with getters and setters:
var object = {
_cL = 0,
get currentLocation() {
return this._cL;
},
set currentLocation(value) {
this._cL = value;
}
}
then you can do:
teamStatus.currentLocation = teamStatus.currentLocation + teamStatus.scrollDistance();
Update:
Regarding IE: If currentLocation should actually be just a number, it might be sufficient to just declare it as property:
var obj = {
currentLocation: 0
}