turbine icon indicating copy to clipboard operation
turbine copied to clipboard

Manipulating DOM imperatively

Open janat08 opened this issue 4 years ago • 11 comments

How do you attach jquery or imperative widgets?

janat08 avatar Jun 15 '20 17:06 janat08

I'm asking for onrendered hook.

janat08 avatar Jun 16 '20 06:06 janat08

There is no official api for this yet, but it can be done by declaring a component using a class extending Component. You can use imperative code in the run method, which is given a very limited api for the parent and a destroyed future meant for cleaning up.

A small example of this is the TextComponent

class TextComponent extends Component<{}, {}> {
  constructor(private t: Showable) {
    super();
  }
  run(parent: DomApi, destroyed: Future<boolean>): Out<{}, {}> {
    const node = document.createTextNode(this.t.toString());
    parent.appendChild(node);
    destroyed.subscribe((toplevel) => {
      if (toplevel) {
        parent.removeChild(node);
      }
    });
    return { available: {}, output: {} };
  }
}

export function text(showable: Showable): Component<{}, {}> {
  return new TextComponent(showable);
}

limemloh avatar Jun 16 '20 15:06 limemloh

Judging by the domapi interface there's no way of passing info up to the parent, like inserted widgets interface?

janat08 avatar Jun 17 '20 13:06 janat08

I'm not entirely sure what you want to archive, but the api is for manipulations. One way to pass information to a parent is as output, but that might not be what you want. Another approach is to make a parent, which supplies it children with something else than the DOMapi, which is bit more work.

Could you be a bit more specific of what you would like to do?

limemloh avatar Jun 17 '20 14:06 limemloh

Use this widget https://webtorrent.io/docs

janat08 avatar Jun 17 '20 19:06 janat08

It creates a client that Id like to use.

janat08 avatar Jun 17 '20 19:06 janat08

I'm assuming you also want to use file.appendTo(elm), The quick solution is to wrap the client code inside run. Here is an example using one of the examples from WebTorrent:

class WebTorrent extends Component<{}, {}> {
  constructor() {
    super();
  }
  run(parent: DomApi, destroyed: Future<boolean>): Out<{}, {}> {
    // Container element for files
    const elm = document.createElement("div");
    parent.appendChild(elm);
    var client = new WebTorrent()
    // torrentId could be given as argument
    client.add(torrentId, function (torrent) {
      var file = torrent.files.find(function (file) {
        return file.name.endsWith('.mp4')
      })
      // Use the container element for the files
      file.appendTo(elm)
    })
    destroyed.subscribe((toplevel) => {
      if (toplevel) {
        parent.removeChild(elm);
      }
      // removes the client when the component is remove
      client.destroy()
    });
    return { available: {}, output: {} };
  }
}

limemloh avatar Jun 17 '20 19:06 limemloh

I forgot to mention that I want to change the file being used in the element after intialization too.

On Thu, Jun 18, 2020, 2:33 AM Emil Gjørup [email protected] wrote:

I'm assuming you also want to use file.appendTo(elm), The quick solution is to wrap the client code inside run. Here is an example using one of the example from WebTorrent:

class WebTorrent extends Component<{}, {}> { constructor() { super(); } run(parent: DomApi, destroyed: Future): Out<{}, {}> { // Container element for files const elm = document.createElement("div"); parent.appendChild(elm); var client = new WebTorrent() // torrentId could be given as argument client.add(torrentId, function (torrent) { var file = torrent.files.find(function (file) { return file.name.endsWith('.mp4') }) // Use the container element for the files file.appendTo(elm) }) destroyed.subscribe((toplevel) => { if (toplevel) { parent.removeChild(elm); } // removes the client when the component is remove client.destroy() }); return { available: {}, output: {} }; }}

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/funkia/turbine/issues/120#issuecomment-645578456, or unsubscribe https://github.com/notifications/unsubscribe-auth/ACWP5AMXNZR73M67SNJ3TE3RXEK7JANCNFSM4N6MPBEA .

janat08 avatar Jun 18 '20 12:06 janat08

If it is changed based on some logic outside the component then you can provide the information to the constructor, otherwise you can just do the change inside run by manipulating elm

limemloh avatar Jun 18 '20 12:06 limemloh

Do you mean passing down a stream as a prop? How does that look like, I'm not familiar with using classes.

janat08 avatar Jun 19 '20 09:06 janat08

We hope to have a cleaner api for wrapping imperative code in the future without using classes, but until then:

class FileContainer extends Component<{}, {}> {
  constructor(private stream: Stream<File>) {
    super();
  }
  run(parent: DomApi, destroyed: Future<boolean>): Out<{}, {}> {
    ...
    // access the stream through `this`
    this.stream.subscribe((file)=> ...)
    ...
  }
}

function fileContainer(files: Stream<File>) {
  return new FileContainer(files);
}

limemloh avatar Jun 19 '20 10:06 limemloh