Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Documentation of ivi-html #7

Closed
brucou opened this issue Jan 31, 2019 · 2 comments
Closed

Documentation of ivi-html #7

brucou opened this issue Jan 31, 2019 · 2 comments

Comments

@brucou
Copy link

brucou commented Jan 31, 2019

Hello there again,

I am trying to move the following application to ivi. I have it already in inferno, nerv, and react. Trying to make it work with ivi but I am encountering some issues probably linked to undocumented behaviour of element factories. I already solved an issue related to the fact that the first argument of the element factory (say div) is a list of classes (or so it seems). So ".class" should become "class" without the dot. Attributes seem to be attributes, and hopefully there is no funky edge case. But I have this which I can't work around

reconciler.ts:1178 Uncaught TypeError: next.u is not a function
    at _updateAttr (reconciler.ts:1178)
    at _updateAttrs (reconciler.ts:1235)
    at _createElement (reconciler.ts:316)
    at _mountObject (reconciler.ts:365)
    at _mount (reconciler.ts:426)
    at _mountFragment (reconciler.ts:410)
    at _mount (reconciler.ts:424)
    at _mountObject (reconciler.ts:371)
    at _mount (reconciler.ts:426)
    at _update (reconciler.ts:476)
    at dirtyCheck (root.ts:47)
    at time (index.ts:177)
    at index.ts:107
    at error.ts:24

This is the next value, which indeed features no u properties :

{
  "t": {
    "f": 2,
    "d": "div"
  },
  "d": {
    "n": "App__view uk-margin-top-small uk-margin-left uk-margin-right",
    "a": {
      "data-page": "home"
    },
    "c": [{
      "t": { "f": 2, "d": "div" },
      "d": {
        "n": "HomePage",
        "a": [{ "t": { "f": 2, "d": "h1" }, "d": { "n": ["TMDb UI – Home"], "c": null } }, {
          "t": {
            "f": 2,
            "d": "legend"
          }, "d": { "n": "uk-legend", "a": { "data-testid": "PROMPT_TESTID" }, "c": ["Search for a Title:"] }
        }, {
          "t": { "f": 2, "d": "div" },
          "d": {
            "n": "SearchBar uk-inline uk-margin-bottom",
            "a": [{
              "t": { "f": 2, "d": "a" },
              "d": { "n": "uk-form-icon uk-form-icon-flip js-clear", "a": { "uk-icon": "icon:search" }, "c": null }
            }, {
              "t": { "f": 2, "d": "input" },
              "d": {
                "n": "SearchBar__input uk-input js-input",
                "a": { "type": "text", "value": "", "data-testid": "QUERY_FIELD_TESTID" },
                "c": null
              }
            }],
            "c": null
          }
        }, {
          "t": { "f": 2, "d": "h3" },
          "d": {
            "n": "uk-heading-bullet uk-margin-remove-top",
            "a": { "data-testid": "RESULTS_HEADER_TESTID" },
            "c": ["Popular Now"]
          }
        }, {
          "t": { "f": 2, "d": "div" },
          "d": {
            "n": "ResultsContainer",
            "a": { "data-testid": "RESULTS_CONTAINER_TESTID" },
            "c": [{ "t": { "f": 2, "d": "div" }, "d": { "n": ["Loading..."], "c": null } }]
          }
        }],
        "c": null
      }
    }]
  }
}

Any idea what I am doing wrong? I assume, that this may be because my first render is on an element (#app) while the subsequent renders are on the first child of that element. Do render in ivi have to be always on the same element? Could also be that the problem is that I am using webcomponent and custom tags : const movieSearch = htmlElementFactory("movie-search");?

I have a codesandbox but the error is swallowed there : https://codesandbox.io/s/0y56rx6zkl
Else, the repo corresponding to the sandbox is here : https://github.com/brucou/movie-search-app-ivi

@localvoid
Copy link
Owner

localvoid commented Feb 1, 2019

Here is a working solution: https://codesandbox.io/s/8ypkk1nqy9

Do render in ivi have to be always on the same element?

No

Could also be that the problem is that I am using webcomponent and custom tags : const movieSearch = htmlElementFactory("movie-search"); ?

It is possible to consume web components in ivi and there shouldn't be any problems, but there is no support for writing custom components. There are so many different edge cases for scheduling, events, handling errors and it would require to add a huge chunk of code to the library to deal with this edge cases.


Sorry for the inconvenience because of the bad documentation, I'll try to explain what I've changed to fix it:

fsm.js:L187, fsm.js:L193, ...: All element factories has interface type ElementFactory<T> = (className?: string, attrs?: T, children?: Op) => OpNode<ElementData<T>>;. ivi doesn't support short forms when className or attrs are omitted and children is a 1st or 2nd argument, because such APIs are usually lead to security issues. It supports this forms: div(), div("classnameA classNameB"), div("className", { attr: value }) or div("className", { attr: value }, children).

fsm.js:L209: This line is correct, but when there is a single child, it is unnecessary to wrap it into array.

fsm.js:L202: ivi is using slightly different way to attach events, there is a special node Events() that is used to handle events. Decoupling events from DOM elements significantly improves composition model.

fsm.js:L201: It is an edge case and usually this edge cases are handled automagically, ivi is using a way much simpler model, by default everything is a DOM attribute, and to handle edge cases there are AttributeDirectives.

fsm.js:L249: Children lists is an extremely complicated topic with so many different edge cases, pros and cons for different solutions. In ivi, dynamic lists should be wrapped in TrackByKey() node, and keys should be assigned by wrapping nodes into another node using key() function (also improves composition model).

index.js:L49: I've replaced it with simple createElement() and appendChild(). Didn't tried to dig deeper, but I think that there is an issue because of the ordering of lifecycle events in web components.

Also, I slightly refactored code to remove code duplication.

@brucou
Copy link
Author

brucou commented Feb 1, 2019

wow, that's great. I did not expect you to actually solve all the issues. I think this would be a great resource for other people coming to ivi for the first time, and maybe you could feature the example among others. I remember having seen this issue asking for examples : #4

Your component-like refactoring is great. That is the great advantage behind hyperscript/ivi-html, it is just javascript so refactoring is trivial.

I agree with you on Events and TrackByKey and key. It is pretty easy to use, and also with a standard functional interface. TrackByKey additionally is not mandatory, so for prototyping purpose it is always possible to ignore it, and put it back when optimizing for performance.

My workflow is to receive html, css from a web designer, to turn that into hyperscript (with the online converter http://html-to-hyperscript.paqmind.com/), and then write the app logic around that. So decorating with Events works well in that workflow.

Would be great though is if web components would work seamlessly. Unfortunately it seems there is still some way to go before that can be used uniformly across browsers, which is why I use only custom elements (no shadow dom, no DOM events emitted by the component but use of a trivial event emitter - 300 bytes min-gzipped).

In any case, thank you for the support. I now have a much clearer vision of how to use ivi and its performance optimizations.

Final codesandbox is here : https://codesandbox.io/s/3x9x5v4kq5

@brucou brucou closed this as completed Feb 1, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants