Update angular2 View/global variable with data inside a promise - javascript

Im using fetch to get data from an external API like this and it is printing to the console correctly. But this.games doesn't update in the global scope so the view isn't updating. How can I update the global gams variable with the data from within the promise once the fetch has returned :
import {bootstrap, Component, CORE_DIRECTIVES, Pipe, Inject} from 'angular2/angular2';
import { Game } from './game/game';
import { API } from './services/API';
import {NgZone} from 'angular2/angular2';
#Component({
selector: 'app',
template: `
Hello
<button (click)="onClickMe()">Click me!</button>
<div *ng-for="#game of games" class = "game">
//layout
</div>
`,
directives: [CORE_DIRECTIVES, Game],
})
export class App {
data: any;
api: API;
games: Game[];
constructor(api:API) {
this.api = api;
this.games = [];
api.fetchGames().then(function(response) {
this.data = response;
console.log(this.data);
var gamesTemp = [];
this.teams = this.data.resultSets[1].rowSet;
for (var i = 0; i < this.teams.length - 1; i += 2) {
//manipulate
}
this.games = gamesTemp;
console.log(this.games);
});
and this it the fetchGames method:
fetchGames() {
return fetch('http://stats.nba.com/stats/scoreboardV2?DayOffset=0&LeagueID=00&gameDate=11%2F5%2F2015')
.then(function(response) {
return response.json();
}).catch(function(ex) {
console.log('parsing failed', ex);
});
}

This seems like a scope (this) problem. Your this inside the callback is not your class! The simplest solution is to use es6 arrow functions:
import {bootstrap, Component, CORE_DIRECTIVES, Pipe, Inject} from 'angular2/angular2';
import { Game } from './game/game';
import { API } from './services/API';
import {NgZone} from 'angular2/angular2';
#Component({
selector: 'app',
template: `
Hello
<button (click)="onClickMe()">Click me!</button>
<div *ng-for="#game of games" class = "game">
//layout
</div>
`,
directives: [CORE_DIRECTIVES, Game],
})
export class App {
data: any;
api: API;
games: Game[];
constructor(api:API) {
this.api = api;
this.games = [];
api.fetchGames().then((response) => { //es6 arrow function was meant to solve this problem!
this.data = response;
console.log(this.data);
var gamesTemp = [];
this.teams = this.data.resultSets[1].rowSet;
for (var i = 0; i < this.teams.length - 1; i += 2) {
//manipulate
}
this.games = gamesTemp;
console.log(this.games);
});

Related

Angular removing elements from a list shared with a service

this problem is driving me crazy.
I have an array defined within a service, which is used in 3 other components:
This is the service, file products.service.ts (notice the product array of Products)
import { Injectable } from '#angular/core';
import { ​​HttpClient } from '#angular/common/http';
import { Product } from './../models/Product';
import { ProductForm, productFormToProduct } from './../models/ProductForm';
// #Injectable({
// providedIn: 'root'
// })
const apiUrl = 'http://localhost:3000/products';
#Injectable()
export class ProductsService {
public products: Product[] = [];
constructor(private http: HttpClient) {}
getProducts() {
return this.http.get(apiUrl)
}
deleteProduct(p: Product) {
// this.products = this.products.filter(prod => prod.id !== p.id);
const i = this.products.indexOf(p);
this.products.splice(i,1);
return this.http.delete(apiUrl + "/" + p.id)
}
storeNewProduct(pf: ProductForm) {
const idList = this.products.map((x) => {return x.id});
const i = Math.max(...idList) + 1;
const p = productFormToProduct(pf);
p.id = i;
this.products.push(p);
return this.http.post(apiUrl, p)
}
}
This is the component where i subscribe to getProducts, and fill the array (file products.component.ts):
import { Component, OnInit } from '#angular/core';
import { ProductsService } from '../../shared/services/products.service';
import { Product } from '../../shared/models/Product';
#Component({
selector: 'app-products',
templateUrl: './products.component.html',
styleUrls: ['./products.component.scss']
})
export class ProductsComponent implements OnInit {
products: Product[] = [];
searchText: string = "";
constructor(private productsService: ProductsService) {}
ngOnInit(): void {
this.productsService.getProducts()
.subscribe((data: Product[]) => {
this.productsService.products = data;
this.products = this.productsService.products;
})
}
}
And this is the component where i subscribe to deleteProduct (file product-card.component.ts):
import { Component, Input, OnInit } from '#angular/core';
import { ProductsService } from '../../services/products.service';
import { Product } from './../../models/Product';
#Component({
selector: 'app-product-card',
templateUrl: './product-card.component.html',
styleUrls: ['./product-card.component.scss']
})
export class ProductCardComponent implements OnInit {
constructor(private productsService: ProductsService) {}
ngOnInit(): void {
}
#Input() product: Product
public buttonDeleteFunction() {
this.productsService.deleteProduct(this.product).subscribe();
}
}
The problem is, when i click on some delete product button, i have this weird behaviour:
Before click:
After click:
Here is the products.component.html file:
<div class="products__header">
<h3 class="products__heading">
Listado de productos ({{ products.length }})
</h3>
<input
class="products__search"
placeholder="Buscador"
type="search"
[(ngModel)]="searchText"
/>
</div>
<p *ngFor="let p of products">{{ p.name }}</p>
<p>{{ products }}</p>
<div class="products__list">
<app-product-card
*ngFor="let p of products | filterNames: searchText"
[product]="p"
></app-product-card>
</div>
Why do i get the expected behaviour in only two of the four places where i use the products list?
I know i can use an Output to manually remove the item from the list when i click the button, but i have been told that services are used instead of Inputs/Outputs when i want to share between multiple components, so i'd rather not use an Output for this
When you use your approach with common data on service layer then a common pitfall is that Angular does not detect the changes that affect your component. In that case you must inform your component for those changes using an emmiter.
Use an emmiter on service
productUpdated :EventEmitter = new EventEmitter();
deleteProduct(p: Product) {
// this.products = this.products.filter(prod => prod.id !== p.id);
const i = this.products.indexOf(p);
this.products.splice(i,1);
this.productUpdated.emit(this.products);
return this.http.delete(apiUrl + "/" + p.id)
}
And then listen for that change ProductsComponent
export class ProductsComponent implements OnInit {
products: Product[] = [];
searchText: string = "";
constructor(private productsService: ProductsService) {}
ngOnInit(): void {
this.productsService.getProducts()
.subscribe((data: Product[]) => {
this.productsService.products = data;
this.products = this.productsService.products;
})
this.productsService.productUpdated.subscribe( (data) => {
this.products = data;
});
}

Angular firebase why same function only works once?

I got upload.service and 2 different modules with some components.
upload.service , upload.ts is imported to components->
Upload.module (
upload.component (everything works fine (i can upload and get photo
url data from database)
)
Ui.module )
upload.component (same function but I can't see photo , no url)
)
Working component (upload.component) :
import { Component, OnInit } from '#angular/core';
import { UploadService } from '../shared/upload.service';
import { Upload } from '../shared/upload';
import { Observable } from 'rxjs/Observable';
#Component({
selector: 'uploads-list',
templateUrl: './uploads-list.component.html',
styleUrls: ['./uploads-list.component.scss'],
})
export class UploadsListComponent implements OnInit {
uploads: Observable<Upload[]>;
showSpinner = true;
constructor(private upSvc: UploadService) { }
ngOnInit() {
this.uploads = this.upSvc.getUploads();
this.uploads.subscribe(() => this.showSpinner = false);
}
}
Not working component ( ui.component 0 errors) :
import {Component, OnInit} from '#angular/core';
import { UploadService } from "../../uploads/shared/upload.service";
import { Upload } from "../../uploads/shared/upload";
import { Observable } from "rxjs/Observable";
#Component({
selector: 'top-nav',
templateUrl: './top-nav.component.html',
styleUrls: ['./top-nav.component.scss'],
})
export class TopNavComponent implements OnInit {
uploads: Observable<Upload[]>;
show = false;
showSpinner = true;
toggleCollapse() {
this.show = !this.show;
}
constructor(private upSvc: UploadService) { }
ngOnInit() {
this.uploads = this.upSvc.getUploads();
console.log("paimama upload :", this.uploads)
this.uploads.subscribe(() => this.showSpinner = false);
}
}
I think the main problem is that I am trying to use these functions and variables from one service in two different modules - components. How to retrieve it so it would work ?
Edit (the function I am calling) :
getUploads() {
this.uploads = this.db.list(`${this.auth.userId}`).snapshotChanges().map((actions) => {
return actions.map((a) => {
const data = a.payload.val();
const $key = a.payload.key;
return { $key, ...data };
});
});
return this.uploads;
}
Html (working one in upload.component) :
<h3>File Uploads</h3>
<div *ngFor="let upload of uploads | async">
<upload-detail [upload]='upload'></upload-detail>
<img src="{{upload.url}}" alt="">
</div>
<loading-spinner *ngIf="showSpinner"></loading-spinner>
<hr>
<upload-form></upload-form>
HTML (not working ui.component)
<div *ngFor="let upload of uploads | async">
<img src="{{upload.url}}" alt="">
</div>
or
It seems that you are re-initializing your service observable each time the getUploads() method is called. This causes each component to be subscribing to a different observable, so they will not be in sync.
Don't do this if you want all components to subscribe to the same stream.
Change this:
getUploads() {
this.uploads = this.db.list(`${this.auth.userId}`).snapshotChanges().map((actions) => {
return actions.map((a) => {
const data = a.payload.val();
const $key = a.payload.key;
return { $key, ...data };
});
});
return this.uploads;
}
To this:
getUploads() {
if (!this.uploads) {
this.uploads = this.db.list(`${this.auth.userId}`).snapshotChanges().map((actions) => {
return actions.map((a) => {
const data = a.payload.val();
const $key = a.payload.key;
return { $key, ...data };
});
});
}
return this.uploads;
}
Now each component should get the same exact observable stream.
Let me know if that doesn't help.
Update:
Another approach you can take is to create a custom subject and use that as an observable. Subscribe to that observable in both components, and when you get uploads from the db you can add the upload to the stream. Each component will get the upload you add to that stream.
#Injectable()
export class UploadService {
uploadSubject: ReplaySubject<Upload> = new ReplaySubject();
upload$: Observable<Upload> = this.uploadSubject.asObservable();
getUploads() {
this.http.get(url).subscribe((upload: Upload) => {
this.uploadSubject.next(upload);
});
}
}
Now in any component, you can just subscribe to upload$ and any time the getUploads() is called, every single component subscribing to upload$ will get the value.
export class Component1 {
upload: Upload;
constructor(private uploadService: UploadService) {
uploadService.upload$.subscribe((upload: Upload) => this.upload = upload);
}
}
export class Component2 {
upload: Upload;
constructor(private uploadService: UploadService) {
uploadService.upload$.subscribe((upload: Upload) => this.upload = upload);
}
}
With this approach, just make sure to call getUploads somewhere, because the components are not calling it; they are just listening for the value it will broadcast.
Update 2:
Now I am modifying your code to work with my example above. Since this is using your code, it may not work because I may be missing certain things you have in your environment. But the original "Update" above works 100%, so use that as a guideline to get your code working. Just read the code and make sure you understand what it does and you will see that it will work for what you're doing.
UploadService:
#Injectable()
export class UploadService {
uploadStream: ReplaySubject<Upload[]> = new ReplaySubject();
uploads$: Observable<Upload[]> = this.uploadStream.asObservable();
constructor() {
this.getUploads();
}
getUploads() {
this.db.list(`${this.auth.userId}`)
.map((actions: Action[]) => {
return actions.map((action: Action) => {
const data = action.payload.val();
const $key = action.payload.key;
return new Upload({ $key, ...data });
});
})
.subscribe((uploads: Upload[]) => {
this.uploadStream.next(uploads);
});
}
}
UploadsListComponent:
import { Component } from '#angular/core';
import { UploadService } from '../shared/upload.service';
#Component({
selector: 'uploads-list',
templateUrl: './uploads-list.component.html',
styleUrls: ['./uploads-list.component.scss'],
})
export class UploadsListComponent {
constructor(public uploadService: UploadService) {}
}
UploadListComponent template:
<h3>File Uploads</h3>
<div *ngFor="let upload of uploadService.uploads$ | async">
<upload-detail [upload]='upload'></upload-detail>
<img src="{{upload.url}}" alt="">
</div>
<loading-spinner *ngIf="showSpinner"></loading-spinner>
<hr>
<upload-form></upload-form>
Now you can take the same component approach as above on your second component.

How to display data from MONGODB to ANGULAR 5's html

I've been stuck here since yesterday.
I have an API that retrieves data from mongodb (mlab.com)
var helpers = require('../config/helper.js');
var UserModel = require('../model/UserModel.js');
module.exports = function (server){
server.get("/", function (req, res, next) {
UserModel.find({}, function (err, users) {
helpers.success(res, next, users);
});
});
}
This is the UserModel.js
const mongoose = require('mongoose');
var Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
var UserSchema = new Schema({
id: ObjectId,
fname: String,
lname: String,
email_add: String,
champ_type: String
});
var UserModel = mongoose.model('users', UserSchema);
module.exports = UserModel;
My app.js
//packages
const restify = require('restify');
const mongoose = require('mongoose');
const restifyValidator = require('restify-validator');
const corsMiddleWare = require('restify-cors-middleware');
//local
var setupController = require('./controller/setupController.js');
var userController = require('./controller/userController.js');
var config = require('./config/dbConfig.js');
//init packages
const server = restify.createServer();
mongoose.connect(config.getMongoConnection());
setupController(server, restify, restifyValidator, corsMiddleWare);
userController(server);
server.listen(8080, function () {
console.log('%s listening at %s', server.name, server.url);
});
ALL OF THE ABOVE IS WORKING WHEN I TRIED IT ON POSTMAN
SCREENSHOT OF THE POSTMAN
NOW LET'S GO TO MY ANGULAR 5 PROJECT
First, I generate a component(retrieve.component) using the CLI.
Second, I created a service[logging.service.ts], code:
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
import { Injectable } from '#angular/core';// If you need external data
import { Http, Response, Headers, RequestOptions, URLSearchParams } from '#angular/http';// If you need to call some API from the cloud
import { Request } from "#angular/http";
// Import RxJs required methods
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import { LogModel } from '../model/log.model';
#Injectable()
export class LoggingService {
private ROOT_URL = "http://localhost:8080/";
constructor(private http: Http) {}
//getPosts() {
//let params = new HttpParams().set('userId', '1');
//this.posts = this.http.get(this.ROOT_URL /*, { params }*/);
//}
addComments(): Observable<LogModel[]> {
let headers = new Headers({ "Content-Type": "application/json" }); // ... Set content type to JSON
let options = new RequestOptions({ headers: headers }); // Create a request option
return this.http
.get(this.ROOT_URL, options)
.map((response: Response) => response.json())
.catch((error: any) =>Observable.throw(error.json().error || "Server error"));
}
test() {
//console.log("Hello!");
const subject = new Subject();
subject.subscribe({
next: function(value) {
console.log(value);
}
});
subject.next("Hello!");
subject.next("Free!");
}
}
Third, I created a model (log.model.ts), code:
export interface LogModel {
lname: String;
fname: String;
email_add: String;
champ_type: String;
}
Fourth, I configured my component (retrieve.component.ts), code:
import { Component, OnInit } from '#angular/core';
import { Observable } from "rxjs/Observable";
import { LoggingService } from "../service/logging.service";
import { LogModel } from "../model/log.model";
#Component({
selector: "app-retrieve",
templateUrl: "./retrieve.component.html",
styleUrls: ["./retrieve.component.css"]
})
export class RetrieveComponent implements OnInit {
//posts: Observable<any>;
private results: LogModel[];
private model: any;
constructor(private _loggingservice: LoggingService) {}
getAllusers() {
this.model = this._loggingservice.addComments().subscribe(data => {
this.results = data;
//this.results = Array.of(this.results);
}
err => console.error(err),
() => console.log('getBooks completed')
);
console.log(this.model);
}
ngOnInit() {
this._loggingservice.test();
}
}
Fifth, configured my retrieve.component.html, code:
<p>
retrieve works!
</p>
<button (click)="getAllusers()">Get Posts</button>
<div *ngFor="let item of results?.data">
<p>Output: {{ item }}</p>
</div>
Sixth, configured my app.module.ts, code:
// This typescript file is called a module. It is a group of components bundled together.
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import {
NgForm,
FormBuilder,
FormGroup,
Validators,
FormArray,
FormControl,
FormsModule
} from "#angular/forms"; // for you to enable ngModel in HTMLs
//import { HttpClientModule } from '#angular/common/http';
import { HttpModule } from "#angular/http";
import { AppComponent } from './app.component';
// Before you can use a component, you'll need to declare it here
import { ServerComponent } from './server/server.component';
import { ServersComponent } from './servers/servers.component';
import { ProfileComponent } from './iprofile/profile.component';
import { ProfileService } from './iprofile/profile.service';
import { LoggingService } from './service/logging.service';
import { RetrieveComponent } from './retrieve/retrieve.component';
#NgModule({
declarations: [
AppComponent,
// Then here.
ServerComponent,
ServersComponent,
ProfileComponent,
RetrieveComponent
],
imports: [
BrowserModule,
FormsModule, // for you to enable ngModel in HTMLs
HttpModule
],
providers: [ProfileService, LoggingService],
bootstrap: [AppComponent]
})
export class AppModule {}
Lastly, I implement it on the main html (app.component.html), code:
<!--The content below is only a placeholder and can be replaced.-->
<div style="text-align:center">
<h1>
Welcome to {{ name }}!
</h1>
<img width="300" alt="Angular Logo" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogIDwvc3ZnPg==">
</div>
<input type="text" [(ngModel)]="name">
<h3>Facker</h3>
<hr>
<app-servers></app-servers>
<app-profile></app-profile>
<app-retrieve></app-retrieve>
<!--
<h2>Here are some links to help you start: </h2>
<ul>
<li>
<h2><a target="_blank" rel="noopener" href="https://angular.io/tutorial">Tour of Heroes</a></h2>
</li>
<li>
<h2><a target="_blank" rel="noopener" href="https://github.com/angular/angular-cli/wiki">CLI Documentation</a></h2>
</li>
<li>
<h2><a target="_blank" rel="noopener" href="https://blog.angular.io/">Angular blog</a></h2>
</li>
</ul> -->
Here's the screenshot of the output PS. When I press "get Posts" button it shows "[Object object]"
So, thank you for reading all the stuff. I just want to know what goes wrong? I just want to display to my Angular 5 project what the postman displayed. I'm trying to google simple tutorials but it doesn't work. By the way, restify and corsmiddleware are my default packages, what I mean is you can suggest but I think that won't solve the problem.
So, you have got this piece of code:
getAllusers() {
this.model = this._loggingservice.addComments().subscribe(data => {
this.results = data;
//this.results = Array.of(this.results);
data here is represented as a json, but you want it to be an object. Try to JSON.parse() your data like this:
getAllusers() {
this.model = this._loggingservice.addComments().subscribe(data => {
this.results = JSON.parse(data);
If this isn't working, try to JSON.stringify() your response instead of response.json here:
return this.http
.get(this.ROOT_URL, options)
.map((response: Response) => response.json())
Here I give working example of getting response from API and parsing it into custom object:
public loadPage() {
this.http
.get(environment.API_URL + "search/" + this.query + "/" + pageToLoad.toString())
.map((data) => JSON.stringify(data))
.subscribe((data) => {
const page: Product[] = JSON.parse(data);
this.showedProducts = this.showedProducts.concat(page);
});
}

Triggering change detection when i use service communication

So I have two not related components and I'm trying to communicate between them using a service and a BehaviorSubject. Everything is cool, data is exchanged, but when i call the service from one of the components, it doesn't trigger change detection on the other component.
So to show what I'm talking about in code:
The service:
import {Injectable, Optional, EventEmitter} from '#angular/core';
import {Http} from '#angular/http';
import 'rxjs/add/operator/map';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { forEach } from '#angular/router/src/utils/collection';
#Injectable()
export class SbkService {
items: any = [];
private _itemsSource = new BehaviorSubject<any>(0);
items$ = this._itemsSource.asObservable();
constructor (
private _localStorageService: LocalStorageService
) {}
storeSelection(item) {
this.items.push(item);
this.setLocalStorage();
}
removeSelection(selectionId) {
for (var i = this.items.length-1; i >= 0; i--) {
if (this.items[i].selectionId == selectionId)
this.items.splice(i, 1);
}
this.setLocalStorage();
return true;
}
getLocalStorage() {
this.items = this._localStorageService.get('items');
this._itemsSource.next(this.items);
return this.items;
}
setLocalStorage() {
this._localStorageService.set('items', this.items);
this._itemsSource.next(this.items);
return true;
}
}
Component 1:
import { Component, OnInit } from '#angular/core';
import { SbkService } from '../../services/sbk.service'
import {Subscription} from 'rxjs/Subscription';
#Component({
selector: 'app-right-sidebar',
template: `<ul>
<li *ngFor="let selection of selections">
{{selection.name}}
<span class="cutom-btn" (click)="remove(selection.selectionId)">
delete
</span>
</li>
</ul>`,
styles: []
})
export class RightSidebarComponent implements OnInit {
selections: any = [];
subscription:Subscription;
constructor (
private _sbkService: SbkService
) {
}
ngOnInit() {
this.subscription = this._sbkService.items$
.subscribe(selections => {
this.selections = selections })
this._sbkService.getLocalStorage();
}
ngOnDestroy() {
// prevent memory leak when component is destroyed
this.subscription.unsubscribe();
}
remove(selectionId) {
this._sbkService.removeSelection(selectionId);
}
}
Component 2:
import { Component, ViewChild, ElementRef } from '#angular/core';
import 'rxjs/add/operator/map';
import {forEach} from '#angular/router/src/utils/collection';
import {SbkService} from '../services/sbk.service'
#Component({
selector: 'app-match-table',
template: `
<div (click)="addItem('mumble', 1)">Add mumble</div>
<div (click)="addItem('ts', 2)">Add ts</div>
<div (click)="addItem('discord', 3)">Add discord</div>
`,
styles: []
})
export class MatchTableComponent {
constructor(
private _sbkService: SbkService
) {}
//Place a bet in the betslip
public addItem = (name, selectionId) => {
item: Object = {};
item.selectionId = selectionId;
item.name = name;
this._sbkService.storeSelection(item);
}
}
So, when I click on a div from component 2 (MatchTableComponent) it updates the selections array in component 1 (RightSideBarComponent) but doesn't trigger a change detection, so the sorted list doesn't get updated until i refresh the page. When i click on delete from RightSideBarComponent template, it updates the selections array and triggers the change detection.
How can I make this work? I tried subscribing to an event from SbkService in the AppComponent and from there triggering the setLocalStorage from SbkService, but no luck...
If I'm not wrong, you should set the next "sequence" on your Observable "items" through your BehaviourSubject.
Could you modify and try this?:
storeSelection(item){
const itemsAux = this._itemsSource.getValues();
itemsAux.push(item);
this._itemsSource.next(itemsAux);
}
setLocalStorage(){
this._localStorageService('items', this._itemsSource.getValues();
return true;
}

Issues with Angular 2 promise passing object

I am having issues using a promise to return a Degree object in Angular 2. The first return statement (uncommented) in degree.service works just fine in combination with the uncommented implementation of getDegree() in build.component. However, when I try to switch to either of the commented implementations using a promise, the object always comes back as "undefined"
degree.service.ts
import { Injectable } from '#angular/core';
import { Degree } from '../components/degree';
import { Category } from '../components/category';
import { Course } from '../components/course';
import { SAMPLE } from '../components/mock-degree';
#Injectable()
export class DegreeService{
getDegree(){
return SAMPLE;
// return Promise.resolve(SAMPLE);
// return new Promise<Degree>(function (resolve, reject) {
// resolve(SAMPLE);
// })
}
}
build.component.ts
import { Component, Input, OnInit } from '#angular/core';
import { SEMANTIC_COMPONENTS, SEMANTIC_DIRECTIVES } from "ng-semantic";
import { Course } from '../course';
import { Category } from '../category';
import { PaneComponent } from './pane/pane.component';
import { Degree } from '../degree';
import { DegreeService } from '../../services/degree.service';
const blank: Category = {
name: '',
rank: 1,
rulestat: 'no',
categories: [],
courses: []
}
#Component({
selector: 'my-build',
directives: [SEMANTIC_COMPONENTS, SEMANTIC_DIRECTIVES, PaneComponent],
templateUrl: `app/components/build/build.component.html`,
providers: [DegreeService]
})
export class BuildComponent implements OnInit{
constructor(private degreeService: DegreeService){}
level: number = 1;
currDeg: Degree;
parents = [blank, blank, blank, blank];
setLast(lst: Category){ //pass category objects, do all UI changing here
this.level = lst.rank + 1;
this.parents[lst.rank - 1] = lst;
}
getDegree(){
//this.degreeService.getDegree().then(deg => this.currDeg = deg)
this.currDeg = this.degreeService.getDegree();
}
ngOnInit(){
this.getDegree();
}
}
I don't know how you use the currDeg in your template but with promises, things are asynchronous. So the corresponding object will be undefined at the beginning since it will be set later (when the promise is resolved). And this, even if the promise is directly resolved with Promise.resolve.
export class DegreeService{
getDegree(){
return Promise.resolve(SAMPLE);
}
}
#Component({
selector: 'my-app',
providers: [DegreeService],
templateUrl: 'src/app.html'
})
export class App {
constructor(private degreeService:DegreeService) {
}
getDegree(){
this.degreeService.getDegree().then(deg => {
this.currDeg = deg;
console.log('this.currDeg = ' + this.currDeg); // <------
});
}
ngOnInit(){
this.getDegree();
}
}
See this plunkr: https://plnkr.co/edit/1fxE0okyMNj2JktURY4w?p=preview.

Categories