Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

findAllComponents throws error when called inside function data #879

Closed
codler opened this issue Jun 25, 2014 · 7 comments
Closed

findAllComponents throws error when called inside function data #879

codler opened this issue Jun 25, 2014 · 7 comments

Comments

@codler
Copy link
Member

codler commented Jun 25, 2014

I dont know if it is a bug or correct. I understand if it is correct due to there are a scenario when it is possible func() are called before "component instance" have been created, but I wonder how you could solve that when it comes to that scenario.
http://jsfiddle.net/Xxcdd/

{{func()}}
ractive = new Ractive({
    el: 'main',
    template: '#template',
    data: { 
        func: function() {
            try {
                console.log('enter func');
                this.findAllComponents('any');
            } catch(e) {
                console.error(e);
            }
        }
    }
});
@martypdx
Copy link
Contributor

@codler What's happening is that the fragment is being initialised in ractive initialise:

ractive.fragment = new Fragment( {
    template: ractive.template || [],
    root: ractive,
    owner: ractive
} );

And inside that new Fragment call, it encounters this inside the ractive.findAllComponents:

this.fragment.findAllComponents( selector, query );

Which doesn't exist! (yet). I could separate out the instantiation from init:

ractive.fragment = new Fragment( {
    template: ractive.template || [],
    root: ractive,
    owner: ractive
} );
ractive.fragment.init();

But there would still be no guarantee that the components had been created.

Might be better to use a data value that is updated on init. What are you trying to do?

@codler
Copy link
Member Author

codler commented Jun 25, 2014

I am making a "Tab UI". It is kinda like this

<tabHead for="relatedId" on-select="tabSelect" >
    {{#items:i}}
    <tabHeader text="{{func(i,.)}}" on-select="bindSelect" />
    {{/items}}
</tabHead>
<div id="relatedId">
    {{#items:i}}
    <tabBody item="{{.}}" />
    {{/items}}
</div>

The component tabBody will do an ajax request and fill its content and its instance will have a "title" value which I want to show in tabHeader and to do that in func() I am doing return ractive.findAllComponents('tabBody')[index].title.

So I believe when adding an item to items func() will be called before tabBody are rendered.

@martypdx
Copy link
Contributor

Why don't you just bind to the data?

<tabHead for="relatedId" on-select="tabSelect" >
    {{#items:i}}
    <tabHeader text="{{title}}" on-select="bindSelect" />
    {{/items}}
</tabHead>
<div id="relatedId">
    {{#items:i}}
    <tabBody item="{{.}}" />
    {{/items}}
</div>

@martypdx
Copy link
Contributor

@codler To work, the tabHeader needs an obj ref, not a string: http://jsfiddle.net/67Ahf/

@codler
Copy link
Member Author

codler commented Jun 25, 2014

I have a bit more complex data model then I described.
It is actually like this today and it is working now but with workarounds.

<tabHead for="relatedId" on-select="tabSelect" >
    {{#openedItems:i}}
    <tabHeader text="{{openedItemsText(i,.)}}" on-select="bindSelect" />
    {{/openedItems}}
</tabHead>
<div id="relatedId">
    {{#openedItems:i}}
    <searchItem item="{{item}}" />
    {{/openedItems}}
</div>
var openedItems = [];
// Example of openedItems structure
openedItems.push({
  ...
  item: new Item() // Item have a bunch of instance methods. It will be Item that fetches via ajax and stores to its self instance
});

/* Copy pasting some typescript code from my project below */

var ractive: Ractive = new Ractive({
    data: {
        openedItems: openedItems,
        openedItemsText: function (index: number, openedItem: any): string {
            // TODO: see if if-statement is needed https://github.com/ractivejs/ractive/issues/868
            if (typeof index == 'undefined') return;

            if (openedItem.view == 'add') {
                return self.translation.getString(self.translation.NEW_DOCUMENT_TAB);
            }

            var searchItem: Ractive;

            // This try catch are needed here due to findAllComponents throws errors and due to loading sequence in the template.
            // The ambigious here is openedItemsText are called before searchItem instance are created.
            try {
                // Another bug. Must be "ractive" and not "this" here
                searchItem = ractive.findAllComponents('searchItem')[index];
            } catch (e) {
                return;
            }

            var item: ICP.CMItem = searchItem.get('item');
            var title: string = < string > item.getAttributeValue(item.representsItem) || self.translation.getString(self.translation.NO_NAME);

            if (searchItem.get('view') == 'displayOld') {
                title += ' (' + item.version + ')';
            }

            return title;
        }
    }
    ...
});

@martypdx
Copy link
Contributor

Put that logic in the tabHeader component in a computed property (and it will self-update as things change, if that's what you want. :)

tabHeader: Ractive.extend({
                template: '<span>{{text}}</span>',
                computed: {
                    text: function(){
                        var view = this.get('item.view');
                        if(view==='add') { return 'NEW ITEM' }
                        var text = this.get('item.representsItem') || 'NO NAME'
                        if(text==='display old') { 
                            text += ' (' + this.get('item.version') + ')';
                        }
                        return text
                    }       
                } 
            }),

@codler
Copy link
Member Author

codler commented Jun 26, 2014

Thanks for the suggestion @martypdx !
Yes best would be if it could self-update. By moving the logic to tabHeader I cant reach component instance which I need to know what "view" it is on and tabHeader wouldnt be generic tab UI anymore either.

@Rich-Harris Rich-Harris added this to the no milestone milestone Jul 3, 2014
@Madgvox Madgvox closed this as completed Oct 24, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants