I have a Service that calls an API (Http get request) then I call the service within my component to get the data and then assign it to a value. after that I pass the value in the html to another component but it showing as undefined
import { Component } from '#angular/core';
import { AuthService } from '../../src/app/services/auth.service';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'app';
passData: any
constructor(private authService: AuthService) {
}
ngOnInit() {
this.authService.test().subscribe(data => {
for (var i = 0; i < data.length; i++) {
data[i].date = new Date(
data[i].date.substring(0, 4)+'-'+
data[i].date.substring(4, 6)+'-'+
data[i].date.substring(6, 10)+ ' '+data[i].minute)
}
this.passData = data;
});
}
}
below is where I pass in my value to the new component, but showing undefined
<div class="card-deck">
<app-stock-bar [customTitle]=passData></app-stock-bar>
The value of passData when passed is undefined, but if I log it, it will display the value in the console
the answer was very simple after some research I found that you can simply use *ngIF to check if the value is present before you load
<app-stock-bar *ngIf="passData" [customTitle]="passData"></app-stock-bar>
When you are passing the value from parent to child component. You can access it inside onChanges LifeCycle hook.
Component
#Component({})
export class AppStockBar implements OnChanges{
#Input() customTitle;
ngOnChanges(){
console.log(this.customTitle);
}
}
I think the property binding missing double quotes
<app-stock-bar [customTitle]="passData"></app-stock-bar>
Related
I am using this wrapper for the azure maps library. I am currently implementing a symbol layer and using one of the default markers works well, but I am not able to add my own marker. I tried to add a custom marker like in my mapReady function, but the response is always undefined and the image is not added.
this is my component:
import {Component, Input, OnInit} from '#angular/core';
import * as atlas from 'azure-maps-control';
#Component({
selector: 'app-map',
templateUrl: './map.component.html',
styleUrls: ['./map.component.scss']
})
export class MapComponent implements OnInit {
private markerImagePath = 'assets/images/map-marker.png';
public dataSource: atlas.source.DataSource;
markerDescription: 'marker';
public options: atlas.IconOptions = {
image: this.markerDescription
};
points = [
[52.52437, 13.41053],
[51.50853, -0.12574]
];
ngOnInit() { }
mapReady(map: atlas.Map) {
map.imageSprite.add(this.markerDescription, this.markerImagePath).then(r => {
console.log(r);
console.log(map.imageSprite.getImageIds());
this.dataSource = new atlas.source.DataSource('markers');
this.points.forEach(p => {
const point = new atlas.Shape(new atlas.data.Point([p[1], p[0]]));
this.dataSource.add([point]);
});
});
}
}
this is my html:
<section>
<div class="row">
<div class="col-12 map-dimensions my-2 mx-auto" azure-map zoom="2"
[dataSources]="[dataSource]" (onReady)="mapReady($event.map)">
<map-symbol-layer dataSourceId="markers"
[iconOptions]="options"></map-symbol-layer>
</div>
</div>
</section>
I suspect, that I access the map data wrongly... Do any of you guys know, how I can add a custom image to the imageSprites in order for me to use it as a marker in the symbol layer?
Your code looks fine. imageSprite.add returns a Promise<void>, so your console.log will always log undefined. Could your icon be the issue ? I have been trying a similar solution and all works fine on my side :
import { Component } from '#angular/core';
import * as atlas from 'azure-maps-control';
#Component({
selector: 'app-root',
template: '<azure-map zoom="2" [dataSources]="[dataSource]" (onReady)="mapReady($event.map)">' +
'<map-symbol-layer [id]="blueLayerId" dataSourceId="blue" [iconOptions]="blueIconOptions"></map-symbol-layer>' +
'</azure-map>',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
public dataSource: atlas.source.DataSource;
public blueLayerId: string = "blueLayer";
public blueIconOptions: atlas.IconOptions = {
image: 'campground'
};
mapReady(map: atlas.Map) {
map.imageSprite.add('campground', 'assets/campground.png').then(() => {
this.dataSource = new atlas.source.DataSource('blue');
for (let i = 0; i < 10; i++) {
const point = new atlas.Shape(new atlas.data.Point([i * 5, i * 5]));
this.dataSource.add([point]);
}
});
}
}
I'm here because I do not understand how Http works in angular. I would create a "news" thread on my website. To do that I have created a service in my angular app that calls a .net core web API.
Also, I would add a paginate to my thread (I want to display news by 5 on the page).
I can get my values, that is not my issue here. But, to create my paginate, I need to have values for number of pages calculation.
I tried to add code to create my paginate (number of pages, number of elements...) but I always get 0 to these values and my array of news is filled after the onInit(). This is what I don't understand.
This is my component:
import { Component, OnInit, OnDestroy } from '#angular/core';
import { NewsService } from '../news.service';
#Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {
title = 'News';
news = [];
displayed = [];
numberOfPages = 0;
constructor(private newsService: NewsService) { }
ngOnInit() {
// I don't really understand these lines (mainly the subscribe part)
this.newsService.getAllNews().subscribe((data) => {
this.news = Array.from(Object.keys(data), k => data[k]);
// this console.log appears after the onInit(), why ?
console.log(this.news);
});
this.numberOfPages = this.news.length / 5; // Get 0 here, why ?
}
}
My service:
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Injectable({
providedIn: 'root'
})
export class NewsService {
private finalData = [];
private apiUrl = 'https://localhost:5001/api/v1/posts';
constructor(private http: HttpClient) { }
getAllNews() {
return this.http.get(this.apiUrl);
}
}
In the browser console, I get this:
console screen
Maybe I forgot something in my code or I don't know what.
Someone can help me to achieve my goal? I want to understand how to proceed to make a working paginate for my news.
You should add
this.numberOfPages = this.news.length / 5;
inside the subscribe
this.newsService.getAllNews().subscribe((data) => {
this.news = Array.from(Object.keys(data), k => data[k]);
// this console.log appears after the onInit(), why ?
console.log(this.news);
});
like so:
this.newsService.getAllNews().subscribe((data) => {
this.news = Array.from(Object.keys(data), k => data[k]);
// this console.log appears after the onInit(), why ?
console.log(this.news);
this.numberOfPages = this.news.length / 5;
});
My guess is that when you try to initialise the this.numberOfPagesthe this.news.length is not yet set(data are not yet retrieved from the API). Hope this helps
I have been searching everywhere I could not find an answer on this. I'm passing a string on the url example. "localhost:4200/home/ABCD%2BrAD4Og%3D%3D" when I subscribe to the param or use snapshot I get something like "ABCD+rAD4Og=="
How do I get what was exactly passed? Thank you
I have found an answer. If you want to get the raw param that was passed all you have to do is use "encodeURIComponent(uri)"
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-example',
template: `<h1>raw URI example</h1>`,
styleUrls: ['./example.component.scss']
})
export class ExampleComponent implements OnInit {
constructor(private router: ActivatedRoute) {}
rawUri: string = null;
ngOnInit() {
this.router.params.subscribe(param => {
// the "id" part inside param[''] could be anything you defined in your route config file.
this.rawUri = encodeURIComponent(param['id']);
console.log('raw url param ', this.rawUri);
}
}
}
So I'm trying to perform some action on the parent component of the child component when a click event is fired in the child component. Currently I have a dynamic loader which is able to load different child components. The problem I have is that the #Output() is being emitted but the parent component doesn't seem to have any knowledge when this event is fired. Is there something I am missing?
child2.component.ts
import {Component, Injector, Output, EventEmitter} from '#angular/core';
#Component({
selector: 'hello-world',
template: `
<div>Hello World {{showNum}}</div>
<li (click)="childButtonClicked(false)"> </li>
`,
})
export class HelloWorldComponent {
showNum = 0;
#Output() childEvent = new EventEmitter<boolean>();
constructor(private injector: Injector) {
this.showNum = this.injector.get('showNum');
console.log("HelloWorldComponent");
}
childButtonClicked(agreed: boolean) {
this.childEvent.emit(agreed);
console.log("clicked");
}
}
child1.component.ts
import {Component, Injector, Output, EventEmitter} from '#angular/core';
#Component({
selector: 'world-hello',
template: `
<div>World Hello {{showNum}}</div>
<li (click)="childButtonClicked(false)"> </li>
`,
})
export class WorldHelloComponent {
showNum = 0;
#Output() childEvent = new EventEmitter<boolean>();
constructor(private injector: Injector) {
this.showNum = this.injector.get('showNum');
console.log("WorldHelloComponent");
}
childButtonClicked(agreed: boolean) {
this.childEvent.emit(agreed);
console.log("clicked");
}
}
dynamic.componentloader.ts
import {Component, Input, ViewContainerRef,ComponentRef, ViewChild, ReflectiveInjector, ComponentFactoryResolver} from '#angular/core';
#Component({
selector: 'dynamic-component',// Reference to the components must be here in order to dynamically create them
template: `
<div #dynamicComponentContainer></div>
`,
})
export class DynamicComponent {
currentComponent:any = null;
#ViewChild('dynamicComponentContainer', { read: ViewContainerRef }) dynamicComponentContainer: ViewContainerRef;
// component: Class for the component you want to create
// inputs: An object with key/value pairs mapped to input name/input value
#Input() set componentData(data: {component: any, inputs: any }) {
if (!data) {
return;
}
// Inputs need to be in the following format to be resolved properly
let inputProviders = Object.keys(data.inputs).map((inputName) => {return {provide: inputName, useValue: data.inputs[inputName]};});
let resolvedInputs = ReflectiveInjector.resolve(inputProviders);
// We create an injector out of the data we want to pass down and this components injector
let injector = ReflectiveInjector.fromResolvedProviders(resolvedInputs, this.dynamicComponentContainer.parentInjector);
// We create a factory out of the component we want to create
let factory = this.resolver.resolveComponentFactory(data.component);
// We create the component using the factory and the injector
let component = factory.create(injector);
// We insert the component into the dom container
this.dynamicComponentContainer.insert(component.hostView);
// We can destroy the old component is we like by calling destroy
if (this.currentComponent) {
this.currentComponent.destroy();
}
this.currentComponent = component;
}
constructor(private resolver: ComponentFactoryResolver) {
}
}
main.component.ts
import { Component, OnInit } from '#angular/core';
import { HelloWorldComponent } from '../../views/main/sidebar-views/comps/hello-world.component';
import { WorldHelloComponent } from '../../views/main/sidebar-views/comps/world-hello.component';
#Component({
selector: 'main-component',
template: require('./main.component.html')
})
export class MainComponent {
private pressed: boolean = false;
componentData:any = null;
constructor() { }
createHelloWorldComponent(){
this.componentData = {
component: HelloWorldComponent,
inputs: {
showNum: 9
}
};
}
createWorldHelloComponent(){
this.componentData = {
component: WorldHelloComponent,
inputs: {
showNum: 2
}
};
}
test(){
console.log("some click event");
}
};
main.component.html
<div>
<h2>Lets dynamically create some components!</h2>
<button (click)="createHelloWorldComponent()">Create Hello World</button>
<button (click)="createWorldHelloComponent()">Create World Hello</button>
<dynamic-component [componentData]="componentData" (childEvent)="test()"></dynamic-component>
</div>
Since you are passing a parameter to the EventEmitter, you need to change your event binding on your component selector in your template to this:
<dynamic-component [componentData]="componentData" (childEvent)="test($event)"></dynamic-component>
Also, don't forget to change function signature in your component to accept the parameter:
test(agreed: boolean){
console.log("some click event");
}
More info on official docs.
In my angular 2 application there is a component containing an array of objects that is passing the chosen (clicked) one to it's direct child component. This does display the data more detailed. I'm using the "SimpleChanges" feature to watch in this child component if the object given changed to make another http request to get the related comments from a database.
If I try to build it with npm I get an error, saying :
app/displayEntry.component.ts(23,41): error TS2339: Property 'entry' does not exist on type 'SimpleChanges'
If I just comment this part out, start npm and finally put it in there again and save it, there is no Problem anymore ( no erro and it works ).
My question is, is there a way to work around this behavior and can this cause any trouble later I don't foresee or should I just ignore it? Thanks for your help
Parent component:
import { Component, OnInit } from '#angular/core';
import { entry } from './Objekte/entry';
import { entryService } from './entry.service'
#Component({
templateUrl: 'app/Html_Templates/displayLastEntrys.template.html'
})
export class displayLastEntrys implements OnInit{
public entrys : entry[];
private entryChoosen: boolean;
private ChoosenEntry : entry;
constructor ( private entryservice : entryService){
this.entryChoosen = false;
}
ngOnInit() : void {
this.getData();
}
getData() {
this.entryservice.getFirstEntrys().then(entrys => this.entrys = entrys);
}
entryClicked(ent: entry){
this.entryChoosen = true;
this.ChoosenEntry = ent;
}
leaveEntry () {
this.entryChoosen = false;
}
voted( upordown : boolean ) {
}
}
Child component:
import { Component, Input, Injectable, OnChanges , SimpleChanges, Output, EventEmitter} from '#angular/core';
import { entry} from './Objekte/entry';
import { entryService } from './entry.service';
import { comment } from './Objekte/comments';
#Component({
selector: 'display-entry',
templateUrl: 'app/Html_Templates/displayEntry.template.html'
})
export class displayComponent implements OnChanges{
#Input() public entry : entry;
public comments : comment[];
private changecounter : number;
constructor(private service : entryService) {
this.changecounter = 0;
}
ngOnChanges(changes : SimpleChanges){
this.service.getComments(changes.entry.currentValue.id)
.then(com => this.comments = com )
.catch();
this.entry.viewed++;
// To implement :: change database
}
votedUp () : void {
this.entry.votes ++;
// To implement :: change database
}
votedDown () : void {
this.entry.votes --;
// To implement :: change database
}
}
The accepted solution is suboptimal for TypeScript, as you're defeating the type system.
SimpleChanges does not have an entry property, so the compiler quite rightly balks. The solution is to treat the changes object as an array:
ngOnChanges(changes : SimpleChanges){
if (changes['entry']) {
this.service.getComments(changes['entry'].currentValue.id)
}
}
Then you can continue to strongly type the ngOnChanges method.
To make the compiler not complain just change your method definition for parameter one from SimpleChanges to any:
ngOnChanges(changes: any) {
//...
Maybe it's changed a lot now but this works these days
import {Component, Input, OnChanges, SimpleChanges} from '#angular/core';
import {ConfigModel} from './config.model'
#Component({
selector: 'selector',
templateUrl: './template.html',
styleUrls: ['./styles.scss']
})
export class BlaComponent implements OnChanges {
#Input() config: ConfigModel;
ngOnChanges(changes: SimpleChanges): void {
if (changes.config && changes.config.currentValue) {
let config = <ConfigModel>changes.config.currentValue;
// do more
}
}
}
I myself got the compile error because i wasn't using .currentValue after calling changes.config
If you are completely dependent on the IDE's auto-completion, make sure to actually use SimpleChanges instead of just SimpleChange. A very thing to be overlooked at.