marko-js-archive / marko-widgets

[LEGACY] Module to support binding of behavior to rendered UI components rendered on the server or client
http://v3.markojs.com/docs/marko-widgets/
MIT License
141 stars 40 forks source link

client-side rendering issues with `<lasso-slot>` and `out.global` #86

Closed yomed closed 9 years ago

yomed commented 9 years ago

I have a widget template that looks roughly like this:

<lasso-slot name="inline-js" />
<div class="${out.global.wrapperClassName}"> <!-- access out.global in some way -->
    <div w-bind class="${data.widgetClassName}"> <!-- not highest level element in the template -->
        ...
    <div>
</div>

In my index.js file (approximately):

module.exports = require('marko-widgets').defineComponent({
    template: template,

    getInitialState: function(input, out) {
        input.widgetClassName = 'widget'
        return input;
    },

    getTemplateData: function (state, input) {
        return state;
    },

    changeWidgetClassName: function () {
        this.setState('widgetClassName', 'widget2');
    }

The initial server render works fine, but I run into problems whenever I trigger a client rerender via changeWidgetClassName. It seems to choke on the lasso-tag, even though it is outside the w-bind. The .marko.js file contains something like this:

__tag(out,
      _________node_modules_lasso_taglib_slot_tag_js,
      {
        "name": "inline-js"
      });

And in the rerender it errors out:

Uncaught Error: Lasso page result not found for slot "inline-js-before-resultset". The <lasso-page> tag should be used to lasso the page.render @ slot-tag.js:20
module.exports.t @ helpers.js:275
render @ template.marko.js:37
Template.render @ marko-runtime.js:192
renderer @ defineRenderer.js:191
exports.render @ raptor-renderer.js:67
(anonymous function) @ Widget.js:463
batchUpdate @ update-manager.js:80
widgetProto._rerender @ Widget.js:461
widgetProto.rerender @ Widget.js:484
widgetProto.doUpdate @ Widget.js:390
widgetProto.update @ Widget.js:352
updateWidgets @ update-manager.js:59
batchUpdate @ update-manager.js:86
(anonymous function) @ marko-widgets-browser.js:109

If I remove the lasso-tag, it chokes on out.global properties being undefined. And finally if I remove all of those, it still attempts to rerender the entire template, rather than just the w-binded part and below. So a few questions:

1) Does w-bind always have to be at the highest parent level of the template? 2) Should <lasso-slot> be kept out of client-rendered widgets? 3) Are there any gotchas for using out.global on the client?

Thanks

patrick-steele-idem commented 9 years ago

1) Does w-bind always have to be at the highest parent level of the template?

No, it does not

2) Should <lasso-slot> be kept out of client-rendered widgets?

Yes, it should only be in server-side templates.

3) Are there any gotchas for using out.global on the client?

Yes, when a rerender happens it manufactures a new out that will not have any user globals.

yomed commented 9 years ago

Is there any way to still use out.global? It seems like that should be available on both sides somehow.

patrick-steele-idem commented 9 years ago

What do you need out.global in the browser for? I want to better understand your use case

yomed commented 9 years ago

I actually don't need out.global or <lasso-tag> on the client, but just having those present in a template that gets rendered clientside (even if they're ignored via preserve or otherwise) will error and prevent the rerender. I imagine the use-case for maintaining access to out.global might come up in the future, so I was more curious on that piece.

patrick-steele-idem commented 9 years ago

During a rerender, the top-level widget can add additional things to out.global.

It's bad practice if Lasso.js related tags are embedded in the templates for UI components. Lasso.js tags belong at the top-level page or page layout.

yomed commented 9 years ago

Ok I'll refactor out the lasso tags. We can revisit out.global usage if it comes up in the future. Thanks!

yomed commented 8 years ago

Hey @patrick-steele-idem, this came up again recently. I have out.global.config on the server, which I would like to have available to widgets initialized on the client. Do you have a suggestion for this use case?

patrick-steele-idem commented 8 years ago

Hey @yomed, what I find works well is to pass configuration data from the server to the browser by adding the data to the widget config of a UI component rendered on the server. For example:

defineComponent({
    getWidgetConfig(input, out) {
        // Assuming this is rendered on the server
        return {
            serverConfig: out.global.config
        };
    },

    init: function(widgetConfig) {
        var serverConfig = widgetConfig.serverConfig;
        // You can do what you want with the server config...
        // For example, store it as part of a single module:
        var myApp = require('src/app');
        myApp.setServerConfig(serverConfig);

        // Or, you could just store it as part of this widget
        this.serverConfig = serverConfig;
    }
})

Does that work for you?

yomed commented 8 years ago

Hmm I think it's a weird use case. We have the component split into renderer/widget, so that we could do server render initially in the future. But for now, we are actually not doing any server-rendering at all of the component itself. However, I suppose I could get a similar result by modifying input to contain config.