Code Monkey home page Code Monkey logo

Comments (6)

docteurklein avatar docteurklein commented on May 15, 2024 1

hi :) sorry for the delay, I was in holidays. I'm gonna test your solutions, thanks for the pointers!

from hybrids.

docteurklein avatar docteurklein commented on May 15, 2024

maybe there is something to do with

export function compileTemplate(rawParts, isSVG, styles) {
to make it populate from a predefined template?

from hybrids.

smalluban avatar smalluban commented on May 15, 2024

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.

smalluban avatar smalluban commented on May 15, 2024

@docteurklein Did you have time to test out my proposition? Can I help you with anything else according to your problem?

from hybrids.

smalluban avatar smalluban commented on May 15, 2024

I'm closing an issue now, but you are welcome to re-open if you will have any problems with the subject.

from hybrids.

docteurklein avatar docteurklein commented on May 15, 2024

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)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.