Angular 2 issue in accessing componet property - javascript

i am new in Angular 2 and trying to develop a project with basic CRUD functionality.
I am facing an issue that i have a User component and trying to access it's property in ngAfterViewInit hook, but it's showing undefined.
Here is code of User component.
export class UserComponent implements OnInit, AfterViewInit
{
private users: User[];
private userRoles;
ngOnInit(){
//get users from service
this.getUserDetails();
}
ngAfterViewInit() {
console.log(this);
console.log(this.users);
}
getUserDetails() {
this._userService.getUsers().subscribe(users => this.users = users);
}
}
Below is screen shots of console. I can see that Object has property users but it shows undefined in console.
Please give suggestion if i missed anything.
Thanks in advance.

Check user property inside getUserDetails. You can use users property in your template. Check component lifecycle hooks to get better idea about component lifecycle.
getUserDetails() {
this._userService.getUsers().subscribe((users) => {
this.users = users;
console.log(this.users);
});
}

I think this is not angular 2 issue...
This is typescript issue accessing this instance globally .
We can do this by using let and declaring variable in typescripts...
When javascript object assigned is cloned to other object then changes made to one is automatically passed to other
So this issue also arrises when we want to access typescript this object inside jquery functions
let _this = this;
something.each(function() {
console.log(_this); // the lexically scoped value
console.log(this); // the library passed value
});

Related

calling angular component function from plain javascript

I am using angular for my web app and i need bluetooth on one of my pages.
I am using loginov-rocks/bluetooth-terminal(https://github.com/loginov-rocks/bluetooth-terminal) for the bluetooth connection and it works i can conect my device and see data from it. now the problem i have is i cannot get the data from my recieve function to my angular component, i can print data to console, but that is not what i want, i want to parse my data and set some variables in my angular component from it.Here is my code:
let bluetooth = new BluetoothTerminal();
bluetooth.receive = function (data) {
console.log(data);
//i want to call ParseBtData here from my angular component to parse data
//or somehow send data and cach it for parsing inside my component
};
export class GpsAppComponent extends AppComponentBase implements OnInit
{
//all the angular stuff
parseBtData(data) {
//parse my BT data and set some variables inside my component...
};
}
I have tryed making BluetoothTerminal inside the component, but i still cannot call any function to parse my data. Is it even possible to do that, or is there another way i should aproach my problem?
In general if you create an object javascript you can do it using declare, then the only is override the "received" data. But a change because an event not controlle by Angular, you need say to Angular that "something" has changed outside Angular, so you need use ngZone, some like:
//DISCLAMER: I don't know if work
declare var bluetooth = new BluetoothTerminal();
export class AppComponent implements AfterViewInit
{
constructor(private ngZone:NgZone,private dataService:DataService){}
ngAfterViewInit(){
bluetooth.receive = (data)=> {
this.ngZone.run(()=>{
this.dataService.sendData(data)
})
};
}
}
Then you only need define in your service
bluethoodData:Subject<any>=new Subject<any>()
sendData(data:any)
{
this.bluethoodData.next(data)
}
And you can subscribe in any component to dataService.bluethoodData
this.dataService.bluethoodData.subscribe(res=>{
console.log(res)
})
I managed to solve my problem by moving my bluetooth inside the component and doing:
this.bluetooth.receive = (data) => {
//console.log(data);
this.writeToTerminalConsole(data);
};

Why isn't my component object getting updated itself when anything in my globally shared service updates? Angular

I have this service:
export class RecipeService{
selectedRecipe: Recipe = 'xyz';
}
I have this component using this service:
export class RecipesComponent implements OnInit {
selectedRecipe: Recipe;
constructor(private recipeService: RecipeService) { }
ngOnInit(): void {
this.selectedRecipe = this.recipeService.selectedRecipe;
}
}
The service is defined in app.module.ts for injection, which means all components get the same instance.
My question is, whenever I update the selectedRecipe variable in one of my components, it doesn't get updated back in other components although it is referenced and hence I expect a change immediately.
What am I doing wrong?
It doesn't get updated because the new value is not "sent" to the already initiated angular components.
Instead you should use observables.
for example:
/* service */
private recipe = "xyz";
public recipeSubject: BehaviorSubject<string> = new BehaviorSubject(this.recipe);
// when changing the recipe
recipeSubject.next(this.recipe);
/* component */
this.service.recipeSubject.subscribe(res => this.recipe = res);
I googled and found out in one of the posts that its because of my object. The object (Recipe) in my service contains a primitive type, i.e string. If your object contains a primitive type, it isn't passed as a reference, hence a change in service object won't be reflected in component because they are now different.
Although I must clear that in case of array it worked perfectly fine even when my array contained objects which had primitive types. Changes were still reflected.

Angular/Typescript: Accessing object data in constructor

Trying to access an object's data in a constructor returns an "undefined" object. It works on the ngOnInit() function, but the data (going to get reset) is needed every time the component starts.
import { Component, OnInit, Input } from '#angular/core';
#Input() data: any;
constructor(dataService: DataService)
{
console.log(this.data); // undefined
}
ngOnInit()
{
console.log(this.data) // works here
}
Have you read this documentation?
It said:
After creating a component/directive by calling its constructor,
Angular calls the lifecycle hook methods in the following sequence at
specific moments
And then it lists all methods according to the order of excecution.
You can read the full documentation of lifecycle hook, it is good to know this when we start developing application using Angular.
You can't access the value of a component input inside its constructor, except for the initial value you assign to it.
Why?
Well, easy: Angular must create the component first (by invoking its constructor). Only then can it set the inputs. If you try to use an input property in the constructor, obviously it will be undefined.
In your case, you should use the value in ngOnInit, or, if you really need it in the constructor, retrieve it in another way (by using an injected service, a global shared object ...).

Angular 2 transfer ajax call response to another component

I just started playing with angular 2 and i've ran into a small problem, that i ve searched for in various forms and also angulars documentation.
I've managed to make a service that makes a call and then i want in a component when i press a button to load another component with dynamicload component and have access to the ajax result.
The problem is that I can t figure out how to do that..
The question is how can I make the result accesible in other components using Observables or Promises method.
If I understood correctly your question, you are looking a way to insert a data from request to another nested component.
I hope this image will clarify for you the data flow for this case.
Your Root component is calling a service method which returns for you promise object.
Then you map needed data from response to the component model inside Root Component constructor.
And your Child component should be subscribed for the model which you was preparing in previous step.
ngOnInit() {
this.dataService.getSomeData()
.subscribe((data: IData) => {
this.data = data;
});
}
Just a short example above how to set model in the root component from the promise object to the local model.
New research:
There is another way to fill your components by data from api's. You can use EventEmitter to emit event from service, and then, you can subscribe for this event inside you created components, so they will get a data, each time there will be called the service. Here is nice example of this strategy in the first answer. Service Events
Hope it will help you, let me know if you will need additional info!
Just create a service, then inject the service where you want.
Here it's an example how to share a service ajax data across many components without making the request twice :
https://stackoverflow.com/a/36413003/2681823
the Service:
#Injectable()
export class DataService {
constructor(private http: Http) { }
private _dataObs = new ReplaySubject<request>(1);
getData(forceRefresh?: boolean) {
// On Error the Subject will be Stoped and Unsubscribed, if so, create another one
this._dataObs = this._dataObs.isUnsubscribed ? new ReplaySubject(1) : this._dataObs;
// If the Subject was NOT subscribed before OR if forceRefresh is requested
if (!this._dataObs.observers.length || forceRefresh) {
this.http.get('http://jsonplaceholder.typicode.com/posts/2')
.subscribe(
requestData => {
this._dataObs.next(requestData);
},
error => this._dataObs.error(error));
}
return this._dataObs;
}
}
the Component:
#Component({
selector: 'child',
template : `<button (click)="makeRequest()" class="btn">Click me!</button>`
})
export class Child {
constructor(private _dataService: DataService) { }
makeRequest() {
this._dataService.getData().subscribe(
requestData => {
console.log('ChildComponent', requestData);
}
}
}
A full working example/plunker can be found here : http://plnkr.co/edit/TR7cAqNATuygDAfj4wno?p=preview

How to expose angular 2 methods publicly?

I am currently working on porting a Backbone project to an Angular 2 project (obviously with a lot of changes), and one of the project requirements requires certain methods to be accessible publicly.
A quick example:
Component
#component({...})
class MyTest {
private text:string = '';
public setText(text:string) {
this.text = text;
}
}
Obviously, I could have <button (click)="setText('hello world')>Click me!</button>, and I would want to do that as well. However, I'd like to be able to access it publicly.
Like this
<button onclick="angular.MyTest.setText('Hello from outside angular!')"></click>
Or this
// in the js console
angular.MyTest.setText('Hello from outside angular!');
Either way, I would like the method to be publicly exposed so it can be called from outside the angular 2 app.
This is something we've done in backbone, but I guess my Google foo isn't strong enough to find a good solution for this using angular.
We would prefer to only expose some methods and have a list of public apis, so if you have tips for doing that as well, it'd be an added bonus. (I have ideas, but others are welcomed.)
Just make the component register itself in a global map and you can access it from there.
Use either the constructor or ngOnInit() or any of the other lifecycle hooks to register the component and ngOnDestroy() to unregister it.
When you call Angular methods from outside Angular, Angular doesn't recognize model change. This is what Angulars NgZone is for.
To get a reference to Angular zone just inject it to the constructor
constructor(zone:NgZone) {
}
You can either make zone itself available in a global object as well or just execute the code inside the component within the zone.
For example
calledFromOutside(newValue:String) {
this.zone.run(() => {
this.value = newValue;
});
}
or use the global zone reference like
zone.run(() => { component.calledFromOutside(newValue); });
https://plnkr.co/edit/6gv2MbT4yzUhVUfv5u1b?p=preview
In the browser console you have to switch from <topframe> to plunkerPreviewTarget.... because Plunker executes the code in an iFrame. Then run
window.angularComponentRef.zone.run(() => {window.angularComponentRef.component.callFromOutside('1');})
or
window.angularComponentRef.zone.run(() => {window.angularComponentRef.componentFn('2');})
This is how i did it. My component is given below. Don't forget to import NgZone. It is the most important part here. It's NgZone that lets angular understand outside external context. Running functions via zone allows you to reenter Angular zone from a task that was executed outside of the Angular zone. We need it here since we are dealing with an outside call that's not in angular zone.
import { Component, Input , NgZone } from '#angular/core';
import { Router } from '#angular/router';
#Component({
selector: 'example',
templateUrl: './example.html',
})
export class ExampleComponent {
public constructor(private zone: NgZone, private router: Router) {
//exposing component to the outside here
//componentFn called from outside and it in return calls callExampleFunction()
window['angularComponentReference'] = {
zone: this.zone,
componentFn: (value) => this.callExampleFunction(value),
component: this,
};
}
public callExampleFunction(value: any): any {
console.log('this works perfect');
}
}
now lets call this from outside.in my case i wanted to reach here through the script tags of my index.html.my index.html is given below.
<script>
//my listener to outside clicks
ipc.on('send-click-to-AT', (evt, entitlement) =>
electronClick(entitlement));;
//function invoked upon the outside click event
function electronClick(entitlement){
//this is the important part.call the exposed function inside angular
//component
window.angularComponentReference.zone.run(() =
{window.angularComponentReference.componentFn(entitlement);});
}
</script>
if you just type the below in developer console and hit enter it will invoke the exposed method and 'this works perfect ' will be printed on console.
window.angularComponentReference.zone.run(() =>
{window.angularComponentReference.componentFn(1);});
entitlement is just some value that is passed here as a parameter.
I was checking the code, and I have faced that the Zone is not probably necessary.
It works well without the NgZone.
In component constructor do this:
constructor(....) {
window['fncIdentifierCompRef'] = {
component = this
};
}
And in the root script try this:
<script>
function theGlobalJavascriptFnc(value) {
try {
if (!window.fncIdentifierCompRef) {
alert('No window.fncIdentifierCompRef');
return;
}
if (!window.fncIdentifierCompRef.component) {
alert('No window.fncIdentifierCompRef.component');
return;
}
window.fncIdentifierCompRef.component.PublicCmpFunc(value);
} catch(ex) {alert('Error on Cmp.PublicCmpFunc Method Call')}
}
</script>
This works to me.
The problem is that Angular's components are transpiled into modules that aren't as easy to access as regular JavaScript code. The process of accessing a module's features depends on the module's format.
An Angular2 class can contain static members that can be defined without instantiating a new object. You might want to change your code to something like:
#component({...})
class MyTest {
private static text: string = '';
public static setText(text:string) {
this.text = text;
}
}
Super simple solution!! save component or function with an alias outside
declare var exposedFunction;
#Component({
templateUrl: 'app.html'
})
export class MyApp {
constructor(public service:MyService){
exposedFunction = service.myFunction;
}
at index.html add in head
<script>
var exposedFunction;
</script>
Inside exposed function do not use this. parameters if you need them you will have to use closures to get it to work
This is particularly useful in ionic to test device notifications on web instead of device

Categories

Resources