Angular 2 - Call a function that exists outside of the current class - javascript

I want to call a function that exists in HomePage class which is outside of the (class Popover) that I want to use the function on, I've already done some research, and I guess that I need to do something like dependency injection, I've tried to follow some tutorials but I was not lucky enough to solve the issue.
Popover class:
#Component({
template: `
<div>
<button ion-item *ngFor="let city of cities" (click)="switchToThisCity(city.cityName);close();">{{city.cityName | uppercase}}</button>
</div>
`
})
class MyPopover{
static get parameters(){
return [[Http], [ViewController]];
}
constructor(http, viewCtrl) {
this.http = http;
this.viewCtrl = viewCtrl;
//Async Call
var getCities = new URLSearchParams();
this.http.get('https://restApi.com/class/outlet', {headers: ParseHeaders}).subscribe(data => {
this.cities = data.json().results;
});
///
}
close() {
this.viewCtrl.dismiss();
}
switchToThisCity(currentCity){
//I WANT TO CALL THIS FUNCTION WHICH EXISTS ON HomePage CLASS
return getQueries(currentCity);
}
}
HomePage Class:
#Component({
templateUrl: 'build/pages/home/home.html',
})
export class HomePage {
static get parameters(){
return [[NavController],[Http], [NavParams]];
}
// this.cartLength = this.cart.items.length;
constructor() {}
//I NEED TO USE THIS IN THE POPOVER CLASS
getQueries(city){
var cities = new URLSearchParams();
cities.set('cityName', city);
this.http.get('https://restApi.com/classes/citiesList', { search : dishesParams, headers: ParseHeaders}).subscribe(data => {
this.getCities = data.json().results;
});
}
}

Create a Service class
cities.service
#Injectable()
export class CitiesService {
getQueries(city) {
var cities = new URLSearchParams();
cities.set('cityName', city);
return this.http.get('https://restApi.com/classes/citiesList', {
search: dishesParams,
headers: ParseHeaders
}) // here we return an observable so we can subscribe to it in our class
}
and in Popover: (Same with homepage class)
export class MyPopover{
constructor(private citiesService:CitiesService) {
}
// and this is how you use the function
this.citiesService.getQueries().subscribe(data => {
this.getCities = data.json().results;
});
}
UPDATE : have a look at this article: http://nicholasjohnson.com/blog/how-to-do-everything-in-angular2-using-es6/
First up, anything is injectable in Angular, so PetService can be just a newable function.
The Angular DI mechanism will automatically use it to create a
singleton that is local to the correct branch of the injector tree. If
you only have a root injector (made automatically by Angular 2 on
bootstrap), this will be a global singleton, just like Angular
the principle here is to create a service that handles the request and inject it, return an observable object and subscribe, then you can do whatever you want with the response...

I would extract the getQueries method into a service:
#Injectable()
export class QueryService {
constructor(http) {
this.http = http;
}
static get parameters(){
return [[Http]];
}
}
and inject it into both components:
#Component({
templateUrl: 'build/pages/home/home.html',
providers: [ QueryService ]
})
export class HomePage {
static get parameters(){
return [[NavController],[NavParams], [QueryService];
}
constructor(nav, params, service) {
this.service = service;
}
getQueries(city){
this.service.getQueries(city)...
}
}
and the same in the MyPopover class.

Related

Unable to call a method that is declared in one class from another singleton class

As the title says I'm trying to access a method that is declared in one class (Post) from another class (Comments) which is following a singleton pattern. Post class is a service class which has some methods to make API calls. So I need to have access to them from inside Comments class so that I can make API calls.
This is how a simplied version of Post class looks like right now:
#Injectable({
providedIn: 'root'
})
class PostService extends AnotherService {
constructor( auth: AuthService, http: HttpClient ) {
super('string', auth, http);
}
getPost( id: string ) {
return this.http.get(`posts/${id}`);
}
}
This is how the Comments class look like:
class Comments {
private postService: PostService;
private static instance;
private constructor() {}
static createInstance() {
if ( !Comments.instance ) {
Comments.instance = new Comments();
}
return Comments.instance;
}
getComments( id ) {
// these does not even run
this.postService.getPost( id )
.then( post => {
console.log( post );
})
.catch( error => {
console.log( error );
});
}
}
How can I go about accessing it?
=========UPDATE=======
Creating an instance of Comment class in another class called ClassC.
const instance - Comments.createInstance();
instance.getComments( id );
Use a new service to save your comment object data
let say We have a service named SharedDataService.
private _comments: Array<any> =[];// or array<IComment> (a defined interface from your part)
class SharedDataService(){}
get comments():Array<any>{
return this._comments}
set comments(value:Array<any>){
this._comments = value;
}
}
You should init PostService on your Comments Constructor
private constructor(private postService: PostService,private sharedDataService :SharedDataService) {
}
getComments() {
// these does not even run
this.postService.getPost( '1' )
.then( post => {
this.sharedDataService.comments = post // if You get an array of comments here
console.log( post );
console.log(this.comments)// getter function its new value has been set
})
.catch( error => {
console.log( error );
});
get comments(){
this.sharedDataService.comments
}
}
If You want to send two http request in parallel then get their values You should use combineLatest rxjs operator.
Your post service will be like this:
getPost(id: string) {
$postDataHttpRequest = this.http.get(`posts/${id}`);
$commentsDataHttpRequest = this.http.get(`posts/${id}/comments`);
return combineLatest($postDataHttpRequest, $commentsDataHttpRequest)
}
///
This is how the Comments class look like:
private constructor(private postService: PostService) {
}
getComments() {
this.postService.getPost( '1' )
.subscribe( (posts,comments) => {
console.log(posts);
console.log(comments);
},(error)=>{console.log(error)})
}

Why change in Observable variable triggers change in View Angular

I'm learning Angular, so I'm building todo app. Todos are fetched from API, and every one of them has a project as parent. In a view I'm adding new project which triggers addNewProject method in service which in turn triggers POST request to the API in another service. Projects are listed in the sidebar, bind to input from parent component. ProjectsService holds array of projects in a private field, and has observable which is used by main component.
I'm struggling to understand why on earth appending private property _projects after API call in the service triggers change in MainComponent property even though _projects is private and change in Observable from array should not trigger functions passed from Observers.
Parent:
#Component({
selector: 'app-main',
templateUrl: './main.component.html',
styleUrls: ['./main.component.css']
})
export class MainComponent implements OnInit {
projects: Project[] = new Array<Project>();
constructor(private projectsService: ProjectsService) {
}
ngOnInit() {
this.projectsService.projects$.subscribe((projects) => {
this.projects = projects;
});
}
addNewProject(newProjectName: string) {
this.projectsService.addNewProject(newProjectName);
}
}
Sidebar:
#Component({
selector: 'app-sidebar',
templateUrl: './sidebar.component.html',
styleUrls: ['./sidebar.component.css']
})
export class SidebarComponent implements OnInit {
#Input() projects: Project[];
#Output() projectEntered = new EventEmitter<string>();
constructor() {
}
ngOnInit() {
}
projectAdded(projectName: string) {
this.projectEntered.emit(projectName);
}
}
Service:
export class ProjectsService {
private _projects$: Observable<Array<Project>> = new Observable<Array<Project>>();
private _projects: Project[] = [];
private _loadedProject$: Observable<Project>;
private _projectsLoaded: boolean;
private _taskForProject: object;
constructor(private userService: UserService, private api: ApiService) {
}
loadAllProjects() {
this._projects$ = this.api.getAllProjectsByUserId(this.userService.userId).pipe(
map((projects) => {
this._projects = projects;
return this._projects;
})
);
}
get projects$() {
if (!this._projectsLoaded) {
this.loadAllProjects();
}
return this._projects$;
}
getAllTasks() {
return this.api.getAllTasksByUserId(this.userService.userId);
}
getProject(projectId: string) {
this._loadedProject$ = this.api.getProjectById(projectId);
return this._loadedProject$;
}
getTodayTasksForProject(tasks: Task[]) {
const todayTasks: Task[] = [];
const todayDate = new Date();
tasks.forEach((task) => {
if (new Date(task.completionPlannedDate).getDate() === todayDate.getDate()) {
tasks.splice(tasks.indexOf(task), 1);
todayTasks.push(task);
}
});
return todayTasks;
}
getTomorrowTasksForProject(tasks: Task[]) {
const tomorrowTasks: Task[] = [];
const tomorrowDate = new Date(new Date().getDate() + 1);
tasks.forEach((task) => {
if (new Date(task.completionPlannedDate).getDate() === tomorrowDate.getDate()) {
tasks.splice(tasks.indexOf(task), 1);
tomorrowTasks.push(task);
}
});
return tomorrowTasks;
}
getUpcomingTasks(tasks: Task[]) {
const upcomingTasks: Task[] = [];
const upcomingDate = new Date(new Date().getDate() + 2);
tasks.forEach((task) => {
if (new Date(task.completionPlannedDate).getDate() > upcomingDate.getDate()) {
tasks.splice(tasks.indexOf(task), 1);
upcomingTasks.push(task);
}
});
return upcomingTasks;
}
addNewProject(projectName: string) {
this.api.postNewProject({
id: null,
userId: this.userService.userId,
title: projectName,
tasks: []
}).subscribe((project: Project) => {
this._projects.push(project);
});
}
}
Please see here:
Basically application state change can be caused by three things:
Events - click, submit
XHR - Fetching data from a remote server
Timers - setTimeout(), setInterval()
If you don't want change detection to fire try changing to ChangeDetectionStrategy.OnPush

Adding an src from an API in Angular7

This is my component.ts where when it's loaded I get the data from the api, which I can see in the console.log, I do infact get my array of 10 objects (they come in groups of 10 on the api). I have the correct path in the API for the source code of the first image in the array of 10 which I typed to out the correct path for in normal http/javascript format of data.hits.hits[n]._source.images[n].urls.original. However when I try to put it in angular it can't read the data value as it is right now since it's out of scope, but I can't figure out how to word it in a better way.
import { Component, OnInit } from '#angular/core';
import { ConfigService } from '../../config.service';
import { Observable } from 'rxjs';
#Component({
selector: 'app-property-binding',
templateUrl: './property-binding.component.html',
styleUrls: ['./property-binding.component.css']
})
export class PropertyBindingComponent implements OnInit {
private isHidden : boolean;
public zeroImage : string;
private Photos : Observable<Object>;
constructor(private configService: ConfigService) { }
ngOnInit() {
//doing the API call
this.Photos = this.configService.getConfig();
this.Photos.subscribe((data) => console.log(data));
}
toggle() : void {
this.isHidden = !this.isHidden;
if(this.isHidden){
//var zeroImg = document.createElement("img");
this.zeroImage.src = data.hits.hits[0]._source.images[0].urls.original;
}
}
}
Here is the Angular html page that should property bind the src with the variable that I want.
<p>
View Artworks
</p>
<button class="btn btn-info" (click)="toggle()">Show Artwork</button>
<div class="col-md-4" *ngIf="isHidden">
<img [src]="zeroImage">
</div>
Here is the service method that I have the method that makes the API call
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { HttpHeaders } from '#angular/common/http';
#Injectable({
providedIn: 'root'
})
export class ConfigService {
private httpOptions = {
headers: new HttpHeaders({
'ApiKey': 'my_personal_key'
})
};
private configUrl = 'https://api.art.rmngp.fr/v1/works';
constructor(private http: HttpClient) { }
getConfig(){
let obs = this.http.get(this.configUrl, this.httpOptions)
console.log("Inside the getConfig method, before subscribe, doing API call" +
obs);
//might not need to subscribe here??
//obs.subscribe((response) => console.log(response))
return obs;
//return this.http.get(this.configUrl, this.httpOptions);
}
}
And slightly unrelated code, this is the normal http/javascript where I wrote the code Outside of Angular, which works perfectly fine.
function displayPhoto(){
fetch('https://api.art.rmngp.fr/v1/works/, {headers: {ApiKey: "my_personal_key"}})
.then(function(response){
return response.json();
})
.then(function(data){
document.getElementById("zeroImg").src = data.hits.hits[0]._source.images[0].urls.original;
Again, the API call in Angular works, I can see I am pulling the data successfully, I however can not set the image to the first image in the set of data and have been struggling with it. any help will help
You are not doing anything with the data when you subscribe
this.Photos.subscribe((data) => console.log(data));
You have not done anything with the data here.
zeroImg.src = data.hits.hits[0]._source.images[0].urls.original;
zeroImg is a string and makes no sense to set a src property on it and data is undefined at the point. The only place there is a data variable is in your subscription function but it is not available here.
The following will set the src of the image
this.Photos.subscribe((data) => {
this.zeroImg = data.hits.hits[0]._source.images[0].urls.original;
});
Make the toggle function just toggle the isHidden flag and get rid of the rest.
ngOnInit() {
//doing the API call
this.Photos = this.configService.getConfig();
this.Photos.subscribe((data) => {
this.zeroImg = data.hits.hits[0]._source.images[0].urls.original;
});
}
toggle() : void {
this.isHidden = !this.isHidden;
}

ERROR Error: Cannot insert a destroyed View in a ViewContainer

I have designed a page generator by configuration app and it works fine and generate components and render them on the page as good as it should.
But when i try to render a new page by new configuration, i get this error ERROR Error: Cannot insert a destroyed View in a ViewContainer! right when the generator service try to render the first component on the page after cleaning the page.
The pages configuration arrives from pageConfigService at ngOnInit in NemoContainerComponent and error appears when pageGenerator try to render the WidgetContainerComponent.
** UPDATE **
Page will be generate after changing the rout, all of the routes base component is the NemoContainerComponent and when route changes, the NemoContainerComponent destroyed and created again.
** UPDATE **
This is NemoContainerComponent:
#Component({
selector: 'nemo-container',
templateUrl: './nemo-container.component.html',
encapsulation: ViewEncapsulation.None
})
export class NemoContainerComponent {
private subscription: Subscription = new Subscription();
#ViewChild(ChildReferenceDirective) childReference: ChildReferenceDirective;
constructor(
private pageGenerator: PageGeneratorService,
private route: ActivatedRoute,
private routeService: RouteService,
private router: Router,
private storeEngine: StoreEngineService,
private pageConfigService: PageConfigService
) {
this.subscription.add(route.params.subscribe((params) => {
this.routeService.setRouteService = params;
}));
let activeRoute = router.url.split('/').join(' ');
document.body.className += ' ' + activeRoute;
}
ngOnInit() {
this.pageGenerator.containerRef = this.childReference.viewReference;
this.subscription.add(this.pageConfigService
.get(this.route.data['value'].page)
.subscribe(data => {
this.pageGenerator.renderNewPageByConfigurationFile(data.json)
}));
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
WidgetContainerComponent is here:
#Widget({
typeName: 'widget-container',
displayName: '',
type: 'parent',
settings: []
})
#Component({
selector: 'widget-container',
templateUrl: "widget-container.component.html",
encapsulation: ViewEncapsulation.None
})
export class WidgetContainerComponent {
#ViewChild(ChildReferenceDirective, { read: ViewContainerRef }) childRef;
get childReference(): ViewContainerRef {
return this.childRef;
}
private data: ObjectInterface = {};
set widgetData(value: ObjectInterface) {
for (let item in value) {
this.data[item] = value[item];
}
}
get widgetData(): ObjectInterface {
return this.data;
}
public id: string = '';
}
** UPDATE **
angular pack version: 4.4.6
** UPDATE **
thanks in advance for your helps :)

Working RouteService Function is Returning "Undefined" When Used in Another Component

I am running two checks and then conditionally populating some data to certain routes if both conditions are met. In my room.component.html file I am using an *ngIf for this:
<div *ngIf="isLoggedIn() && isRoomRoute()" class="others">
... do some work
</div>
My room.component.ts file looks like this:
import { RouteService } from './../../data/route.service';
import { Component } from '#angular/core';
import { AuthenticationService } from './../../data/authentication.service';
import { Router } from '#angular/router';
#Component({
selector: 'app-room',
templateUrl: './room.component.html',
styleUrls: ['./room.component.less']
})
export class RoomComponent {
model: any = {};
loading = false;
username;
password;
routeUrl;
private url = 'http://localhost:5000';
constructor(private authenticationService: AuthenticationService,
private router: Router,
private routeService: RouteService,
private appComponent: AppComponent) { }
isLoggedIn() {
this.loading = true;
if (this.authenticationService.isAuthenticated()) {
return true;
}
}
isRoomRoute(routeUrl) {
if (this.routeService.isRoomRoute(this.routeUrl)) {
return true;
}
}
}
As you can see above, the second check is using a function from my routeService. That function looks like this:
isRoomRoute(routeUrl) {
if (routeUrl.includes('staff')) {
console.log('This url: ' + routeUrl + ' is a roomRoute');
return true;
} else {
console.log('This url: ' + routeUrl + ' is NOT a room route');
return false;
}
}
This urls are being tracked in my app.component, which is using the routeService in the constructor, and looks like this:
constructor(private routeService: RouteService,
private router: Router)
{
this.router.events.subscribe((route) => {
let routeUrl = route.url;
this.routeService.sendRoute(routeUrl);
this.routeService.isRoomRoute(routeUrl);
});
}
I am successfully getting the right result from my routerService's "isRoomRoute" function, but I am getting an "undefined" error when I try and pass that result to my room component, even though I though I am calling the routeService in that component. So my question is, what am I missing that's making the result "undefined" from the room component? How do I pass a boolean value from the result of the isRoomRoute() function in the routeService to my room component?

Categories

Resources