I'm trying to pass my Get method from my parent component to my child component to display the data but I get an error I don't understand.
the error comes from my service
here is the error Cannot read property 'charAt' of undefined
thanks in advance.
parent.component.html
<app-child [arrayList]="getDetails()"></app-child>
parents.component.ts
public array: any[] = [];
getRoute() {
this.route.paramMap.subscribe((params: Param) => {
this.id = params.get('id');
this.material = params.get('matos');
}
}
getDetails() {
this.service.get(this.id, this.material).subscribe((data:any[]) => {
this.array.push(data);
}
}
parent.service
get(id: string, material:string) {
let material = this.str(material:string);*
return this.get<any>(url+id+material);
str(material) {
return material.charAt(0).toUpperCase()+material.substr(1);
}
child.component.ts
#Input() arrayList: any[] = [];
There are several glitches in your code, first in child component add input decorator, so that it accepts the input i.e
child.component.ts
#Input() arrayList: any;
parents.component.html
In parents component you are passing getDetails() method to input which will always be undefined since its not returning anything, try to pass array list in which you are pushing the data
<app-child [arrayList]="array"></app-child>
Related
I'm passing a value from a service to a component using BehaviorSubject -
In serviceFile.service.ts:
taskComplete = new BehaviorSubject<{ complete: Boolean; error: any }>(null);
...
this.taskComplete.next({ complete: false, error: error });
...
In componentFile.component.ts:
ngOnInit() {
this.taskCompleteSub = this.myService.taskComplete.subscribe(
(data) => {
this.error = data.error
? data.error.error.message
: null;
console.log(this.error);
}
);
}
The problem is that the value of property this.error is changed and printed in console.log(), but this change is not reflected in the component template. In other words, angular does not check this change and re-render.
You are initializing your taskComplete BehaviorSubject with null, so that's the first value emitted. However, in your component you are trying to access data.error when data is null for the first value emitted. The following should work:
this.error = data && data.error && data.error.error
? data.error.error.message
: null;
I created this working example: https://stackblitz.com/edit/angular-fh6cfg?file=src%2Fapp%2Fapp.component.ts
If this.myService.taskComplete is an asynchronous action you'll need to manually trigger change detection.
constructor(private cdr: ChangeDetectorRef) { }
...
ngOnInit() {
this.taskCompleteSub = this.myService.taskComplete.subscribe(
(data) => {
this.error = ...;
this.cdr.markForCheck();
}
);
}
I'd suggest two changes.
If the default value of the BehaviourSubject is null and if you're forced to check if the value is null in each of it's subscription, you're better off using a ReplaySubject with buffer 1 instead. It'll buffer/hold the last value similar to BehaviorSubject but doesn't require a default value.
If the object's underlying reference hasn't changed, the Angular change detection may detect any changes to re-render the template. In that case try to make a hard-copy using JSON.parse(JSON.stringify()).
Service
taskComplete = new ReplaySubject<{ complete: Boolean; error: any }>(1);
Component
ngOnInit() {
this.taskCompleteSub = this.myService.taskComplete.subscribe(
(data) => {
// `null` check not required here now
this.error = JSON.parse(JSON.stringify(data.error.error.message));
console.log(this.error);
}
);
}
I have this model :
export interface AddAlbumeModel {
name: string;
gener: string;
signer: string;
albumeProfile:any;
albumPoster:any;
tracks:TrackMode[];
}
export interface TrackMode {
trackNumber: number;
trackName: string;
trackProfile: any;
trackPoster:any;
trackFile: any;
}
And I want to add tracks with this code :
let addModel = {} as AddAlbumeModel;
for (let index = 0; index < this.addAlbumFG.controls['tracks']['controls'].length; index++) {
const item= this.addAlbumFG.controls['tracks']['controls'][index]['controls'];
addModel.tracks.push({
trackFile:item.trackFile.value['files'][0],
trackNumber:item.trackNumber.value,
trackName:item.trackName.value,
trackPoster:item.trackPoster.value['files'][0],
trackProfile:item.trackProfile.value['files'][0]
})
}
But it shows me this error :
ERROR TypeError: Cannot read property 'push' of undefined
how can I solve this problem ???
Hi below error came because you did not declare available as an array:
ERROR TypeError: Cannot read property 'push' of undefined
Do first:
let addModel = {} as AddAlbumeModel;
addModel.tracks = [];
for (let index = 0; index < this.addAlbumFG.controls['tracks']['controls'].length;
index++) {
const item= this.addAlbumFG.controls['tracks']['controls'][index]['controls'];
addModel.tracks.push({
trackFile:item.trackFile.value['files'][0],
trackNumber:item.trackNumber.value,
trackName:item.trackName.value,
trackPoster:item.trackPoster.value['files'][0],
trackProfile:item.trackProfile.value['files'][0]
})
}
Now done :)
The above fails because the property tracks is undefined.
In typescript, Interfaces are used for type checking only, interfaces are lost after the code is transpiled to JavaScript.
Here you have a couple of solutions:
Initialize property before using it
One thing you can do is initialize tracks before you use it.
interface TrackMode {
// props
}
interface AddAlbumeModel {
// other props
tracks: TrackMode[];
}
let addModel: AddAlbumeModel = {
tracks: []
} as AddAlbumeModel;
addModel.tracks.push(...)
Use class instead of interface
Interfaces can't have default values but classes can, so you can use a class instead and set defaults value for tracks as an empty array.
interface TrackMode {
// props
}
class AddAlbumeModel {
// other props
tracks: TrackMode[] = [];
}
const addModel = new AddAlbumeModel()
addModel.tracks.push(...)
Hi I have an issue like in subject:
My Parent component passes the value to his child component like this:
<em-event-list [sumListEvents]="sumListEvents"></em-event-list>
So when when value sumListEvents will change I want to subscribe that value to call out the function from Service. This is how my children component looks:
#Input() sumListEvents: Observable<number>;
private events: Event[] = [];
constructor(private dataService: EventListService) {
let loadSubscription = this.sumListEvents.subscribe(
value => {
this.events = this.dataService.showEvents(value)
}
)
}
But I receive error on subscribe Cannot read property 'subscribe' of undefined in the sumListEvents. Any ideas?
sumListEvents is not available at the moment you need it. You can use *ngIf to subscribe only when sumListEvents available.
<em-event-list *ngIf [sumListEvents]="sumListEvents"></em-event-list>
This will work but it might not be a best practise to pass event to the child and subscribe it in a child.
You may refer to https://scotch.io/tutorials/3-ways-to-pass-async-data-to-angular-2-child-components for other ways to pass async data to child component.
A better approach will be to use the async pipe and a setter.
<em-event-list *ngIf [sumList]="sumListEvents$ | async"></em-event-list>
#Input() set sumList(value) {
if(value) {
this.events = this.dataService.showEvents(value);
}
}
private events: Event[] = [];
constructor(private dataService: EventListService) { }
I am using angular 5
my scenario is from one component im setting the data in service and from another component i'm getting that data|
Component- 1:
makeUser(row){
this.agentsService.setSelectedAgentData(row); // setting the data (row) in agentsService.
const mrf = this.modalService.open(MakeUserComponent);
}
Service:
declaring the varible in class.
public selectedData:any = {};
setter and getter methods are as below
setSelectedAgentData(selectedTableRowData){
this.selectedData = selectedTableRowData;
}
getSelectedAgentData(){
return this.selectedData;
}
Component - 2:
ngOnInit() {
this.userDetails = this.agentsService.getSelectedAgentData();
this.roles = this.agentsService.getRolesList();
}
Here the selectedData value is an empty object when I call the method this.agentsService.getSelectedAgentData() from component -2
Any help would be appreciated.
You can use Subject (rxjs library) for this purpose. So Subject can generate data on the one hand. And on the other hand, you can subscribe to changes in any place.
You service would look like this:
#Injectable()
export class YourService {
public selectedData: Subject<any>;
constructor() {
this.selectedData = new Subject();
}
generateSelectedAgentData(row: string) {
this.selectedData.next(row);
}
}
In your first Component:
makeUser(row){
this.agentsService.generateSelectedAgentData(row);
const mrf = this.modalService.open(MakeUserComponent);
}
In your second Component:
constructor(private ys: YourService){
this.ys.selectedData.subscribe(
data => {
console.log(data);
this.userDetails = data;
});
}
I have issue, with passing async data to child component. I trying to write dynamic form generator. Issue starts when I try to call json via Observable and pass it into child component.
service:
generateSearchFields2(): Observable<any> {
return this.http
.get(this.API + 'searchFields')
.map((res:Response) => {
res.json().data as any;
for (var i = 0; i < res.json().data.length; i++) {
var searchField = res.json().data[i];
switch (searchField.component) {
case "TextboxQuestion":
let TXQ: TextboxQuestion = new TextboxQuestion({
key: searchField.key,
label: searchField.label,
value: searchField.value,
required: searchField.required,
order: searchField.order
});
this.searchFieldModels.push(TXQ);
console.log("TXQ: ", TXQ, this.searchFieldModels);
break;
case "DropdownQuestion":
let DDQ: DropdownQuestion = new DropdownQuestion({
key: searchField.key,
label: searchField.label,
required: searchField.required,
options: searchField.options,
order: searchField.order
});
this.searchFieldModels.push(DDQ);
console.log("TXQ: ", DDQ, this.searchFieldModels);
break;
default:
alert("DEFAULT");
break;
}
}
return this.searchFieldModels.sort((a, b) => a.order - b.order);
})
.catch(this.handleError);
}
Component Parent:
generateSearchFields2() {
this.service.generateSearchFields2()
.subscribe(res => this.searchFields = res)
}
Iam passing variable via INPUT directive in parent template to child: [searchFields]="searchFields"
Issue is in child component, where searchField has undefined value. In this child I pass value to another service, to create formContros, but I got undefined there also. Data missing starts here, in child:
#Input() searchFields: SearchBase<any>[] = [];
ngOnInit() {
this.form = this.qcs.toFormGroup(this.searchFields);
console.log("ONINIT DYNAMIC FORM COMPONENT: ", this.searchFields);
}
Please for hint how I can pass async variable, to not loose data meantime
You can make #Input() searchFields a setter
private _searchFields: SearchBase<any>[] = [];
#Input() set searchFields(value SearchBase<any>[]) {
if(value != null) {
this.form = this.qcs.toFormGroup(this.searchFields);
console.log("ONINIT DYNAMIC FORM COMPONENT: ", this.searchFields);
}
}
get searchFields() : SearchBase<any>[] {
return this.searchFields;
}
You can also use ngOnChanges() which is called every time an input is updated, but a setter is usually more convenient except perhaps when the executed code depends on multiple inputs being set.
In the ngOnInit event the data which comes from the parent is not bound yet. So your searchFields is undefined yet. You can use it in NgAfterViewInit component lifecycle event.
#Input() searchFields: SearchBase<any>[] = [];
ngAfterViewInit() {
this.form = this.qcs.toFormGroup(this.searchFields);
console.log("ONINIT DYNAMIC FORM COMPONENT: ", this.searchFields);
}
For other cases you can see Angular2 Component Lifecycle events