NodeJS, Angular 2 | Executing method on Observable next - javascript

I'm currently getting started with Angular 2 and got stuck on something probably pretty simple:
I have a shared service chatMessageService.ts:
import { Injectable } from '#angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
#Injectable()
export class ChatMessageService {
private messageList = new BehaviorSubject<string>("");
currentMessage = this.messageList.asObservable();
constructor() {
}
public addMessage(msg:string) {
this.messageList.next(msg) }
}
The service is imported by two components, one that calls it's addMessage function to add the message to the Observable and then my chatComponent.ts looks like this (shortened fpr convinience):
import { Component } from '#angular/core';
import { Message } from './message';
import { ChatMessageService } from './chatMessage.service';
#Component({
selector: 'app-chat',
templateUrl: './chat.component.html',
styleUrls: ['./chat.component.css']
})
export class ChatComponent {
conversation: Message[] = [];
//.....
constructor(private chatMessageService: ChatMessageService) { }
addUserMessage(message) {
this.conversation.push({
content: message
});
}
ngOnInit() {
this.chatMessageService.currentMessage.subscribe(message => {this.addUserMessage(message);} )
}
}
My crisis arises at that last subscripion part. When I replace
{this.addUserMessage(message);}
with
{console.log(message)}
the message is printed out perfectly fine. If I call the addUserMessage()-method manually it works just fine. But when I call the method right there, with the message as argument, nothing happens. The method isn't even executed?
Thankful for your insights!

It looks like you need some buffering in the service.
Instead of BehaviorSubject, try
private messageList = new ReplaySubject<string>(10);
See working example: Plunker

Related

Extracting data from model to variables

I'm new to typescript and angular and I was trying to fetch some data from firebase using angularfire2 and assign it to variables to use in some other functions later. I'm only familiar with javascript dot notation where I access members of the object using dot notation seems like it doesn't work with angular can somebody please help me with extracting data from the model to variables, please
I'm still having a hard time understanding Observable and subscribes too.
code
model
export class Reacts {
sad?: number;
happy?: number;
neutral?: number;
}
service
import { Injectable } from "#angular/core";
import {
AngularFirestore,
AngularFirestoreCollection,
AngularFirestoreDocument
} from "angularfire2/firestore";
import { Reacts } from "../models/reacts";
import { Observable } from "rxjs";
#Injectable({
providedIn: "root"
})
export class ReactService {
mapCollection: AngularFirestoreCollection<Reacts>;
reacts: Observable<Reacts[]>;
constructor(public afs: AngularFirestoreDocument) {
this.reacts = this.afs.collection("reacts").valueChanges();
}
getItems() {
return this.reacts;
}
}
component
import { Component, OnInit } from "#angular/core";
import { Reacts } from 'src/app/models/reacts';
import { ReactService } from 'src/app/services/react.service';
#Component({
selector: "app-reacts",
templateUrl: "./reacts.component.html",
styleUrls: ["./reacts.component.css"]
})
export class ReactsComponent implements OnInit {
react: Reacts[];
happy: number;
sad: number;
neutral:number;
constructor(private reactsService: ReactService ) {}
ngOnInit(): void {
this.reactsService.getItems().subscribe(reacts => {
this.react = reacts;
console.log(reacts); //this works print an array object of data from database
this.happy= reacts.happy// what i'm trying to achieve
});
}
}
Ok, I'll break it down for you. You are trying to access .happy but it is actually an array of React[]
ngOnInit(): void {
this.reactsService.getItems().subscribe((reacts:Reacts[]) => { // Note I have defined its model type
this.react = reacts;
console.log(reacts); //this works print an array object of data from database
//this.happy= reacts.happy // Now VS code will show you error itself
this.happy = reacts[0].happy;
});
}
The power of typscript comes as it is strongly typed language. If you'll make changes as below in service, the VS Code will itself explain you the error:
export class ReactService {
mapCollection: AngularFirestoreCollection<Reacts>;
reacts: Observable<Reacts[]>;
constructor(public afs: AngularFirestoreDocument) {
this.reacts = this.afs.collection("reacts").valueChanges();
}
getItems(): Observable<Reacts[]> { // added return type
return this.reacts;
}
}
Once I provide return type of getItems() , you dont even have to define type in .subscribe((reacts:Reacts[]) as I have done in your component.

Angular 4: Cannot instantiate cyclic dependency! InjectionToken_HTTP_INTERCEPTORS

I know, this question may sound duplicate and I have tried everything found on stackover flow unable to resolve this problem, so please bear with me
To make you able to reproduce the error I am providing you the whole code thought this
Github Repo
Problem
I am getting the following error:
Provider parse errors:↵Cannot instantiate cyclic dependency!
InjectionToken_HTTP_INTERCEPTORS ("[ERROR ->]"): in NgModule AppModule
in ./AppModule#-1:-1
Information about the scenario (Notes)
Note 1
File: response-interceptor.service.ts
Path: ./src/app/shared/interceptors/response-interceptor/
I am intercepting the HTTPClient responses to check the 401 error and when the error comes I need to ask user to re-login.
To show the re-login prompt to user I have made a global-functions-services that has a function 'relogin'
Note 2
File: global-function.service.ts
Path: ./src/app/shared/services/helper-services/global-function/
Here is the place where this all started to happen...
As soon as I am injecting the PersonService
constructor(
public dialog: MatDialog,
private _personService: PersonService
) { }
I am getting this error and in PersonService I cannot find any import that can cause the issue.
PersonService:
./src/app/shared/services/api-services/person/person.service.ts
import { IRequest } from './../../../interfaces/I-request';
import { environment } from 'environments/environment';
import { Injectable } from '#angular/core';
// for service
import 'rxjs/add/operator/map'
import 'rxjs/add/operator/toPromise';
// models
import { Person } from 'app/shared/models/person';
import { RequestFactoryService, REQUEST_TYPES } from 'app/shared/factories/request-factory/request-factory.service';
#Injectable()
export class PersonService {
private _requestService: IRequest;
constructor(
_requestFactoryService: RequestFactoryService
) {
this._requestService = _requestFactoryService.getStorage(REQUEST_TYPES.HTTP);
}
public signup(record): Promise<Person> {
let url = environment.api + 'person/public/signup';
return this._requestService.post(url, record) as Promise<Person>;;
}
public authenticate(code: string, password: string): Promise<Person> {
let url = environment.api + 'auth/signin';
let postData = {
code: code,
password: password
}
return this._requestService.post(url, postData) as Promise<Person>;
}
}
Request
Please suggest a solution for this, I have already wasted 2 days to figure out the issue but no luck.
Many thanks!! in advance
Cyclic dependency, means circling around endless, like planets orbiting sun..
Solution: Break the dependency chain, Re-factor code.
You have GlobalFunctionService -> PersonService -> so on... -> ResponseInterceptorService -> and back to -> GlobalFunctionService.
Cycle complete.
REMOVE the PersonService dependency from GlobalFunctionService. (its not used anyway, if you need it then find different way to get around.)
import { PersonService } from 'app/shared/services/api-services/person/person.service';
import { Injectable } from '#angular/core';
import { InputModalComponent } from 'app/shared/components/input-modal/input-modal.component';
import { MatDialog } from '#angular/material';
#Injectable()
export class GlobalFunctionService {
constructor(
public dialog: MatDialog
) { }
relogin(): void {
let dialogRef = this.dialog.open(InputModalComponent, {
width: '250px',
data: { title: "Please provide your password to re-login." }
});
dialogRef.afterClosed().subscribe(result => {
debugger
console.log('The dialog was closed');
let password = result;
});
}
}
Use setTimeout() function in constructor to assign service.
constructor(private injector: Injector) {
setTimeout(() => {
this.loginService = this.injector.get(LoginService);
})
}
Try this and revert back if you face any issue.
You have to modify your response-interceptor.service.ts
import { Injectable,Inject, Injector } from '#angular/core';
constructor( inj: Injector) {
this._globalFunctionService=inj.get(GlobalFunctionService)
}
You can get more info From this link

Angular 4: router.navigate() does not ensure to stop execution rest of the codes

I am looking for a better way of handling router.navigate(['/some-route']).
My code is like below
class SomeComponent implements OnInit, AfterViewInit {
ngOnInit() {
...
router.navigate(['/some-route']);
...
console.log('This line will be logged.');
}
ngAfterViewInit() {
...
console.log('This line will also be logged.');
...
}
}
In the above code, both the console.log will be logged though navigation is called.
I know that it happens as router.navigate() is just a function and not a termination statement and behaves according to JavaScript behavior.
My exception is, none of the logs should be in Browser Console.
Can anyone help me with better solution to handle the above scenario so that I can ensure rest of the code is not being executed after navigation?
ngAfterViewInit will be always executed,You may want to use CanActivate guard , so try somthing like this :
import { Injectable } from '#angular/core';
import { CanActivate, Router } from '#angular/router';
import { AuthService } from './auth.service';
#Injectable()
export class SomeComponentGaurd implements CanActivate {
constructor(private router :Router) {}
canActivate() {
if( /** some condition*/){
this.router.navigate(['/some-route']);
// will access to the component
return false;
}else{
// won't access to the component
return true;
}
}
}
routing config :
export const AppRoutes:RouterConfig = [
{
path: 'some-cmp',
component: SomeComponent,
canActivate: [SomeComponentGaurd]
}
];
this will prevent ngOnInit from being executed.
A simple work around without using a AuthGuard:
class SomeComponent implements OnInit, AfterViewInit {
private getOut : boolean = false;
ngOnInit() {
...
if(/** some condition*/){
this.getOut = true;
return router.navigate(['/some-route']); // don't forget to return
}
...
console.log('This line will be logged.');
}
ngAfterViewInit() {
...
if(this.getOut){
return;
}
console.log('This line will also be logged.');
...
}
}
this should work too, but is not a good practice.

How to refactor component into using service Angular 2

Recently ive made an angular 2 todo app that is working, however im not using a service for this app, and ive heard that using a service is the way to go. But i am not entirely sure how i refactor my code so that i can push data into my service instead.
My component:
import { Component } from '#angular/core';
import { Todo } from './todo';
import { TODOS } from './mock-todos';
import { TodoService } from './todo.service';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.sass'],
providers: [TodoService]
})
export class AppComponent {
title = 'Todo List';
selectedTodo: Todo;
completed = false;
constructor(private todoService: TodoService){
}
onSelect(todo: Todo): void {
this.selectedTodo = todo;
}
addTodo(value: any) {
this.todoService.addTodo(value);
console.log(value);
}
deleteTodo(todo) {
this.todos.splice(todo,1);
console.log("This todo has been deleted"+ todo);
}
completedTodo(todo){
todo.isCompleted = !todo.isCompleted;
todo.completed = !todo.completed;
}
}
My Service:
import { Injectable } from '#angular/core';
import { Todo } from './todo';
#Injectable()
export class TodoService {
todos: Todo[] = [];
lastId: number = 0;
constructor() { }
addTodo(value: any) {
this.todos.push(value);
console.log("This was pushed");
}
}
I thought i was able to use the service to push my data there , instead of having the component to handle this. So the service can be used for other components.
I would be happy to get a reply to this.
Instead of performing actions on variable in component, you can instead store your todos in the service, and when you want to make changes to your array, you just call the service functions. This is pretty well covered in the Services tutorial in the official docs, but just to throw in a short example for getting and adding todos:
In component, get the todos in OnInit and store in local variable.
ngOnInit() {
this.todos = this.todoService.getTodos()
}
The adding of a todo, call the service to do the adding.
addTodo(todo) {
this.todoService.addTodo(todo)
}
Your TodoService looks codewise totally right, so you were almost all there with your code :)

angular 2 subscribe shareService working twice or several times

I have a ShareService in angular 2,
******************************shareService*************************
import { BehaviorSubject , Subject} from 'rxjs/Rx';
import { Injectable } from '#angular/core';
#Injectable()
export class shareService {
isLogin$:BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
CheckUser = this.isLogin$.asObservable();
public isLogin (bool){
this.isLogin$.next(bool);
}
}
and its my another component and subscibe the CheckUser;
***********************another Component*******************************
_shareService.CheckUser.subscribe((val) =>{
*********all of this scope execute for several times just i have one another component and one next function*******
this.isLogin = val;
alert(val);
if(this.isLogin){
console.log("req req req");
this.MyBasket();
}
else if(this.ext.CheckLocalStorage("ShopItems")){
this.ShopItems = JSON.parse(localStorage.getItem("ShopItems"));
setTimeout(() => {
_shareService.sendShopItems(this.ShopItems);
},100);
}
});
my problem is i execute once this.isLogin$.next(bool) but subscribe function execute twice or several times !!!! my basket function is an xhr request this means when user loged in i get the several request to server!!!i cant fix it...i dont know this problem is for angular 2 or not,Anyone have this problem??
last a few days i Involved in this problem!
The problem is that your shareService is getting multiple instances.
One of the solutions is forcing the service to be a singleton.
Something like this should work:
import { provide, Injectable } from '#angular/core';
#Injectable()
export class shareService {
private static instance: shareService = null;
// Return the instance of the service
public static getInstance(/*Constructor args*/): shareService {
if (shareService.instance === null) {
shareService.instance = new shareService(/*Constructor args*/);
}
return shareService.instance;
}
constructor(/*Constructor args*/) {}
}
export const SHARE_SERVICE_PROVIDER = [
provide(shareService, {
deps: [/*Constructor args dependencies*/],
useFactory: (/*Constructor args*/): shareService => {
return shareService.getInstance(/*Constructor args*/);
}
})
];
Everything that is required on your current constructor should be placed where it says constructor args
Now on your components you use the service like this:
#Component({
providers: [SHARE_SERVICE_PROVIDER]
})
And then you can call it like you usually do.
Another solution would be injecting your current service on the main component of the app. See here for more info.
The problem is that the service is singleton and the component subscribe to it each time it created or (I don't see the full code) at the point the
_shareService.CheckUser.subscribe
is placed , so CheckUser should be a method that returns an Observable . if you have plunkr I can edit it .
Another semantic problem is that the observable should end with $ and not the BehaviorSubject.

Categories