3

I am dabbling with learning Angular 4 and I am running into a stumbling block.

I have the following app/content.component.ts file:

import { Component, Input, OnInit } from '@angular/core';
import { AngularFire, FirebaseListObservable } from 'angularfire2';
import { Content } from './content';

@Component({
    selector: 'app-content',
    templateUrl: './content.component.html'
})

export class ContentComponent implements OnInit {
    @Input() loadScripts: string;
    items: FirebaseListObservable<any[]>;
    constructor(af: AngularFire) {
        this.items = af.database.list('/pages', { 
            query: {
                orderByChild: 'sortOrder',
                limitToLast: 100
            }
        });
    }
    ngOnInit() {
        console.log(this.loadScripts);
    }
}

I have the following app/content.component.html file:

<div id="content-{{item.$key}}"  *ngFor="let item of items | async | path : 'path'">
    <h2>{{item.title}}</h2>
    <div [innerHTML]="item.content | trustedHtml"></div>
</div>

"items" is a result set from Firebase. I need to pass the value of "item.loadScripts" to the loadScripts variable in the ContentComponent class.

How do I do this?

1
  • Why do you have to do it in template? Commented Apr 19, 2017 at 18:50

2 Answers 2

2

You can subscribe to the observable yourself (moved it to ngOnInit). In the subscription you can set this.items and iterate over the result. Because of this change you want to keep track of your subscription and unsubscribe on destruction.

import { Component, Input, OnInit, OnDestroy } from '@angular/core';
import { AngularFire, FirebaseListObservable } from 'angularfire2';
import { Content } from './content';

@Component({
    selector: 'app-content',
    templateUrl: './content.component.html'
})

export class ContentComponent implements OnInit, OnDestroy {
    @Input() loadScripts: string;
    subscriptions: Array<any> = [];
    items: Array<any>;
    constructor(private af: AngularFire) {}
    ngOnInit() {
        this.subscriptions.push(
            this.af.database.list('/pages', { 
                    query: {
                        orderByChild: 'sortOrder',
                        limitToLast: 100
                    }
                }).subscribe((items) => {
                    this.items = items;
                    this.items.forEach((item) => {
                        // do something with each item.loadScripts
                    });
                });
            );
    }
    ngOnDestroy() {
     this.subscriptions.forEach(s => s.unsubscribe());
    }
}

Make sure to remove the async pipe from your html.

<div id="content-{{item.$key}}"  *ngFor="let item of items | path : 'path'">
    <h2>{{item.title}}</h2>
    <div [innerHTML]="item.content | trustedHtml"></div>
</div>

If you always want to apply the filter, update the code inside the subscribe to something like this (and remove the filter from the html):

this.items = items
    .filter((item) => {
        return checkPathFilter(item);
    });

this.items.forEach((item) => {
    // do something with each item.loadScripts
});

If you always filter down to 1 item, then you can remove the ngFor all together and do this in the subscribe:

this.item = items
    .find((item) => {
        return checkPathFilter(item);
    });

this.loadScripts = item.loadScripts;
Sign up to request clarification or add additional context in comments.

8 Comments

Since my path pipe does some filtering on the results, do I also need to apply similar filtering logic to the subscribe method in the ngOnInit?
Also, now that the observable is inside the ngOnInit, I get find name 'af' because it is no longer inside the constructor?
For the second question, change the constructor's signature to protected af: AngularFire, and the af service will be set as a member on your class, accessible via this.af.
For your first question, you'll need to repeat the filtering process yes. You can inject the pipe in the constructor and use its transform method, as in this answer
Just updated the code to what @FraserES mentioned. Your other filter should still work. Removing items is the same as after the async pipe. You will need to repeat the filter in the function too.
|
2

You should move the code which is currently in the constructer into the onInit() method I think.

Since the contents of your firebase request are already returning to the component, you don't need to pass them back from the template. You just need to copy whatever comes back from firebase during your request handling function into the loadScripts variable. However, if loadScripts is defined on each item, then your loadScripts class property will need to be an Array, rather than just a string:

this.items = af.database.list('/pages', { 
        query: {
            orderByChild: 'sortOrder',
            limitToLast: 100
        }
    });
this.items.subscribe(items => this.loadScripts=items);

However, I have a few questions. What do you need to do with each value of loadScripts?

If you need to process each item differently, you'd be better off writing a child component which takes an item as an input and then processes/renders it. If the processing is more simple, you could define a function on your ContentComponent which takes an item as a single argument and then returns whatever value you need.

5 Comments

Didn't see that Joseph Connolly posted below - his answer is more in depth, except for the bit about asking what you want to do with each item :)
See my question to Joseph Connolly above, maybe you can answer too?
I ultimately need only one item, but the problem is I need to filter the results before hand. Because Firebase lacks robust querying capabilities like something MS SQL, I end up putting this filtering logic into the custom 'path' pipe.
Ah - @JosephConnolly has updated his answer and I agree with him!
Thanks for your help on that. Still having a couple of issues (see above) but SO close.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.