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 :)
Related
I'm getting the below error in my console when trying to run my Angular application. Here is my html code if you need it https://stackblitz.com/edit/angular-xrbrjq?file=src%2Fapp%2Fapp.component.html
Error: Can't resolve all parameters for HomeInnerComponent: (?).
Please have a look below for my component details.
Home-inner.component.ts:
import { Component, OnInit } from '#angular/core';
import {CategoriesService } from '../../Admin/categories/categories.service';
import {Categories } from '../../Admin/categories/categories';
#Component({
selector: 'app-home-inner',
templateUrl: './home-inner.component.html',
styleUrls: ['./home-inner.component.css']
})
export class HomeInnerComponent implements OnInit {
leftCat:Categories;
constructor(private leftCategoriesService: CategoriesService) { }
ngOnInit() {
// For showing category list in the left side
this.leftCategoriesService.getCategories()
.subscribe((data: any) => {
this.leftCat = data;
//console.log(this.leftCat);
// localStorage.removeItem('editEmpId');
});
// Left side category list ends here
}
}
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.
I created an Account Service, for my angular application, and it handles the Login and logout. and this works perfectly. But I am having an issue, I used BehaviourSubject Observables to render the variables.
I am trying to retrieve the loginstatus value, and the username string on the component using the service, but the observable is returning an object, and I am having problems extracting the string out of the object. How can I extract variable types from Behavioursubject observables?
The Account Service...
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { Observable, Subject, BehaviorSubject } from 'rxjs';
import { map } from 'rxjs/operators';
import { Router } from '#angular/router';
#Injectable({
providedIn: 'root'
})
export class AccountService {
private baseUrlLogin:string = "/api/account/login";
private loginStatus = new BehaviorSubject<boolean>
(this.checkLoginStatus());
private userName = new BehaviorSubject<string> localStorage.getItem['username']);
constructor(
private http:HttpClient,
private router: Router
){}
login(username:string, password:string){
return this.http.post<any>(this.baseUrlLogin,{username, password}).pipe(
map(result => {
if(result && result.token){
localStorage.setItem('loginStatus', '1');
localStorage.setItem('username', result.username),
}
return result;
})
);
}
logout(){
this.loginStatus.next(false);
localStorage.setItem('loginStatus', '0');
localStorage.removeItem('username'),
localStorage.clear();
//now redirect to the login page...
this.router.navigate(['/login']);
console.log("logged out successfully...");
}
get isLoggedIn(){
return this.loginStatus.asObservable();
}
get currentUserName(){
return this.userName.asObservable();
}
}
The Component Using the Service
import { Component, Input, OnInit } from '#angular/core';
import { AccountService } from 'src/app/services/account.service';
import { Observable } from 'rxjs';
#Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {
pgtitle:string = "SCB Dashboard";
loginStatus$ : Observable<boolean>;
username$ : Observable<string>;
constructor(
private acc:AccountService
){}
ngOnInit() {
this.loginStatus$ = this.acc.isLoggedIn;
this.username$ = this.acc.currentUserName;
console.log(this.loginStatus$); //here it ruturns an object
console.log(this.username$); //and here too...
}
}
The console.log() returns an object, but how do I retrieve the variables, and work with them in the controller, since they are of type observable?
Rxjs BehaviourSubject has an asObservable() method, you can generate your observable from it
let sourceSubject = new BehaviourSubject();
let source$ = sourceSubject.asObservable();
source$.subscribe(result => // Your data)
// Update the BehaviourSubject
sourceSubject.next(newValue);
You need to subscribe to the observable to get the value out of it:
this.loginStatus$.subscribe(value => {
console.log(value); // access value
});
try this:
get isLoggedIn(){
return this.loginStatus.value;
}
get currentUserName(){
return this.userName.value;
}
This should also work:
ngOnInit() {
this.loginStatus$ = this.acc.isLoggedIn.pipe(
tap(status => console.log(status))
);
this.username$ = this.acc.currentUserName.pipe(
tap(userName => console.log(userName))
);
}
Assuming that you subscribed somewhere, such as with an async pipe.
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
So I'm trying to toggle an element in a separate component, while firing the function to change the boolean value from the ionic tabs component so far I have this.
//App.module.ts - Cut down for the sake of brevity
import { AppGlobals } from './globals';
#NgModule({
providers: [AppGlobals]
})
//Globals.ts
import { Injectable } from '#angular/core';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/share';
import 'rxjs/add/operator/startWith';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
#Injectable()
export class AppGlobals {
// use this property for property binding
public showSearch:BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
setShowSearch(search){
this.showSearch.next(search);
console.log(search);
}
}
// Tabs.ts
import { AppGlobals } from '../../app/globals';
constructor(private _appGlobals: AppGlobals) {
this._appGlobals.showSearch.subscribe(value => this.search = value);
}
toggleSearch() {
this.search = !this.search;
console.log(this.search);
}
//Tabs.html
(ionSelect)="toggleSearch();"
//This is on some HTML within the the separate component
<div *ngIf="search" [ngClass]="{'slideInRight':showSearch, 'fadeOut':!showSearch}" class="search-filters animated">
However this doesn't appear to be working, I'm toggling the value however the global "showSearch" seems to stay the same. What is the correct way of achieving the toggle of the element across the two components?
Any help at all is very appreciated.
your Tabs.ts will emit an event when toggleSearch() method call
toggleSearch() {
this.search = !this.search;
console.log(this.search);
this._appGlobals.showSearch.next(this.search);
}
in another component
import { AppGlobals } from 'path/to/app/globals';
#Component({...})
export class SubComponent implements OnInit {
showSearch: boolean = false;
constructor(private _appGlobals: AppGlobals) {
}
ngOnInit() {
this._appGlobals.showSearch.subscribe(value => this.showSearch = value);
}
}
SubComponent template
<div *ngIf="showSearch">content</div>