angular2 saving response from firebase to a model - javascript

Thanks a lot for reading this question. Have been struggling for a while, will be great if someone point out what I'm doing wrong here.
I'm getting result from firebase and trying to save it in the User Model, but I'm getting this error
Uncaught TypeError: Cannot set property 'users' of null.
import { Component, OnInit, EventEmitter, Output } from '#angular/core';
import { User } from '../user.ts';
import { UserComponent } from './user.component';
declare var firebase: any;
#Component({
moduleId: module.id,
selector: 'trafford-user-list',
templateUrl: 'user-list.component.html',
styleUrls: ['user-list.component.css'],
directives: [ UserComponent ]
})
export class UserListComponent implements OnInit {
private users: User[] = [{ name: 'sosk', email: 'dfdok', password: 'asdf'}];
private user: User;
#Output() selected = new EventEmitter<User>();
constructor() { }
ngOnInit() {
this.getUsers();
}
getUsers() {
firebase.database().ref('accounts').on('value', function(snapshot) {
this.users = snapshot.val();
});
}
onSelected(user: User) {
this.selected.emit(user);
}
}
When I try to write the response in the console its working. But this.users don't have any scope inside that firebase callback. I'm very new to angular any help would be appreciated.

Use arrow function (=>) to preserve scope
getUsers() {
firebase.database().ref('accounts').on('value', (snapshot) => {
this.users = snapshot.val();
});
}

Related

Can't get deeper into the response data object in subscribe's callback function. Why?

I'm fetching data from RandomUser api with Angular HttpClient. I've created a method in a service calling GET, mapping and returning a Observable. Then I subscribe on this method in a component importing this service and in subscribe's callback I am trying to store the response data in a local variable. The problem is I can't get "deeper" into this response object than:
this.randomUser.getNew().subscribe(data => {
this.userData = data[0];
})
If I'm trying to reach any further element of that response object, and log it to console it I get "undefined". To be precise I cant reference to, for example:
this.randomUser.getNew().subscribe(data => {
this.userData = data[0].name.first;
})
If I store the "data[0]" in a variable first I can get into these unreachable properties. What is the reason of it? Please, help. Let me know what important piece of fundamental JS (or Angular) knowledge I'm not aware of. As far as I know I should be able to do what I am trying to do :)
service looks like these
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { map } from 'rxjs/operators';
import { Observable } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class RandomUserService {
url: string = " https://randomuser.me/api/ "
constructor(private http: HttpClient) { }
public getNew(): Observable<any> {
return this.http.get(this.url)
.pipe(map(responseData => {
const returnDataArray = [];
for (const key in responseData) {
returnDataArray.push(responseData[key])
}
return returnDataArray;
}))
}
}
component looks like these:
import { Component, OnInit } from '#angular/core';
import { RandomUserService } from 'src/app/shared/random-user.service';
import { Observable } from 'rxjs';
#Component({
selector: 'app-single-character',
templateUrl: './single-character.component.html',
styleUrls: ['./single-character.component.scss']
})
export class SingleCharacterComponent implements OnInit {
userData: object;
fname: string;
constructor(private randomUser: RandomUserService) {
this.randomUser.getNew().subscribe(data => {
this.userData = data[0];
})
}
ngOnInit(): void {
}
}
You are not parsing the returned data correctly in getNew().
The returned data looks like this:
So you need to access the user data like:
this.randomUser.getNew().subscribe(data => {
this.userData = data[0][0]; // note 2nd [0]
})
or for first name:
this.randomUser.getNew().subscribe(data => {
this.userData = data[0][0].name.first;
})
See stackblitz here: https://stackblitz.com/edit/so-http-parse?file=src/app/app.component.ts

Cannot access variable outside the subscription

I have an component in angular. I get data from the API and I instantiate a new object to create a map. But I can't access a variable outside the subscribe function. I also can't access my method.
maps.service.ts
This part, get data form api
import { Injectable } from '#angular/core';
import {HttpClient} from '#angular/common/http';
#Injectable()
export class MapsService {
constructor(private http: HttpClient) { }
getMap(params) {
console.log('Service', params);
return this.http.get('/api/posts/' + params.id);
}
}
map.component.ts
Here, where I build the map with google in the future
import { Component, OnInit } from '#angular/core';
import {MapsService} from '../maps.service';
import {ActivatedRoute} from '#angular/router';
import {MapPath} from '../map-path';
#Component({
selector: 'app-maps',
templateUrl: './maps.component.html',
styleUrls: ['./maps.component.css']
})
export class MapsComponent implements OnInit {
results: any;
params: {};
test: any;
constructor(
private mapsService: MapsService,
private route: ActivatedRoute,
) { }
ngOnInit() {
this.route.params.subscribe( params => {
this.params = params;
});
this.mapsService.getMap(this.params).subscribe(
data => {
this.results = data;
this.test = new MapPath(data, 'test');
},
err => {
console.log('Error occured', err);
});
console.log('this.test', this.test.getX());
console.log('this.results', this.results);
}
}
map-path.ts
Here get the different properties from geoJSON
export class MapPath {
test: string;
constructor(path: any, test: string) {
this.test = test;
console.log('Path', path);
console.log('test', test);
}
getX() {
return this.test;
}
}
Thanks.
Your problem is that Observables are async functions, meaning your subscription callback will happen later than the console.log calls following your this.mapsService.getMap call.
This is guaranteed by async nature of Observables.
You can either move your console logs inside of the subscribe function or create another subscription.
Hope this helps.

Undefined this in ES6/Typescript Callback Arrow Function

In an Ionic/Angular project, I would like to move from the ES5 syntax of referencing this by using a self variable to Arrow Functions.
However, arrow functions don't seem to allow me to access this inside a callback function.
Here is an example of using the arrow function for the then case of a Promise (Ionic's LocalStorage), which results in this as being undefined.
constructor(private storage: Storage) {
}
getItems() {
this.storage.get('items').then((res) => {
console.log(this);
})
I've even found a guide that seems to do exactly the same: Ionic Todo List
His DataService:
getData() {
return this.storage.get('todos');
}
His Component:
this.dataService.getData().then((todos) => {
if(todos){
this.items = todos;
}
});
As I've said, using a dummy variable works perfectly:
getItems() {
let self = this;
this.storage.get('items').then((res) => {
console.log(self);
})
Do I have a fundamental misunderstanding of how arrow functions work? Thanks.
EDIT:
As requested, here is a more complete example:
My Ionic3 DataProvider:
import { Injectable } from '#angular/core';
import { Storage } from '#ionic/storage';
#Injectable()
export class DataProvider {
constructor(private storage: Storage) {
}
getItems() {
return this.storage.get('items');
}
}
My Ionic3 HomePage:
import { Component } from '#angular/core';
import { IonicPage, NavController } from 'ionic-angular';
import { DataProvider } from '../../providers/data/data';
#IonicPage()
#Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
items = [{
title: 'Title',
description: 'Description'
}];
constructor(public navCtrl: NavController,
private dataProvider: DataProvider) {
let self = this;
this.dataProvider.getItems()
.then((items) => {
# self works perfectly, but this is undefined
console.log(self.items);
console.log(this.items);
if (items) {
self.items = items;
}
})
.catch((e) => {
console.log(e);
});
}
ionViewDidLoad() {
console.log('ionViewDidLoad HomePage');
}
}
Expected: this should be equal self, but is undefined
I hope this helps. Thanks.

Angular 2 Interface throwing error of non existing property

I have an Angular 2 interface books.ts
export interface Books {
artists: Object;
tracks: Object;
}
This is the my service file where I am using it with http request searchService.ts
import { Injectable } from '#angular/core';
import { Http, Response, Headers, RequestOptions } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import { Books } from 'app/pages/search-results/books';
import 'rxjs/add/operator/map'
#Injectable()
export class SearchService {
constructor(private _http:Http) { }
getBook(keyword): Observable<Books[]>{
return this._http.get('https://api.spotify.com/v1/search?q=' + keyword + '&type=track,artist')
.map((response: Response) => <Books[]> response.json());
}
}
And this is my component where I am using interface searchResults.ts
import { Component, OnInit } from '#angular/core';
import { ActivatedRoute, Router } from '#angular/router';
import { SearchService } from 'app/shared/search/search.service';
import { Books } from 'app/pages/search-results/books';
#Component({
selector: 'app-search-results',
templateUrl: './search-results.component.html',
styleUrls: ['./search-results.component.css'],
providers: [SearchService]
})
export class SearchResultsComponent implements OnInit {
keyword: any;
sub: any;
books: Books[];
errMessage: string;
arists: Object;
constructor(private _route: ActivatedRoute, private _router: Router, private _search: SearchService) { }
ngOnInit() {
this.sub = this._route
.queryParams
.subscribe(params => {
// Defaults to 0 if no query param provided.
this.keyword = params['keyword'] || 0;
this.getBooks(this.keyword);
});
//
}
getBooks(value) {
this._search.getBook(value)
.subscribe(
res => {
this.books = res;
console.log(res.artists);
},
error => { this.errMessage = <any>error }
);
}
}
The error comes when I try to console the res.artists. The error says Property 'artists' does not exist on type 'Books[]'. I am new to Angular 2 and doesn't know how to fix that.
The response is looks like
{artists:{limit: 20, item:[]}, tracks:{limit: 20, item:[]}}
I'm not sure but I think you try to get res.artist from collection of books. You can check it by for or e.g res[0].artist to get concrete artist.
getBook function in class SearchService return an array of Books object (Books[])
so, the res in getBooks function in SearchResultsComponent will be an Array of Books.
You can console.log(res) to see detail, if you want access to artists please try with res[0].artists if the res is not an empty array
The problem is that I am getting Object in response and I am assigning it to an Array which is causing the error. I have simply changes the both types to object and it solved my problem.
From this
books: Books[];
To this
books: Books;

cant pass data from AppComponent to function

I send the email data to this.user in constructror.
So it storage in AppComponent, Next i need this variale in function getUserData for import some data...
but the console.log show undefined, and there is also error for users :
Cannot read property 'auth' of undefined
So what i made wrong? Why i cant pass data using this.?
Update
Now the user.String is string that contain a "xxxx#.xx.com"
But still i cant pass it there. this.user in getUserData is undefind :/
import { Component, OnInit } from '#angular/core';
import { RewardsComponent } from './rewards/rewards.component';
import { AngularFire,AuthProviders, AuthMethods, FirebaseAuthState } from 'angularfire2';
import { Router } from '#angular/router'
declare var firebase: any;
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
title = 'app works!';
userData: any = [];
user: String;
constructor(public af: AngularFire, private router: Router) {
af.auth.subscribe(auth => {
if(auth) {
this.user = auth.auth.email.toString();
}
})
}
ngOnInit() {
this.getUserData();
}
getUserData() {
var ref = firebase.database().ref("/user");
ref.orderByChild("email").equalTo(this.user).on("child_added", (snapshot) => {
this.userData.push(snapshot.val());
});
}
}
Probably
this.user = auth.auth.email;
is storing a string, something like 'someemail#gmail.com'
When you try to access
this.user.auth
there is no .auth attribute/key, because this.user is not an object.
Also, you have to keep in my mind that af.auth.subscribe is assynchronous code, therefore you can't access this.user in the ngOnInit method, because you don't know if the af.auth.subscribe has been called yet.
You should access email since you are assigning only email ,
console.log(this.user.email);
var users = this.user.email;
if you need to access the whole auth object, then assign it as,
this.user = auth;

Categories

Resources