marko-js / marko

A declarative, HTML-based language that makes building web apps fun
https://markojs.com/
MIT License
13.35k stars 643 forks source link

different behavior for include tag #768

Closed hedav85 closed 4 years ago

hedav85 commented 7 years ago

I'm a bit confused about the include tag.

If I use the include tag in the following way, the template will be loaded and rendered correctly. <include('../path/to/my/template/index.marko', {...})/>

But if I use it with expressions like <include('../path/to/my/template/' + 'index.marko', {...})/> then just the string of the path is rendered.

My target is to dynamicly import components like e.g. <include(data.templatePath + data.templateName, {...})/>

Do I anything wrong?

Thanks!

Update:

If I do a var template = require('../path/to/my/template/' + 'index.marko') and then in the template <include(data.template, {...})/> the template is loaded and rendered. But in this case I run into another issues, because the component.js logic is missing: #689, #693

patrick-steele-idem commented 7 years ago

Sorry for the confusion @hedav85. The <include> tag is fairly flexible. You can pass it any of the following:

In your case, you need to provide the loaded template:

// Load the template:
$ let template = require(data.templatePath + data.templateName);
<include(template, {...})/>

The reason for this behavior is that only static string literals can be resolved to template paths at compile-time. At render-time we did not want marko to have to resolve and load templates from a path since that operation can be expensive (better to only load templates once at startup) and often times developers would pass relative paths that were not relative to the template where is <include> is used and this would lead to runtime exceptions.

After looking at the docs, I see that we absolutely need to do a better of updating the docs to match the <include> implementation.

@austinkelleher do you mind working on the docs for the <include> tag? (thanks in advance!)

patrick-steele-idem commented 7 years ago
patrick-steele-idem commented 7 years ago

@hedav85 Regarding:

If I do a var template = require('../path/to/my/template/' + 'index.marko') and then in the template <include(data.template, {...})/> the template is loaded and rendered. But in this case I run into another issues, because the component.js logic is missing: https://github.com/marko-js/marko/issues/689, https://github.com/marko-js/marko/issues/693

Can you provide some code samples for us to add a reproducible test case? It should not be the case that component.js logic is missing.

hedav85 commented 7 years ago

Hi, thanks for the feedback. I created a sample to reproduce my problem. https://github.com/hedav85/marko-test In this sample you have the following structure:

In the sample the module provides a button to generate a random number. If the container has a component.js, the button will not work. If you rename or remove the component.js from the container, then the button works fine. You also can check the console. The sample will log the onMount() events.

All components are included with the <include/>-tag.

I hope this sample helps you to reproduce the problem. Thank you!

hedav85 commented 7 years ago

I've tested a bit more around this issue. With your provided solution I get it working if the component is in the same node module. I can refer direct to the path.

// Load the template:
$ let template = require(data.templatePath + data.templateName);
<include(template, {...})/>

In the compiled template index.marko.js you will find

let template = require(data.templatePath + data.templateName);

My Problem now is that a component can be located in a another node module. The generated static code has the module version in the path. In my template I did not know anything about this version tag. This means my path becomes incorrect and require is not able to locate the template.

e.g.

If the path is correct the component is loaded and rendered correct and also the logic is there.

hedav85 commented 7 years ago

If you use a string in the template, e.g. (based on my sample)

// Load the template:
$ let template = require('../module');
<include(template, {...})/>

in the compiled template you get the following require statement:

var template = require('/marko-test$1.0.0/components/module/index.marko'/*'../module'*/);

It seems that a string literal is transformed into a another correct path.

Would it be possible to do this also for variables if they are strings?

$ let path = '../module';
$ let template = require($path);

should be handled in same way like

$ let template = require('../module');

to get

var template = require('/marko-test$1.0.0/components/module/index.marko'/*'../module'*/);
icrotz commented 6 years ago

Up i have the same issue :'(

andresilvasantos commented 6 years ago

Same issue here

DylanPiercey commented 4 years ago

In <include> tag is now deprecated and replace with the <${dynamic}> tag. The docs have also been improved https://markojs.com/docs/syntax/#dynamic-tagname and https://markojs.com/docs/syntax/#dynamic-components.