Famous/framework

Cannot use `$if` together with setting `$state` from within `$lifecycle`

Opened this issue · 9 comments

In short, I can't use $if together with setting $state from within post-load.

This works:

FamousFramework.scene('ildar:page-controller', {
    behaviors: {
        // '#home': {
        //     '$if': function(path) { return path === '/'  }
        // },
        // '#blog': {
        //     '$if': function(path) { return path === '/blog'  }
        // },
        // '#about': {
        //     '$if': function(path) { return path === '/about' }
        // },
        '.page': {
            'size': [200, 200],
            'align': [0.5, 0.5],
            'mount-point': [0.5, 0.5],
            'style': {
                'background': 'whitesmoke'
            }
        }
    },
    events: {
        '$lifecycle': {
            'post-load': function($state) {
                console.log('post-load');
                $state.set('path', '/');
            }
        },
        '.page': {
            'click': function($state) {
                console.log('WORKS');
                $state.set('path', '/blog');
            }
        }
    },
    states: {
        path: null,
    },
    tree: `
        <node id="home"  class="page">  <div> HOME  </div>  </node>
        <node id="blog"  class="page">  <div> BLOG  </div>  </node>
        <node id="about" class="page">  <div> ABOUT </div>  </node>
    `
});

This also works:

FamousFramework.scene('ildar:page-controller', {
    behaviors: {
        '#home': {
            '$if': function(path) { return path === '/'  }
        },
        '#blog': {
            '$if': function(path) { return path === '/blog'  }
        },
        '#about': {
            '$if': function(path) { return path === '/about' }
        },
        '.page': {
            'size': [200, 200],
            'align': [0.5, 0.5],
            'mount-point': [0.5, 0.5],
            'style': {
                'background': 'whitesmoke'
            }
        }
    },
    events: {
        '$lifecycle': {
            'post-load': function($state) {
                console.log('post-load');
                // 
                // If $state isn't set from here, the code works.
                // 
                // $state.set('path', '/');
            }
        },
        '.page': {
            'click': function($state) {
                console.log('WORKS');
                $state.set('path', '/blog');
            }
        }
    },
    states: {
        path: '/',
    },
    tree: `
        <node id="home"  class="page">  <div> HOME  </div>  </node>
        <node id="blog"  class="page">  <div> BLOG  </div>  </node>
        <node id="about" class="page">  <div> ABOUT </div>  </node>
    `
});

But this doesn't work:

FamousFramework.scene('ildar:page-controller', {
    behaviors: {
        '#home': {
            '$if': function(path) { return path === '/'  }
        },
        '#blog': {
            '$if': function(path) { return path === '/blog'  }
        },
        '#about': {
            '$if': function(path) { return path === '/about' }
        },
        '.page': {
            'size': [200, 200],
            'align': [0.5, 0.5],
            'mount-point': [0.5, 0.5],
            'style': {
                'background': 'whitesmoke'
            }
        }
    },
    events: {
        '$lifecycle': {
            'post-load': function($state) {
                console.log('post-load');
                $state.set('path', '/');
            }
        },
        '.page': {
            'click': function($state) {
                console.log('WORKS');
                $state.set('path', '/blog');
            }
        }
    },
    states: {
        path: null,
    },
    tree: `
        <node id="home"  class="page">  <div> HOME  </div>  </node>
        <node id="blog"  class="page">  <div> BLOG  </div>  </node>
        <node id="about" class="page">  <div> ABOUT </div>  </node>
    `
});

Could be related to #41

Looks like the same issue as #47 where event handlers aren't registered to nodes that are created after the component has loaded.

In the 3rd example the click handler is not registered presumably because the .page node was created after the component loaded.

@Imti

@ildarsamit @djgrant, thanks for bringing this issue to our attention. I'll work on getting a fix to this in ASAP.

@ildarsamit Fixed with 6b86475.

The issue was a result of the MutationObserver being created after the post-load event was fired. In our current implementation, the MutationObserver listens to changes in the underlying detached DOM, and adds event listeners such as click whenever new <node>s are added to our scene via the control flow conduit. By fixing the order, the MutationObserver properly responds to a new node being added on the post-load event.

Can you double check that this issue has been resolved?

@arkadyp how do you update an existing project? I don't see any command within the cli and famous-framework is not on npm.

@djgrant to run a project you usually run npm run dev. But that's just a script that can point anywhere, so you can actually run something like node path-to-framework/bin/famous-framework.js local-only-bootstrap --sourceFolder=components --destinationFolder=public/build --servedFolder=public --port=1618.

So here's my setup:

I git clone the Famous Framework repo (so I can switch to any branch or commit etc). Then run the bin/famous-framework.js script. My package.json file inside of my project looks like this:

{
  "name": "famous-framework-test",
  "version": "0.0.0",
  "description": "A Famous Framework component",
  "license": "",
  "private": true,
  "scripts": {
    "dev": "node ../framework/bin/famous-framework.js local-only-bootstrap --sourceFolder=components --destinationFolder=public/build --servedFolder=public --port=1618"
  }
}

This lets me run npm run dev as usual but using my version of the Framework.

I'm by no means a Node expert though, so there might be a better way, but this works for me :)

I'll try to find some time to double-check the fix, but I've already found a hackish workaround already for my own purpose.

Thanks @ildarsamit. Now you've mentioned it I realise you can just reference the commit directly in package.json dependencies.

"dependencies": {
    "famous-framework": "Famous/framework#6b86475"
  }

See https://docs.npmjs.com/files/package.json#github-urls.

@djgrant oh yeah, sorry you can just do that lol. I have the repo checked out in case I wanna play around with the actual code etc.

@arkadyp Taking a quick look at this, the original problem seems to be resolved. Thanks!

Perhaps a separate issue, it's still not possible to attach event handlers to HTML elements inserted by the inner-html behavior.

@djgrant Thanks for taking a look.

We're aware of the separate issue of not being able to attach event handlers to HTML elements created via the inner-html behavior. Under the hood, the setContent method is called on Engine's DOMElement when the inner-html behavior is applied. Currently, there is not a good way to get a reference to the DOM element on the page via Engine's DOMElement. (See Engine Issue #183 for a discussion.) We are working with the Engine team to figure out a solution to this problem so that Framework users can apply event handlers directly to HTML elements.