Accessing current controller instance (`this`) in events - angularjs

We are using TypeScript with angularjs 1.5. We have a situation where we need to trigger an event to load data from the parent component. The problem we are facing is that when the event is triggered, this is referring to the window object not the controller in which it is present. How to get the current instance in the event?
Because it is TypeScript any method or property can be accessed though this only. And I need the controller instance because there are few initialization done in the constructor which I need to access.
Example implementation
namespace doc.common {
#Component({
selector: name
})
export class myComponent {
#bind.oneWay()
public id:number;
public service:myService;
/*ngInject*/
constructor(private serviceRef:myService){
this.service = serviceRef;
}
public $onChange():void {
//*this* is referred to window object, not controller instance
}
}
}
Usage
<my-component id='id'></my-component>

Related

Error occurs in the template of component upperCaseComponent

It shows an error with the template,
Errors:
Property 'result' is private and only accessible within class 'upperCaseComponent'
Property 'mymessage' is not declared in 'upperCaseComponent'
my html
<input type="text" [(ngModel)]="mymessage"
<button (click)="clickMe()"> send</button>
<br><br>
<h1>{{result | json}}</h1>
my component.ts
import {Component} from "#angular/core";
export class MyComponent {
private result: any;
constructor()
}
So here the error say it not able to find variable 'mymessage' in uppercase.component.ts
export class UpperCaseComponent {
public results: any; // Change it private to public
public mymessage: any;
}
If you are trying to access variable within template you need to declare it as public as if you declare it as private it would be accessible only within class
welcome to SO.
Regarding your question, could you please give me a confirmation on the below points so that I can help you in a better way.
Can you please edit the question and post the component code here. (You can mask the sensitive data here).
Please check if you are using binding data is marked private?
Try like this:
export class upperCaseComponent {
result: any;
mymessage: String;
... // other code goes here
}
UpperCaseComponent.ts has result field as private, uppercase.component.html cannot access it so make result field as public.
As for myMessage field as it's not defined anywhere in the UpperCaseComponent.ts I assume you want to set it from the parent component, then create an #Input myMessage field in UpperCaseComponent.ts and use it in the template. Bind this from parent component wherever you want to use this UpperCaseComponent

Opening error popover from service in Angular 2

I would like to create a service which will be responsible for opening bootstrap popovers with errors and success communicates. I have two components ErrorComponent, SuccessComponent and one service called CommunicatesComponent. I would like to use this service to open popover like
service.error('some error')
service.success('success!')
And this should display popover with provided text as argument. What I am doing is setting component property in service like followed and use this property in this service:
ErrorComponent
export class ErrorComponent implements OnInit {
public text:string;
#ViewChild('errorPopover') private errorPopover: NgbPopover;
constructor(private communicatesService:CommunicatesService) {
}
public ngOnInit() {
this.communicatesService.setErrorComponent(this)
}
}
Service:
#Injectable()
export class CommunicatesService {
private errorComponent:ErrorComponent
public setErrorComponent(component:ErrorComponent) {
this.errorComponent = component;
}
public error(text:string) {
console.log(this.errorComponent)
// this.errorComponent.text = text;
}
}
Unfortunitelly, it seems that my component object is not provided well, because console log prints undefined. How it should be done?
Regards
There are two things I would change in your design
ErrorComponent and CommunicatesService depends on each other. It's good to avoid it - CommunicatesService could easily work with different components. So you could create an rx Observable public property of the service, so any component can subscribe to it. When service.success('success!'), the service will send the message text to the subscribers.
In ErrorComponent, you get the popover component as a #ViewChild. You could consider binding ErrorComponent.text to the popover directly (reversing the dependency).
These changes could solve the problems you have and make the design looser - easier to understand and maintain.

Angular 2 : Call root component's method from nested child component

I have few components in my application:
//.ts:
RootComponent
-ParentComponent
--ChildComponent
---LastLevelComponent
//.html
<root-component>
<parent-component>
<child-component>
<last-level-component></last-level-component>
</child-component>
</parent-component>
</root-component>
I want to call a method of Rootcomponent from LastLevelComponent.
Am aware of EventEmitter, but problem with that is I need to pass
value to each child component.
Is there any way I can call directly
RootComponent's method from LastLevelComponent without having
dependency on ParentComponent or ChildComponent.
You could use an #Input() in your LastLevelComponent.
Or an #Output() without Child- and ParentComponent needs to use this Event.
//.html
<root-component #rootCmp>
<parent-component>
<child-component>
<last-level-component [root-comp]="rootCmp" (myOutput)"rootCmp.func()"></last-level-component>
</child-component>
</parent-component>
</root-component>
You can solve this by creating a service. Follow these steps:
Create a service
#Injectable()
export class CompService {
private componentRef = null;
constructor(){
}
getParent(){
return this.componentRef;
}
setParent(ref){
this.componentRef = ref;
}
}
Include this service in your root component, in providers section:
#Component({
. . .
providers: [CompService]
})
export class RootComponent {
constructor(
private cmpService?: CompService)
{
}
}
During initialization of the root component, call the service to set the component value to point to the root:
ngOnInit(){
this.cmpService.setParent(this);
}
In child component or last level component, call this service to retrieve the root component:
#Component({
. . .
})
export class LastLevelComponent {
constructor(
private cmpService?: CompService)
{
}
someMethod(){
if (this.cmpService){
let parentCtrl = this.cmpService.getParent();
// Call your parent method or property here
// if (parentCtrl)
// parentCtrl.rootMethod();
}
}
}
You may notice that CompService is only set in providers section of root component. This enables to have one instance of this service. So all child components will inherit the service settings pointing to the root component.
You only need to call the getParent method of the service in each child component to get access to the root component public methods, properties and events.
Furthermore, this service is reusable and avoids dependency of any other component.

How does Angular 2 change detection work?

In Angular 1, change detection was by dirty checking the $scope hierarchy. We would implicitly or explicitly create watchers in our templates, controllers or components.
In Angular 2 we no longer have $scope, but we do override setInterval, setTimeout, et al. I can see how Angular might use this to trigger a $digest, but how does Angular determine what has changed, especially given that Object.observe never made it into browsers?
Example
Here is a simple example. An object defined in a service is updated in a setInterval. The DOM is recompiled each interval.
How is Angular able to tell that the AppComponent is watching the service, and that the value of an attribute of the service has changed?
var InjectedService = function() {
var val = {a:1}
setInterval(() => val.a++, 1000);
return val;
}
var AppComponent = ng.core
.Component({
selector: "app",
template:
`
{{service.a}}
`
})
.Class({
constructor: function(service) {
this.service = service;
}
})
AppComponent.parameters = [ new ng.core.Inject( InjectedService ) ];
document.addEventListener('DOMContentLoaded', function() {
ng.platform.browser.bootstrap(AppComponent, [InjectedService])
});
Angular creates a change detector object (see ChangeDetectorRef) per component, which tracks the last value of each template binding, such as {{service.a}}. By default, after every asynchronous browser event (such as a response from a server, or a click event, or a timeout event), Angular change detection executes and dirty checks every binding using those change detector objects.
If a change is detected, the change is propagated. E.g.,
If an input property value changed, the new value is propagated to the component's input property.
If a {{}} binding value changed, the new value is propagated to DOM property textContent.
If the value of x changes in a style, attribute, or class binding – i.e., [style.x] or [attr.x] or [class.x] – the new value is propagated to the DOM to update the style, HTML attribute, or class.
Angular uses Zone.js to create its own zone (NgZone), which monkey-patches all asynchronous events (browser DOM events, timeouts, AJAX/XHR). This is how change detection is able to automatically run after each asynchronous event. I.e., after each asynchronous event handler (function) finishes, Angular change detection will execute.
I have a lot more detail and reference links in this answer: What is the Angular2 equivalent to an AngularJS $watch?
Zone.js
Changes happen as a reaction to something, so in this respect they are asynchronous. They are caused by asynchronous actions, and in the browser world those are Events. To intercept those events angular uses zone.js, which patches JavaScript call stack (I beleive, someone correct me if I'm wrong) and exposes hooks that can be used to take other actions.
function angular() {...}
zone.run(angular);
If you imagine this angular function is the entire Angular, this would be how it is run in zone. By doing so Events can be intercepted and if they are triggered we can assume changes happen, and listen/watch for them.
ApplicationRef
In reality ApplicationRef creates the zone:
/**
* Create an Angular zone.
*/
export function createNgZone(): NgZone {
return new NgZone({enableLongStackTrace: assertionsEnabled()});
}
and class NgZone is created with few event emitters:
this._onTurnStartEvents = new EventEmitter(false);
this._onTurnDoneEvents = new EventEmitter(false);
this._onEventDoneEvents = new EventEmitter(false);
this._onErrorEvents = new EventEmitter(false);
that it exposes to the outside world via getters:
get onTurnStart(): /* Subject */ any { return this._onTurnStartEvents; }
get onTurnDone() { return this._onTurnDoneEvents; }
get onEventDone() { return this._onEventDoneEvents; }
get onError() { return this._onErrorEvents; }
When ApplicationRef is created it subscribes to the zone's events, specifically onTurnDone():
this.zone.onTurnDone
.subscribe(() => this.zone.run(() => this.tick());
Changes
When events are triggered tick() function is run which loops through every component:
this._changeDetectorRefs.forEach((detector) => detector.detectChanges());
and detects changes based on components' ChangeDetectionStrategy. Those changes are collected as an array of SimpleChange objects:
addChange(changes: {[key: string]: any}, oldValue: any, newValue: any): {[key: string]: any} {
if (isBlank(changes)) {
changes = {};
}
changes[this._currentBinding().name] = ChangeDetectionUtil.simpleChange(oldValue, newValue);
return changes;
}
witch is available for us through onChanges interface:
export interface OnChanges {
ngOnChanges(changes: {[key: string]: SimpleChange});
}

TypeScript: inherit from Array

I'm trying to create a rich data model for an AngularJS application and I would like for my model to inherit from Array<BaseModel>. I haven't found a way to do this (with confidence) yet.
In pseudocode this would be something like:
// NOT REAL CODE. DOES NOT WORK
class BaseModel {}
class Person extends BaseModel {}
class People extends Array<Person> {
constructor(private promise:Promise) { }
$unwrap(promise) {
promise.then((response) => {
response.data.map((person) => {
this.push(new Person(person));
});
});
}
static $load() {
/* do some ajaxy thing and unwrap the promise right onto the
the new instance of this rich model */
return new People(promise);
}
}
The reason I would like something like this is that now I can bind this model directly to my view and get updates on the fly.
Any thoughts on extending from Array?
At this point it time it is not possible to inherit from Array in TypeScript. It is best to unwrap into a public array field and use this field to bind to the UI. It resolves into the same functionality and remove the necessity of inheriting from Array.

Resources