i have a trouble.
here i let my code.
https://gist.github.com/anonymous/b651408a8419f13a949d719e6b87d8ea
in my app i connect to the firebase cloud message service, in the appComponent i listen the messages that firebise send and emit the data content whit the DataInterchage.service, in the chatComponent i suscribe to the event emited and i process the data.
the problem is the next. when I receved the data, i set the this.messeges variable the data content but the view dont update.
what do you believe that be?
when you set this.messages your code might be running outside the angular because that code is written in service callback. that is why when you assign values to variable it doesn't update the view.
try running code inside the angular NgZone. after that your view will be updated successfully.
for your code snippet will be
import {NgZone,ChangeDetectorRef} from "#angular/core";
export class ChatComponent implements OnInit{
constructor(
private zone: NgZone,
private cd: ChangeDetectorRef,
) {}
ngOnInit() {
this.user = JSON.parse(appStorage.getString("user_info"));
this.me = {
id: this.user.id,
name: this.user.full_name,
pictureUrl: this.user.icon
};
this.other = {
id: "",
name: "",
pictureUrl: "",
coverUrl: ""
};
this.emitter.msgRecived$
.subscribe(data => {
data = JSON.parse(data);
this.http.get(`${ env['api_route'] }/api/users/${ data.user }`)
.subscribe((res: Response) => {
let user = res.json().data;
this.other = {
id: user.id,
name: user.full_name,
pictureUrl: user.icon,
};
this.zone.run(()=>{
this.messages.push({
sender: this.other,
content: data.message,
date: data.date
});
});
console.dump(this.other);
console.dump(this.messages)
}, (err: Response) => {
this.oauth.isLogged(err);
});
});
}
}
Related
I'm trying to use an angular service to store and communicate data through observable.
Here is my service
public _currentLegal = new BehaviorSubject({
name: 'init',
_id: 'init',
country: 'init',
city: 'init',
});
readonly currentLegal$ = this._currentLegal.asObservable();
constructor(private http: HttpClient) {
}
setCurrentLegal(legal) {
const legaltest = {
name: 'test',
_id: 'test',
country: 'test',
city: 'test',
}
console.log('emitting next value from', legal.name)
this._currentLegal.next({ ...legaltest });
}
I got a component that call setCurrentLegal, the console.log is triggered and correct. I then navigate to another component which subscribe to the currentLegal$ observable and the value is still the same (init) !
I tried accessing the value directly by setting public type and using getValue(), same.
I tried duplicating the object as to not pass a reference, did not change anything.
Here is my subscripting component ngOnInit :
ngOnInit(): void {
this.legalToUpdate = this.legalService.currentLegal$.subscribe((legal) => {
console.log(legal)
})
}
What's wrong here ?
Thanks
You have this behavior because you use it in different modules and it is created a different instances.
Put your service to be provide in root:
#Injectable({ providedIn: 'root' })
export class LegalService implements HttpInterceptor { }
I'm using stripe-js.
When I call this.initializePaymentRequest() at following code,
If I call initializePaymentRequest() from first observable, canMakePayment() returns Object, and I'm able to see that google pay is supported in browser.
If I call initializePaymentRequest() from this.datas.subscribe, I'm getting null from canMakePayment() which is not true. I'm still same tab, and google pay is supported.
export class DatasComponent implements OnInit {
datas: any;
data: any;
data2s: any;
data2: any;
paymentRequest: any;
private isStripeAvailable: boolean;
constructor(
private db: AngularFirestore,
private paymentService: PaymentService
) {
// stripe js load status listener (true/false)
paymentService.stripeStatus.asObservable().subscribe(data2 => {
this.isStripeAvailable = !!data2;
if ((this.data || {}).val) {
// /******************** works here ****************
this.initializePaymentRequest();
}
});
this.slug1 = 'hello', this.slug2 = 'hi';
this.data2s = db
.collection('data2s', ref => ref
.where('slug', '==', this.slug1)
).valueChanges();
this.data2s.subscribe(data3 => {
if (data3.length) {
this.data2 = data[0];
this.datas = db
.collection('datas', ref => ref
.where('slug', '==', this.slug2)
)
.valueChanges();
this.datas.subscribe(data4 => {
if (data4.length) {
this.data = data4[0];
if (this.isStripeAvailable) {
// /*************** doesn't work here ********
this.initializePaymentRequest();
}
}
});
}
});
}
initializePaymentRequest = () => {
this.paymentRequest = this.paymentService.stripe.paymentRequest({
country: 'US',
currency: 'usd',
total: {
label: 'Sample Payment',
amount: 500,
},
requestPayerName: true,
requestPayerEmail: true,
requestPayerPhone: true,
});
this.paymentRequest.canMakePayment().then(data => {
// data is object if called from first, null if called from second
debugger;
});
}
}
Why would this happen?
Update
I can see that if I call initializePaymentRequest() within setTimeout, It is returning null too. Is there any way set timeout is breaking payment apis?
In an angular 5 app, there is a route guard that check from an API if an object exists:
//guard.ts excerpt
canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<boolean> | Promise<boolean> | boolean {
return this.clientService.get(next.params.id).switchMap( data => {
return Observable.of(true);
})
.catch( err => Observable.of(false))
}
//route.ts excerpt
{ path: ':id', canActivate: [ ClientDetailGuard ], component: ClientDetail }
this works perfect, but I am wondering if is there a way to pass the data retrieved from my service to next the route/component (ClientDetail), so I won't need to call the service again this again.
I tried to add
next.data.client = data;
before the return of Observable(true) but in the component, the ActivatedRoute's data does not have this value set.
Or should I use something like Resolve?
I know I can achieve this using some state container or a shared service to store/retrieve data, but I wouldn't like to do this at this time, as long as the app is not complex.
I could do this using a Resolver instead of a guard
//route.ts
{ path: ':id', resolve: { client: ClientDetailResolver }, component: ClientDetail }
//resolver.ts
#Injectable()
export class ClientDetailResolver implements Resolve {
constructor(private clientService: ClientService, private router: Router, public location: Location) {
}
resolve(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<any>|Promise<any>|any {
return this.clientService.get(route.params.id)
.catch( err => {
//handle error
const path = this.location.path();
this.router.navigate(["error", err.status], { skipLocationChange: true })
.then( () => {
this.location.replaceState(path);
});
return Observable.empty();
})
}
}
You seem to be under-estimating the power of services. Services are the best way to save/store data or states between components. You can set the data from any component, pull the data from any component. You don't have to worry about putting data in for the next route, instead you go to the next route and subscribe to your data on ngOnInit and boom, got everything you need. Here is an example of just how simple it really is.
Example of service
import { Injectable } from '#angular/core';
import { BehaviorSubject, Subscription } from 'rxjs';
#Injectable()
export class AppService {
alertData = new BehaviorSubject({
type: '',
message: '',
timer: 0,
showing: false
});
constructor() {}
setAlertData(data: AlertModel) {
this.alertData.next(data);
}
}
Example of using service
this.subscription$.push(this._as.alertData.subscribe((data: AlertModel) => {
this.alertData = data;
if (data.showing) {
this.alertIsShowing = true;
}
else {
this.alertIsShowing = false;
}
}));
On my web-app written in angular I am posting data to a Database and I am displaying this data in a table on the same html. Each data record has an ID. And every time I am adding new data, the ID is going to be increased. The first input field shows the actual ID, see the screenshot below:
In my ngOnInit-method I am initialising the id and I call the function fbGetData() in order to display the data.
But now I am facing one odd problem:
Everytime I starting the application the initial value which is displayed in the ID-field is NaN.
Obviously I cannot post any data to the database because the ID is not a number. So I have to switch to another page on my application and then switch back. After that the correct ID is displayed. I also tried to move my methods from the ngOnInit-method to the constructor but this didn't help.
Somehow I think that I need to implement the methods asynchronously, but I have no idea how to do this, since I am quite new to Angular/Typscript.
I hope you guys can help me with this problem or give me any hint or idea.
I appreciate your answers!
Here is my .ts Code:
import { Component, OnInit, ViewEncapsulation } from '#angular/core';
import { Router, ActivatedRoute, Params } from '#angular/router';
import { DataService } from '../data.service';
import { Http } from '#angular/http';
import { rootRoute } from '#angular/router/src/router_module';
import { SearchNamePipe } from '../search-name.pipe';
import { LoginComponent } from '../login/login.component';
import {NavbarService} from '../navbar.service';
declare var firebase: any;
const d: Date = new Date();
#Component({
selector: 'app-business-function',
templateUrl: './business-function.component.html',
styleUrls: ['./business-function.component.css'],
encapsulation: ViewEncapsulation.None,
providers: [DataService, SearchNamePipe, LoginComponent]
})
export class BusinessFunctionComponent implements OnInit {
id;
name: String;
descr: String;
typ: String;
bprocess: String;
appsystem: String;
applications: String;
datum: String;
liste = [];
bprocessliste = [];
applicationliste = [];
appsystemliste = [];
isDesc: boolean = false;
column: String = 'Name';
direction: number;
loginName: String;
statusForm: Boolean = false;
private idlist = [];
constructor(
private dataService: DataService,
private router: Router,
private route: ActivatedRoute,
private searchName: SearchNamePipe,
private navbarService: NavbarService
) {
this.datum = Date().toString();
}
ngOnInit() {
this.navbarService.show();
firebase.database().ref().child('/AllID/').
on('child_added', (snapshot) => {
this.idlist.push(snapshot.val()
)})
this.id = this.idlist[0];
console.log("ID: "+this.id);
console.log("IDlist: "+this.idlist[0]);
this.id++;
console.log("ID: "+this.id);
this.fbGetData();
}
fbGetData() {
firebase.database().ref().child('/BFunctions/').orderByChild('CFlag').equalTo('active').
on('child_added', (snapshot) => {
//firebase.database().ref('/BFunctions/').orderByKey().on('child_added', (snapshot) => {
// alter code ... neuer Code nimmt nur die Validen mit dem X Flag
this.liste.push(snapshot.val())
});
// firebase.database().ref().child('/ID/').on('child_added', (snapshot) => {
//Bprocess DB Zugriff
firebase.database().ref().child('/BProcess/').orderByChild('CFlag').equalTo('active').
on('child_added', (snapshot) => {
this.bprocessliste.push(snapshot.val())
});
//Appsystem DB Zugriff
firebase.database().ref().child('/Appsystem/').orderByChild('CFlag').equalTo('active').
on('child_added', (snapshot) => {
this.applicationliste.push(snapshot.val())
})
//Application DB Zugriff
firebase.database().ref().child('/Application/').orderByChild('CFlag').equalTo('active').
on('child_added', (snapshot) => {
this.applicationliste.push(snapshot.val())
});
console.log(this.applicationliste);
}
You need to update the id inside your callback:
firebase.database().ref().child('/AllID/').on('child_added', (snapshot) => {
this.idlist.push(snapshot.val())
this.id = this.idlist[0];
console.log("ID: "+this.id);
console.log("IDlist: "+this.idlist[0]);
this.id++;
console.log("ID: "+this.id);
this.fbGetData();
})
Otherwise id retains it initial undefined value. This is because the call to firebase is asynchronous.
Here is what happens in your original code:
call to firebase API... wait your response
set id to this.idlist[0], which is empty (undefined)
...some time later, getting response from firebase
id does not get updated because the code in point 2. has already been executed.
Anything that you need to do when you get the result from an asynchronous call, must be executed inside the callback function.
I'm working on a simple project using nodejs and angular 2.
In my client project I have a component which has a form and a submit event. When I throw this event all data from the form is properly sent to my node application.
But I have another component which has a table where I list all registers located on my database.
Everything works fine. But I should make my table load in the same time after I've sent my submit.
I have no idea how I can do this.
I tried implement something like this:
export class EmployeeListComponent implements OnInit {
public employees: string[];
constructor(private employeeService : EmployeeService) {
this.employeeService.employeeRequest().subscribe(employees => {
this.employees = employees;
})
}
ngOnInit() {
}
}
And in my register component I did this method:
onRegister(){
const Employee = {
name : this.name,
familyName: this.familyName,
participation: this.participation
}
if (!this.validateService.validateRegister(Employee)){
this.alertMsg = JSON.stringify({'msg': 'All fields must be filled'});
console.log(this.alertMsg);
return false;
} else {
this.registerService.employeeRegister(Employee).subscribe(data => {
this.alertMsg = JSON.stringify({'msg': 'Employee registered successfully'});
console.log(this.alertMsg);
return this.router.navigate(['/list']);
});
};
};
My tests show that my code works fine, but my table just load properly at first submit. After that I have to refresh manually the browser to load my table again.
Is there someone who know what I'm doing wrong or can tell me some way to code that?
Try to change your constructor to a function, and than you call it on ngOninit, so every time your list component initialize your list of employees will load again, something like this:
export class EmployeeListComponent implements OnInit {
public employees: string[];
constructor(private employeeService : EmployeeService) { }
ngOnInit() {
this.loadAllEmployees();
}
loadAllEmployees() {
this.employeeService.employeeRequest().subscribe(data => {
this.employees = data;
});
}
}
Also see Angular life cycle hooks documentation to understand more about ngOnInit and others:
https://angular.io/guide/lifecycle-hooks
Well. After I got the suggestions I tried another strategies. First I've simplify my two components as a single component. Then I moved my logic from the constructor to a method called dashCall() which I call inside my constructor and in the final of onRegister() method.
The final code looks like this:
export class EmployeeRegisterComponent {
public name: string;
public familyName: string;
public participation: number;
public alertMsg: any;
public employees: string[];
constructor(private validateService : ValidateService,
private registerService : EmployeeService,
private employeeService : EmployeeService) {
this.dashCall();
}
onRegister(){
const Employee = {
name : this.name,
familyName: this.familyName,
participation: this.participation
}
if (!this.validateService.validateRegister(Employee)){
this.alertMsg = JSON.stringify({'msg': 'All fields must be filled'});
return false;
} else {
this.registerService.employeeRegister(Employee).subscribe(data => {
this.alertMsg = JSON.stringify({'msg': 'Employee registered successfully'});
this.dashCall();
});
};
};
dashCall(){
this.employeeService.employeeRequest().subscribe(employees => {
this.employees = employees;
});
}
}
I know it's not the best way to implement this. But in the end I got what I needed.