SERVICE--
import {Injectable} from '#angular/core';
import {UserData} from '../user-data/user-data.component';
#Injectable()
export class UserDataService {
constructor(){}
userdata:UserData[];
getData(){
console.log('service',this.userdata);
return this.userdata;
}
setData(user:any){
this.userdata=user;
console.log(this.userdata);
}
}
USER-DATA-class ---
export class UserData {
firstname: string;
middlename: string;
lastname: string;
email: string;
contact: number;
}
Component1 --
import { Component,OnInit,OnDestroy } from '#angular/core';
import { UserData } from '../../user-data/user-data.component';
import { ViewEditUser } from '../../view-edit-user/view-edit-user.component';
import {UserDataService} from '../../user-data-service/user-data-service.service';
#Component({
selector: 'form-page',
templateUrl: `app/add-user-sidebar/user-form/form.component.html`,
providers:[UserDataService]
})
export class Form implements OnInit,OnDestroy {
userdetail:UserData;
constructor(private service:UserDataService){
}
addUser(first:string,middle:string,last:string,emailid:string,contactno:number){
this.userdetail=({firstname:first,middlename:middle,lastname:last,email:emailid,contact:contactno})
console.log(this.userdetail);
this.service.setData(this.userdetail);
}
ngOnInit(){
}
ngOnDestroy(){
}
}
Component2--
import { Component,Input, OnInit } from '#angular/core';
import { Form } from '../add-user-sidebar/user-form/form.component';
import {UserData} from '../user-data/user-data.component';
import { WelcomePage } from '../welcome-page/welcome-page.component';
import {UserDataService} from '../user-data-service/user-data-service.service';
#Component({
selector:'view-edit',
templateUrl: 'app/view-edit-user/view-edit-user.component.html',
providers: [UserDataService]
})
export class ViewEditUser implements OnInit {
arraydata:any;
constructor(private service:UserDataService){}
// arraydata:any;
printarray(){
console.log(this.arraydata);
}
ngOnInit()
{
this.arraydata=this.service.getData();
console.log("hhghdfghdf",this.arraydata);
}
}
I am new to angular2, I have two components in my module, one component is a form where user inputs data, that data is then sent to a service, when I console.log it then I can see the data in service. but when I try to access that array from the second component then I can't access the data what to do?
If you provide the service on each component, you can't use it for communication, because each component will get a different service instance.
If one component is a parent (ancestor) of the other component, only provide it on the parent component.
Otherwise provide it on a component that is a parent (anjestor) of both or provide it only in #NgModule() to make the service global.
You also need to be aware that it's possible that one component reads, before the other set the value, depending on where you set the value and in what order the components are created.
Using a BehaviorSubject usually avoids this pitfall, because this way it doesn't matter which component is created first or if one component tries to read, while the other hasn't set the value yet.
For shareing between to Angular instances see also How to share service between two modules - #NgModule in angular2?
You nee to use observables to pass data between components.
In your service create a Subject type variable and in the your first component do a .next to pass data to the service and in your 2nd component, subscribe to the service veriable and it will get you the data.
You are not getting the data because of the async behavior of JavaScript which will be handled by observables
Related
I'm trying to create a service to share the data between two components. I injected the service into root module to make it accessible throughout the application by doing DI into the root module provider. My code looks roughly like this.
Service
#Injectable(){
export class ForumService{
forum: any;
setForum(object){
this.forum = object;
}
getForum(){
return this.forum;
}
}
Root Module
.......
import { ForumService } from 'forumservice';
.......
#NgModule({
declarations: [.....],
imports: [.....],
providers: [....., ForumService],
bootstrap: [AppComponent]
})
export class AppModule{}
Component One
//A bunch of import statements
import { ForumService } from 'forumservice'; //Without this Angular throws a compilation error
#Component({
selector: 'app-general-discussion',
templateUrl: './general-discussion.component.html',
styleUrls: ['./general-discussion.component.css'],
providers: [GeneralDiscussionService] //Not injecting ForumService again
})
export class GeneralDiscussionComponent implements OnInit{
constructor(private forumService: ForumService){}
ngOnInit(){
helperFunction();
}
helperFunction(){
//Get data from backend and set it to the ForumService
this.forumService.forum = data;
console.log(this.forumService.forum); //prints the data, not undefined
}
}
Component Two
//A bunch of import statements
import { ForumService } from 'forumservice'; //Without this Angular throws a compilation error
#Component({
selector: 'app-forum',
templateUrl: './forum.component.html',
styleUrls: ['./forum.component.css'],
providers: []
})
export class ForumComponent implements OnInit {
forumData: any;
constructor(private forumService: ForumService){}
ngOnInit(){
this.forumData = this.forumService.forum; // returns undefined
}
}
Once I navigate from Component One to Component Two I'm expecting "This is a string". However I get undefined. Is it because of the import statements in the component? If I remove that I see a compilation error saying that ForumService is not found.
Instead of using getter and setter, use the object (not primitibe such as string) directly In your components.
Your service
#Injectable(){
export class ForumService{
forum:any = {name:string};
}
Component one
export class GeneralDiscussionComponent implements OnInit{
constructor(private forumService: ForumService){}
ngOnInit(){
this.forumService.forum.name="This is a string";
}
}
Component two
export class ForumComponent implements OnInit {
// forumTitle: string; // do not need this anymore
forum:any; // use the forum.name property in your html
constructor(private forumService: ForumService){}
ngOnInit(){
this.forum = this.forumService.forum; // use the
}
}
I know encapsulating is preferable, and with your current code you are probably encountering some timing problems. But when working with shared data in a service you can two-way bind the variable like above, and your components will be in sync.
EDIT:
Also an important notice, the variable you want to sync between components needs to be an object. instead of forumTitle:string, make it forumTitle:any = {subject:string} or something similar.
Otherwise you need to make your components as listeners for data when data changes in your service.
I'd use BehaviorSubject in this case, should be something like that:
#Injectable(){
export class ForumService{
private _forum: BehaviorSubject<any> = new BehaviorSubject<any>(null);
public forum: Observable<any> = this._forum.asObservable();
setForum(object){
this._forum.next(object);
}
}
Then just bind it in template with async pipe: {{forumService.forum|async}} or subscribe to it.
I've html structure like this :
Parent Component where both Comp1 and Comp2 resides :
now in comp1 I've some elements if that changes then I've to reflect values in comp2 but there's no connection between them.
Comp1 :
import { Component } from '#angular/core';
import { Comp2Component } from 'comp2.component';
#Component({
selector: 'comp1',
templateUrl: './comp1.html'
})
export class Comp1Component {
sortBy(value)
{
this.FeedSortBy = value;
this.SortByLabel = this.SortByLabelList[value];
Comp2Component.filterActivities();
}
}
Comp2
import { Component } from '#angular/core';
#Component({
selector: 'comp2',
templateUrl: './comp2.html'
})
export class Comp2Component {
filterActivities()
{
//call this function on comp1 sort function call so it will update value of comp2
}
}
As per Rahul and Sajit's answer I try using with EventEmitter and change my structure to parent child :
In my parent component I use :
import { Component,EventEmitter, Input, Output, OnInit } from '#angular/core';
import { FeedsComponent } from '../feeds/feeds.component';
#Component({
selector: 'my-desk',
styleUrls: ['../../assets/css/style.min.css'],
templateUrl: './my-desk.component.html'
})
export class MyDeskComponent {
#Output() FeedSortBy = new EventEmitter<string>();
sortBy(value)
{
this.FeedSortBy.emit(value);
}
}
and in my child component I use :
import { Component, OnInit, Input, Output } from '#angular/core';
import { DataService } from '../data.service';
declare var $: any;
#Component({
selector: 'feeds',
styleUrls: ['../../assets/css/style.min.css'],
templateUrl: './feeds.component.html'
})
export class FeedsComponent {
constructor(private dataService:DataService)
{
}
#Input() FeedSortBy:number = 2;
}
Child component HTML :
{{FeedSortBy}}
But it always output 2 it doesn't change can I get any trigger as well to know if value is change so I call function there
You cannot do that, There are two possible ways you could achieve this,
use angular service to pass the data between two components
use Event Emitters to pass the value among the components.
You can call method of another component from a different component but it will not update the value of the calling component without some tweaking like
Event Emitters if they have a parent child relationship or Shared Services or using ngrx redux pattern
How to Call a different component method be like
Component1
test(){
console.log("Test");
}
Component 2
working(){
let component = new Component1();
component.test();
}
Now to update the value in component 2 you might have to use any of the above.
For Event Emitters follow this link
For Shared services follow this link
For ngrx follow this link
I am trying to pass user info object to all low level component,
the issue is what is the best way to pass it to lover component even if they are grandchildren?
If the #input will work or have anther way to pass it?
my code for root component is:
constructor(private _state: GlobalState,
private _imageLoader: BaImageLoaderService,
private _spinner: BaThemeSpinner, public af: AngularFire) {
this._loadImages();
this._state.subscribe('menu.isCollapsed', (isCollapsed) => {
this.isMenuCollapsed = isCollapsed;
});
// this.af.auth.subscribe(
// user => this._changeState(user),
this.af.auth.subscribe( user => this._changeState(user));
}
Have you considered creating a service class? They're singletons, so the same instance of that class gets injected into each and every component that asks for it.
https://angular.io/docs/ts/latest/guide/dependency-injection.html
A simple one would look like this
import { Injectable } from '#angular/core';
#Injectable()
export class DummyService {
public userInfo: UserInfo = {
//User stuff goes here
};
}
And you would add it to a component like this.
import {DummyService} from 'dummy.service';
#Component({
selector: 'my-component',
templateUrl: 'my.component.html'
})
export class MyComponent{
constructor(private myDummyService: DummyService){}
}
At runtime, this would inject the same instance of the class into every component you inject it into. So it's a super handy way of syncronizing data across multiple components.
I have a PermissionService, which provide user roles. At the server-side data will not be uploaded if the user is not corresponds on role. The back-end is written with asp.net web api, which will use attributes to secure data. On upload page will be static upload user roles, the idea is just to show or hide elements on page which depending from user role.
The PermissionsService check avaiable role in its array. There are methods like isSeller(), isManager(). And all what i want is to provide accessibility from each view. For now i have this implementation.
permission.service
import { Injectable } from "#angular/core";
export enum Roles {
Admin,
Manager,
Moderator,
Seller
}
interface IPermissionDictionary {
[key: string]: boolean;
}
#Injectable()
export class PermissionService {
private permissions: IPermissionDictionary = {};
public constructor() {
this.emitPermissions();
}
private emitPermissions(): void {
let selector = document.querySelectorAll("#roles > span");
let availableRoles = Array.from(selector).map(element => element.textContent);
for (let role in Roles) {
if (!/^\d+$/.test(role)) { // for strings types in Roles
this.permissions[role] = availableRoles.indexOf(role) > -1;
}
}
}
public isInRole(role: string): boolean {
return this.permissions[role];
}
public isAdmin() {
return this.isInRole(Roles[Roles.Admin]);
}
public isSeller() {
return this.isInRole(Roles[Roles.Seller]);
}
public isManager() {
return this.isInRole(Roles[Roles.Manager]);
}
public isModerator() {
return this.isInRole(Roles[Roles.Moderator]);
}
}
app.component
import { Component } from "#angular/core";
import { ROUTER_DIRECTIVES } from "#angular/router";
import { PermissionService } from "./share/permission.service";
import { HomeComponent } from "./home/home.component";
import { OrderComponent } from "./order/order.component";
#Component({
selector: "admin-panel",
templateUrl: "../app/app.template.html",
directives: [ROUTER_DIRECTIVES],
precompile: [HomeComponent, OrderComponent]
})
export class AppComponent {
constructor(private permissionService: PermissionService) {
}
}
main.ts
import { bootstrap } from "#angular/platform-browser-dynamic";
import { AppComponent } from "./app.component";
import { APP_ROUTES_PROVIDER } from "./app.routes";
import { HTTP_PROVIDERS } from '#angular/http';
import { PermissionService } from "./share/permission.service";
bootstrap(AppComponent, [APP_ROUTES_PROVIDER, HTTP_PROVIDERS, PermissionService]);
For now to access the method of PermissionService need to inject it in component constructor. And in template is is use like
<div *ngIf="permissionService.isAdmin()">will show if you are admin</div>
But every time to inject my service in each component where i want to use it seems for me strange. And i just want to get access it from every part of my app like:
<div *ngIf="isAdmin()">will show if you are admin</div>
I think the person who asked this question has another version of Angular2 (perhaps a pre-release?), but in the latest version if you need to export a service for all the app you do it in the following way.
First, in your main.ts you must have a class that you bootstrap, like this:
platformBrowserDynamic().bootstrapModule(AppModule);
In this class "AppModule" (or whatever it is in your case), you should be able to add a global service provider in this way:
...
import {GlobalService} from './global-service.service'
#NgModule({
...
providers: [MyGlobalService],
...
})
export class AppModule{ ...}
In this way MyGlobalService is available for all other components.
Hopefully this will be useful to someone :).
Some option could be to create top level super class with the permission methods and then just subclass in view .ts. Not sure if this suits you as you still need to import super class into your components and extend it. It can also violate the "is-a".
I'm trying to play with Angular 2-beta and I want to work with Http component. But there is a serious problem here:
I read this and
I know in Angular 2(Unlike Angular 1), Http component is not a service that returns a Promise. It returns something called Observable. We know that a Component is better not to use Http directly. Efficient way is to make a service that is responsible to consume Http. But how?! Should this after completing a request, return a promise? (look at here)
Does it make sense at all?!
It's possible with Angular 2 to implement services. They simply correspond to injectable classes as described below. In this case this class can be injected into other elements like components.
import {Injectable} from 'angular2/core';
import {Http, Headers} from 'angular2/http';
import 'rxjs/add/operator/map';
#Injectable()
export class CompanyService {
constructor(http:Http) {
this.http = http;
}
}
You can inject an Http object in it (using its constructor) at the condition you specified HTTP_PROVIDERS when bootstraping the main component of your application:
import {bootstrap} from 'angular2/platform/browser'
import {HTTP_PROVIDERS} from 'angular2/http';
import {AppComponent} from './app.component'
bootstrap(AppComponent, [
HTTP_PROVIDERS
]);
This service can be then injected into a component, as described below. Don't forget to specify it within the providers list of the component.
import { Component, View, Inject } from 'angular2/core';
import { CompanyService } from './company-service';
#Component({
selector: 'company-list',
providers: [ CompanyService ],
template: `
(...) `
})
export class CompanyList {
constructor(private service: CompanyService) {
this.service = service;
}
}
You can then implement a method leveraging the Http object in your service and return the Observable object corresponding to your request:
#Injectable()
export class CompanyService {
constructor(http:Http) {
this.http = http;
}
getCompanies() {
return this.http.get('https://angular2.apispark.net/v1/companies/')
.map(res => res.json());
}
}
The component can then call this getCompanies method and subscribe a callback on the Observable object to be notify when the response is there to update the state of the component (in the same way you did with promises in Angular1):
export class CompanyList implements OnInit {
public companies: Company[];
constructor(private service: CompanyService) {
this.service = service;
}
ngOnInit() {
this.service.getCompanies().subscribe(
data => this.companies = data);
}
}
Edit
As foxx suggested in his comment, the async pipe could be also used to implicitly subscribe on the observable object. Here is the way to use it. First update your component to put the observable object in the attribute you want to display:
export class CompanyList implements OnInit {
public companies: Company[];
constructor(private service: CompanyService) {
this.service = service;
}
ngOnInit() {
this.companies = this.service.getCompanies();
}
}
Use then the async pipe in your template:
#Component({
selector: 'company-list',
providers: [ CompanyService ],
template: `
<ul>
<li *ngFor="#company of companies | async">{{company.name}}</li>
</ul>
`
})
export class CompanyList implements OnInit {
(...)
}
This article in two parts could give more details as well:
http://restlet.com/blog/2015/12/30/implementing-an-angular-2-frontend-over-an-apispark-hosted-web-api-part-1/
http://restlet.com/blog/2016/01/06/implementing-an-angular-2-frontend-over-an-apispark-hosted-web-api-part-2/
Hope it helps you,
Thierry
There is no need to convert the observable returned by Http's get() method into a promise. In most cases, the service can simply return the observable.
If we are fetching an array or a primitive type (i.e., string, number, boolean) from the server, we can simplify our controller logic by using the returned observable directly in our template, with the asyncPipe. This pipe will automatically subscribe to the observable (it also works with a promise) and it will return the most recent value that the observable has emitted. When a new value is emitted, the pipe marks the component to be checked for changes, hence the view will automatically update with the new value.
If we are fetching an object from the server, I'm not aware of any way to use asyncPipe, we could use the async pipe, in conjunction with the safe navigation operator as follows:
{{(objectData$ | async)?.name}}
But that looks complicated, and we'd have to repeat that for each object property we wanted to display.
Instead, I suggest we subscribe() to the observable in the component and store the contained object into a component property. We then use the safe navigation operator (?.) or (as #Evan Plaice mentioned in a comment) NgIf in the template. If we don't use the safe navigation operator or NgIf, an error will be thrown when the template first tries to render, because the object is not yet populated with a value.
Note how the service below always returns an observable for each of the get methods.
service.ts
import {Injectable} from 'angular2/core';
import {Http} from 'angular2/http';
import 'rxjs/add/operator/map'; // we need to import this now
#Injectable()
export class MyService {
constructor(private _http:Http) {}
getArrayDataObservable() {
return this._http.get('./data/array.json')
.map(data => data.json());
}
getPrimitiveDataObservable() {
return this._http.get('./data/primitive.txt')
.map(data => data.text()); // note .text() here
}
getObjectDataObservable() {
return this._http.get('./data/object.json')
.map(data => data.json());
}
}
app.ts
import {Component} from 'angular2/core';
import {MyService} from './my-service.service';
import {HTTP_PROVIDERS} from 'angular2/http';
#Component({
selector: 'my-app',
providers: [HTTP_PROVIDERS, MyService],
template: `
<div>array data using '| async':
<div *ngFor="#item of arrayData$ | async">{{item}}</div>
</div>
<div>primitive data using '| async': {{primitiveData$ | async}}</div>
<div>object data using ?.: {{objectData?.name}}</div>
<div *ngIf="objectData">object data using NgIf: {{objectData.name}}</div>`
})
export class AppComponent {
constructor(private _myService:MyService) { console.clear(); }
ngOnInit() {
this.arrayData$ = this._myService.getArrayDataObservable();
this.primitiveData$ = this._myService.getPrimitiveDataObservable();
this._myService.getObjectDataObservable()
.subscribe(data => this.objectData = data);
}
}
Note: I put "Observable" in the service method names – e.g., getArrayDataObervable() – just to highlight that the method returns an Observable. Normally you won't put "Observable" in the name.
data/array.json
[ 1,2,3 ]
data/primitive.json
Greetings SO friends!
data/object.json
{ "name": "Mark" }
Output:
array data using '| async':
1
2
3
primitive data using '| async': Greetings SO friends!
object data using .?: Mark
object data using NgIf: Mark
Plunker
One drawback with using the async pipe is that there is no mechanism to handle server errors in the component. I answered another question that explains how to catch such errors in the component, but we always need to use subscribe() in this case.