Comments (6)
hi :) sorry for the delay, I was in holidays. I'm gonna test your solutions, thanks for the pointers!
from hybrids.
maybe there is something to do with
Line 135 in 16dbbac
from hybrids.
Hi again! The <slot>
element is a placeholder for passing custom html inside of the custom element. You should define only one slot with a particular name - it should be unique. In your example code, you are creating a slot element for each result item. You are using it wrong, and it can't be used like this. However, I think I know what you are trying to achieve.
The template is compiled when html
function is called - it is when render
is called - it will not use what is inside of the slot
- slot element does not have children
like normal element, so template engine will not process slotted children.
Also, the template engine uses some quirks to make it work in older browsers, like IE11 and Edge, so passing ${}
in the <template>
may not work as you expect.
So you can't use <slot>
for that. And if you can't use it, the user DOM will be rendered inside of your component with all consequences of Shadow DOM CSS boundaries.
The only way to do so is to allow users to pass a function to property, which returns template for result item. Then you can use that function instead of the default. The downside of the solution is that your users will have to know how to use html
function - so you expose internals, and result DOM will be rendered inside of your internal DOM structure.
export default {
resultTemplate: undefined,
render: ({ query, results, resultTemplate }) => html`
<input placeholder="search" type="search" oninput="${(host, event) => host.query = event.target.value}" />
<ul>${html.resolve(
results.then(
r => html`${resultTemplate && r.map(resultTemplate)}`
).catch(e => html`${e}`),
html`…`
)}</ul>
`,
}
import { html } from 'hybrids';
const el = document.getElementById('..');
el.resultTemplate = (result) => html`
<h1>custom result item ${result.ref}</h1>
<a href="https://localhost/documentation/docs/${result.ref}">${result.ref}</a>
`;
You could possibly pass html
as an argument, so the user would not have to import it directly from the library. If a user would use hybrids in his element it is more straight forward:
const resultTemplate = (result) => html`
<h1>custom result item ${result.ref}</h1>
<a href="https://localhost/documentation/docs/${result.ref}">${result.ref}</a>
`;
const MyElement = {
render: () => html`
<full-search-element resultTemplate="${resultTemplate}"></full-search-element>
`,
};
Anyway, I am not a fan of that solution. I even tried to create an example with <template>
but slightly different than your example, but it would be even harder. Custom elements should provide simple and tool-agnostic public API. Your problem is general, and I think other web components libraries don't solve it better. The polymer had an idea of passing templates inside of the custom elements, but LitElement does not have it anymore. It uses lit-html
internally, and does not expose it outside of the element.
If you are trying to make an element for fetching data and give the user control over how to render results, it might be a better idea to allow defining a name of user custom element, which will be used. I've created a helper function for rendering custom element with html
:
import { html } from 'hybrids';
const RENDER_PLACEHOLDER = `{{prop-${Date.now()}}}`;
export function renderElement(tagName, props) {
const args = [];
let template = `<${tagName}`;
Object.keys(props).forEach(key => {
template += ` ${key}="${RENDER_PLACEHOLDER}"`;
args.push(props[key]);
});
template += `></${tagName}>`;
return html(template.split(RENDER_PLACEHOLDER), ...args);
}
userElement: 'my-default-result-item',
render: ({ query, results, userElement }) => html`
<input placeholder="search" type="search" oninput="${(host, event) => host.query = event.target.value}" />
<ul>${html.resolve(
results.then(
r => html`${r.map((result) => renderElement(userElement, { result })).catch(e => html`${e}`),
html`…`
)}</ul>
`,
Then your user may use your component like this:
<full-search-element user-element="my-result-element"></full-search-element>
This solution has significant advantages:
- the user has full control over his custom element - the only requirement is to consume
result
property - it can be defined in any web components library
- You don't expose hybrids internals
from hybrids.
@docteurklein Did you have time to test out my proposition? Can I help you with anything else according to your problem?
from hybrids.
I'm closing an issue now, but you are welcome to re-open if you will have any problems with the subject.
from hybrids.
hi :) just to give you some feedback, I tried by manipulating the template
attribute using js, eg:
<fulltext-search index-url="https://localhost/docs/search-index.json">
</fulltext-search>
<script type="module">
document.querySelector('fulltext-search').template = (html, result) => html`<h1>custom ${result.ref}</h1>`;
</script>
but got an error like:
TypeError: setting getter-only property "template"localhost:5000:7:14
<anonymous> https://localhost:5000/:7
InnerModuleEvaluation self-hosted:4147
evaluation self-hosted:4101
I ended up trying to dynamically build the template using an "eval" hack and it seems to be ok, but, eewww:
function arendermap(promise, callback, error) {
return html`${html.resolve(promise.then(results => html`${results.map(callback)}`).catch(error || console.error), html`…`)}`;
}
const FullTextSearch = {
indexUrl: "",
query: "",
results: async ({ query }) => Promise.resolve([{ref: 'test1'}, {ref: 'test2'}]),
template: (host) => (host.querySelector('template') ? (result) => html`<div innerHTML="${makeTemplate(host.querySelector('template').innerHTML)(result)}">e</div>` : (result) => html`<li>${result.ref}</li>`),
render: ({ query, results, template }) => html`
<input placeholder="search" type="search" oninput="${(host, event) => host.query = event.target.value}" />
<ul>${arendermap(results, template)}</ul>
`
};
const makeTemplate = (templateString) => {
return (templateData) => new Function(`{${Object.keys(templateData).join(',')}}`, 'return `' + templateString + '`')(templateData);
}
now I can use it like this. Hurrayyy! \o/:
<fulltext-search index-url="https://localhost/docs/search-index.json">
<template>
<h1>custom ${ref} !</h1>
</template>
</fulltext-search>
exactly what I wanted.
If you have any idea how I could do it in a better way, I'm all ears :)
from hybrids.
Related Issues (20)
- Storage-first/storage-only option for [store.connect] HOT 10
- React integration - Error while unmounting the component HOT 2
- Try render a table but encounter a problem HOT 3
- about destructured host parameter in render function HOT 2
- I'm trying to load a model on button click. Component does not update after loading model HOT 5
- A storage without an identifier loads data even before assigning an identifier HOT 2
- Starter / template repository ? HOT 2
- How do I tap into `connected` / `disconnected` lifecycle methods ? HOT 2
- Confusing documentation on "class" attribute HOT 3
- An error occurs when saving a Draft HOT 9
- store.error() Guard does not work with enumerable Models loaded from asynchronous storage HOT 2
- A draft model instance with an empty ID returns an error when editing HOT 6
- When saving a model, linked models cannot be updated HOT 2
- Suggestion: Develop a solution that allows you to use a single component to create, edit and present a model HOT 5
- Cannot define default nested model HOT 3
- how to create a component method HOT 4
- how to implement focus management? HOT 4
- Bug: Template is not updated when component properties change HOT 14
- How to add focus when opening a form? HOT 4
- I can't write a polymorphic model definition for GeoJson objects HOT 7
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from hybrids.