I have an issue with my Angular code. I want to call a function of the Parent component, from the Nested component which is created by the Component() Angular function.
Please see my code below:
import {
Compiler, Component, ComponentRef, Injector, NgModule, NgModuleRef, ViewChild, ViewContainerRef
} from '#angular/core';
import { ActivatedRoute } from "#angular/router";
import { FormsModule, NgForm } from "#angular/forms";
import { HttpService } from './ht.service';
#Component({
selector: 'app-module-routing',
templateUrl: './module-routing.component.html'
})
export class ModuleRoutingComponent {
#ViewChild('vc', {read: ViewContainerRef}) vc: ViewContainerRef;
private cmpRef: ComponentRef<any>;
private module_name: string;
private module_link: any;
private module_links = [
{"access": "salessalquoent", "link": "sales\/sales_order_entry.php?NewQuotation=Yes"},
];
constructor(private compiler: Compiler,
private injector: Injector,
private m: NgModuleRef<any>,
private route: ActivatedRoute,
private httpService: HttpService) {}
test() { //<-- Want to Call this function
alert('function called successfully');
}
printPage(template, httpService, link) {
const styles = [`input { width: 100px, display: block }`];
const tmpCmp = Component({ template, styles }) (
class {
OnSubmit(form: NgForm) {
console.log(form);
this.ModuleRoutingComponent.test(); //<-- Want to call Parent Component function here
httpService.getPage(link, form.value)
.subscribe(
data => this.instance.printPage(data, httpService, link)
);
return false;
};
}
);
const tmpModule = NgModule({
imports: [FormsModule],
declarations: [tmpCmp]
})(class {});
this.compiler.compileModuleAndAllComponentsAsync(tmpModule)
.then((factories) => {
const f = factories.componentFactories[0];
this.cmpRef = f.create(this.injector, [], null, this.m);
this.cmpRef.instance.name = 'someForm';
this.vc.insert(this.cmpRef.hostView);
});
}
}
Please see my comments within code, you will see what I actually mean:
//<-- Want to Call this function
and
//<-- Want to call Parent Component function here
Related
I'm trying to have a navbar with categories and a home component that also uses those categories. I don't want to have to call my API twice and I will use that same categories variable in other places. I tried doing the following:
Data Service
This service gets the data from the api url and returns the subscribable object.
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Injectable({
providedIn: 'root'
})
export class DataService {
api_url: string = "https://apiurlhere";
categories: Object;
constructor(private http: HttpClient) { }
getCategories(){
return this.http.get(this.api_url+'/categorylisting/?z=1');
}
getZones(){
return this.http.get(this.api_url+'/zones/');
}
}
Navbar Component
The Navbar component makes use of the categories variable to show the different options, this works fine since the subscribe is in this component.
import { Component, OnInit } from '#angular/core';
import { trigger, state, transition, animate, style } from '#angular/animations';
import { DataService } from '../data.service';
#Component({
selector: 'app-navbar',
templateUrl: './navbar.component.html',
styleUrls: ['./navbar.component.scss'],
animations: [
trigger('slideInOut', [
state('in', style({
overflow: 'hidden',
height: '*'
})),
state('out', style({
overflow: 'hidden',
height: '0px'
})),
transition('in => out', animate('400ms ease-in-out')),
transition('out => in', animate('400ms ease-in-out'))
])
]
})
export class NavbarComponent implements OnInit {
categories: Object;
constructor(private data:DataService) { }
ngOnInit() {
this.data.getCategories().subscribe( data => {
this.categories = data
for(let category in this.categories){
this.categories[category].productsOpen='out';
for(let product in this.categories[category].product){
this.categories[category].products[product].active = false;
}
}
this.data.categories = this.categories;
});
}
openProducts(index){
this.categories[index].productsOpen = this.categories[index].productsOpen === 'out' ? 'in' : 'out';
}
setActiveProduct(index, productIndex){
for(let category in this.categories){
for(let product in this.categories[category].products){
this.categories[category].products[product].active = false;
}
}
this.categories[index].products[productIndex].active = true;
}
}
Home Component
My Home component also makes use of the categories variable, so I want to know how I can get it here since it is always undefined even if it changes in the service.
import { Component, OnInit } from '#angular/core';
import { DataService } from '../data.service';
#Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit {
categories: Object;
constructor(private data:DataService) { }
ngOnInit() {
this.categories = this.data.categories;
}
}
Am I doing this right? I'm used to react and redux and in there the render method would run every time a setState was called to change the state, when does angular know when the component's variables have changed? I just want to save a global variable with my data so I can reuse it without calling the API every time. Thank You.
You can cache the observable in your service like:
export class DataService {
someProperty;
api_url: string = "https://apiurlhere";
categories: Object;
constructor(private http: HttpClient) { }
getCategories(){
if(!this.someProperty) {
this.someProperty = this.http.get(this.api_url+'/categorylisting/?z=1');
}
return this.someProperty;
}
}
You can also go for angular Http interceptors else you can also opt for rxjs operator shareReplay
You could try calling your API in the constructor of the DataService
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Injectable({
providedIn: 'root'
})
export class DataService {
api_url: string = "https://apiurlhere";
categories: Object;
constructor(private http: HttpClient) {
this.getCategories().subscribe(data => this.categories = data);
}
getCategories(){
return this.http.get(this.api_url+'/categorylisting/?z=1');
}
getZones(){
return this.http.get(this.api_url+'/zones/');
}
}
and then just get the categories in the NavBar component just as you did in the Home component.
ngOnInit() {
this.categories = this.data.categories;
}
I don't get i, how to communicate between components and services.. :(
I have read and tried a lot about even if some examples somehow work, I do not understand why (?)
what I want to achieve:
I have one parent and two child-components:
dashboard
toolbar
graph
in the toolbar-component I have a searchfield, which gets it's result from a external source (works via service).. when the result arrives, I need to trigger the updateGraph()-Method in the graph-component
toolbar.component.ts
import { Component, OnInit, Output, EventEmitter } from '#angular/core';
import { FormControl } from '#angular/forms';
import { WebsocketsService } from '../../../services/websockets/websockets.service';
import { DataService } from '../../../services/data/data.service';
#Component({
selector: 'toolbar',
templateUrl: './toolbar.component.html',
styleUrls: ['./toolbar.component.scss'],
providers: [WebsocketsService, DataService]
})
export class ToolbarComponent implements OnInit {
#Output() newGraphData: EventEmitter<boolean> = new EventEmitter();
searchField: FormControl;
search: string;
private isNewGraph = false;
constructor(private _websocketsService: WebsocketsService, private _dataService: DataService) {
}
ngOnInit() {
this.searchField = new FormControl();
this.searchField.valueChanges
.subscribe(term => {
this.search = term;
});
}
private applySearch() {
const res = this._websocketsService.sendQuery(this.search);
this._dataService.setGraphData(res);
this.newGraphData.emit(true);
this.search = '';
this.searchField.reset();
}
}
graph-component.ts
import { Component, OnInit} from '#angular/core';
import { HttpService } from '../../../services/http/http.service';
import { DataService } from '../../../services/data/data.service';
#Component({
selector: 'graph',
templateUrl: './graph.component.html',
styleUrls: ['./graph.component.scss'],
providers: [HttpService, DataService]
})
export class GraphComponent implements OnInit, AfterViewInit {
constructor( private _httpService: HttpService, private _dataService: DataService ) {
}
ngOnInit() {
}
public renderResult() {
console.log( this._dataService.getGraphData() );
}
}
data.service.ts
import { Injectable } from '#angular/core';
import { Subject } from 'rxjs/Subject';
#Injectable()
export class DataService {
private graphData: Subject<string> = new Subject<string>();
public setGraphData(data) {
this.graphData.next( data );
}
public getGraphData() {
return this.graphData;
}
constructor() { }
}
I simply want ´renderResult()´to be executed after the searchresult has been written to ´graphData´. please help i am confused.
If I understand, you want communication between components and service.
A[component] (make a information) -----(notification)-----> B[service] ----(send)----> C[component] (consume the information)
It's correct? Let's go.
You need create a subscription of graphData(data.service.ts) in GraphComponent.
import { Subscription } from 'rxjs/Subscription';
export class GraphComponent implements OnInit, AfterViewInit {
constructor( private _httpService: HttpService, private _dataService: DataService ) {
}
private subscription: Subscription;
ngOnInit() {
this.subscription = this._dataService.getGraphData().asObservable().subscribe((data) => {
console.log(data);
});
}
}
Look here to help you.
http://jasonwatmore.com/post/2016/12/01/angular-2-communicating-between-components-with-observable-subject
Short answer, I think you need to subscribe to the getGraphData subject, something like this (NOT RECOMMENDED):
public renderResult() {
this._dataService.getGraphData().subscribe(d => {
console.log(d)
});
}
It is not recommended as per the lead of RxJS says: https://medium.com/#benlesh/on-the-subject-of-subjects-in-rxjs-2b08b7198b93
Better answer, create an observable in your service and subscribe to that instead.
data.service.ts
graphObservable = this.graphData.asObservable();
graph-component.ts
public renderResult() {
this._dataService.graphObservable().subscribe(d => {
console.log(d)
});
}
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.
My http-data.service accepts json for output in the component template. Initially, the console shows that the first few calls are given undefined, and the following calls are already taking json, but also if you check the component, then the component shows that the method that outputs the data to the component is called only once and since the data has not yet arrived it writes undefined , But not updated after the arrival of json. Help please understand why? Thank you
My http-data.service:
import {Injectable} from '#angular/core';
import {Http} from '#angular/http';
import {Response} from '#angular/http';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/map';
#Injectable()
export class HttpService{
constructor(private http: Http) {}
getDataOrganizations(): Observable<any[]>{
return this.http.get('http://localhost:3010/data')
.map((resp:Response)=>{
let dataOrganizations = resp.json().organization;
return dataOrganizations;
});
}
getDataModules(): Observable<any[]> {
return this.http.get('http://localhost:3010/data')
.map((resp: Response)=> {
let dataModules = resp.json().modules;
return dataModules;
});
}
getDataPresets(): Observable<any[]> {
return this.http.get('http://localhost:3010/data')
.map((resp: Response)=> {
let dataPresets = resp.json().presets;
return dataPresets;
});
}
getDataModuleItems(): Observable<any[]> {
return this.http.get('http://localhost:3010/data')
.map((resp: Response)=> {
let dataModuleItems = resp.json().module_items;
return dataModuleItems;
});
}
}
My data-all.service
import { Injectable, EventEmitter } from '#angular/core';
import {Response} from '#angular/http';
import { ModuleModel } from './model-module';
import { ModuleItemsModel } from './model-module-items';
import data from '../data/data-all';
import { PriceService } from './price.service';
import { HttpService } from './http-data.service';
#Injectable()
export class ModuleDataService {
constructor(private priceService: PriceService, private httpService: HttpService){
this.dataMinMaxSum = {minSum: 0, maxSum: 0}
}
private currentPopupView: EventEmitter<any> = new EventEmitter<any>();
private dataModules: ModuleModel[] = this.getDataModules();
private dataMinMaxSum: {};
private dataCalculateVariationOrg: any[];
private dataChangeExecutor: any[];
subscribe(generatorOrNext?: any, error?: any, complete?: any) {
this.currentPopupView.subscribe(generatorOrNext, error, complete);
}
calculte(){
return this.priceService.getDataPrice();
}
getDataModules(){
this.httpService.getDataModules().subscribe(((modules)=>{this.dataModules = modules; console.log(this.dataModules);}));
console.log('dataModules');
console.log(this.dataModules);
return this.dataModules;
}
---------------------------------------------------------------------------
}
My left-block.component
import { Component, OnInit} from '#angular/core';
import { ModuleDataService } from '../../service/data-all.service';
import { ModuleModel } from '../../service/model-module';
#Component({
moduleId: module.id,
selector: 'modules-left-block',
templateUrl: './modules-left-block.html',
styleUrls: ['modules-left-block.css']
})
export class ModuleLeft implements OnInit{
modules: ModuleModel[];
constructor(private modulesAll: ModuleDataService){}
ngOnInit(){
this.modules = this.modulesAll.getDataModules();
console.log("view");
console.log(this.modulesAll.getDataModules());
}
onToggle(module: any){
this.modulesAll.toggleModules(module);
}
}
My left-block.component.html
<div class="modules-all">
<div class="modules-all-title">Все модули</div>
<div class="module-item" *ngFor="let module of modules" [ngClass]="{ 'active': module.completed }" (click)="onToggle(module)">{{module?.title}}</div>
</div>
In the component this.modulesAll.getDataModules () method is why it is executed only once without updating (write in console => undefined), if there are any thoughts, write, thanks.
This behaviour is due to the .subscribe() method does not wait for the data to arrive and I'm guessing you already know this. The problem you're facing is because, you have .subscribe to the getDataModules() service in the wron place. You shouldn't subscribe to a service in another service (at leat in this case). Move the subscribe method to the left-block.component and it should work.
getDataModules() {
this.httpService.getDataModules().subscribe(((modules) => {
this.dataModules = modules;
console.log(this.dataModules);
}));
console.log('dataModules');
console.log(this.dataModules);
return this.dataModules;
}
It should look somethig like this:
#Component({
moduleId: module.id,
selector: 'modules-left-block',
templateUrl: './modules-left-block.html',
styleUrls: ['modules-left-block.css']
})
export class ModuleLeft implements OnInit {
modules: ModuleModel[] = new ModuleModel();
constructor(private modulesAll: ModuleDataService, private httpService: HttpService) {}
ngOnInit() {
this.getDataModles();
//this.modules = this.modulesAll.getDataModules();
console.log("view");
//console.log(this.modulesAll.getDataModules());
}
onToggle(module: any) {
this.modulesAll.toggleModules(module);
}
getDataModules(): void {
this.httpService.getDataModules().subscribe(((modules) => {
this.modules = modules;
console.log(this.dataModules);
}));
}
}
I'm learning Angular2. In order to that, I have 2 components, on the click of one of the components, the other component should be notified and act with that.
This is my code so far:
export class JsonTextInput {
#Output() renderNewJson: EventEmitter<Object> = new EventEmitter()
json: string = '';
process () {
this.renderNewJson.next(this.json)
}
}
The process function is being called on the click on the first component.
On the second component I have this code:
export class JsonRendered {
#Input() jsonObject: Object
ngOnChanges () {
console.log(1)
console.log(this.jsonObject)
}
}
The ngOnChanges is never runned, I dont get how to pass the info from one component to other
EDIT
There is an app component which is parent of those 2 components. None of both is parent of the other
This is how my clasess look now:
export class JsonRendered {
private jsonObject: Object
constructor (private jsonChangeService: JsonChangeService) {
this.jsonChangeService = jsonChangeService
this.jsonObject = jsonChangeService.jsonObject
jsonChangeService.stateChange.subscribe(json => { this.jsonObject = json; console.log('Change made!') })
}
}
export class JsonTextInput {
json: string = '';
constructor (private jsonChangeService: JsonChangeService) {
this.jsonChangeService = jsonChangeService
}
process () {
this.jsonChangeService.jsonChange(this.json)
}
}
And the service
import {Injectable, EventEmitter} from '#angular/core';
#Injectable()
export default class JsonChangeService {
public jsonObject: Object;
stateChange: EventEmitter<Object> = new EventEmitter<Object>();
constructor(){
this.jsonObject = {};
}
jsonChange(obj) {
console.log('sending', obj)
this.jsonObject = obj
this.stateChange.next(this.jsonObject)
}
}
Create a service like so...
import {Injectable, EventEmitter} from 'angular2/core';
#Injectable()
export class MyService {
private searchParams: string[];
stateChange: EventEmitter<any> = new EventEmitter<any>();
constructor(){
this.searchParams = [{}];
}
change(value) {
this.searchParams = value;
this.stateChange.next(this.searchParams);
}
}
Then in your component...
import {Component} from 'angular2/core';
import {MyService} from './myService';
#Component({
selector: 'my-directive',
pipes: [keyValueFilterPipe],
templateUrl: "./src/someTemplate.html",
providers: [MyService]
})
export class MyDirective {
public searchParams: string[];
constructor(private myService: MyService) {
this.myService = myService;
myService.stateChange.subscribe(value => { this.searchParams = value; console.log('Change made!') })
}
change(){
this.myService.change(this.searchParams);
}
}
You have to subscribe to the eventemitter, then update your variable. The change event in the service would get fired of from something like...
(click)="change()"