question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Manual Instantiation of Cells : Draft version 1.

See original GitHub issue

A lot of people have been asking for an explicit cell instantiation method.

Currently, Cell automatically figures out what elements to turn into Cells by searching from global variables with a $cell key. This works in most cases, but there are many other cases where it makes sense to manually create cells.

Some of the problems brought up were:

1. Namespace

From https://github.com/intercellular/cell/issues/12#issuecomment-308876874

“The one thing I would still like to find a solution for is the fact that declarative cells must not be lexically scoped – only var or a global declaration will do, and neither const nor let will work.”

Like @Caffeinix pointed out in above comment, Cell won’t be able to automatically detect const and let variables since they are not global variables.

2. Integration into legacy frameworks

The power of Cell comes from its pluggable nature. You can use it with any existing framework and any web library with zero hassle because it creates an actual, completely encapsulated HTML node instead of a virtual DOM.

@kingoftheknoll mentioned integrating with angular https://github.com/intercellular/cell/issues/12#issuecomment-310935220 and I think this argument generally applies to all other frameworks/libraries out there.

It would be great to be able to create cells that can be injected into other frameworks. Cell is well positioned for that since it doesn’t create any extra data structure like virtual DOM but instead creates native HTML elements that contain everything they will ever need to work when plugged into any context.

3. Doesn’t work in dynamically loaded contexts

This is related to #2 above, but nowadays many web apps use dynamic module loading, and web browsers themselves will natively support them in the future via ES6.

To be clear, I think most of these modular approaches on the frontend web context are convoluted and mostly unnecessary (which was the main reason I wrote Cell), but I also see no reason not to support these approaches since allowing manual instantiation has very clear and powerful benefits.

Currently it’s not easy to integrate Cell into existing web apps that dynamically load modules. Maybe you’re using angular, maybe you’re using Backbone.js, or anything else really. This is because the main Cell creation logic creates cells this way:

  1. Wait for window.onload event
  2. Find all global variables that have a $cell attribute
  3. Automatically build cells from these variables

This means, by default there’s only one opportunity to create cells–at the very beginning.

Liberating Cell from this specific lifecycle will allow dynamic creation of cells, making Cell truly injectable into all kinds of context.

One possible approach (and why it’s not optimal)

A lot of people suggested an option to add something like this:

const $node = Cell({
  $type: "p",
  $text: "I will be created by a global Cell variable"
})

Well the thing is, it would be best if we could avoid the global variable Cell altogether. To understand the rationale behind this, please read https://github.com/intercellular/cell/issues/111

Basically, we already have a decentralized DOM, why not utilize it instead of creating a centralized constructor? This would be in line with the philosophy of the library after all.

Goals

The main goals:

  1. The Creator function does not pollute the global namespace but belongs to each HTML element, including the body.
  2. Any regular HTML node should have the ability to: Evolve into a cell.
  3. Any regular HTML node should have the ability to: Create a new cell without directly adding it to the DOM.

Solution

Here’s the solution:

  1. Add a $cell() function to window.Element.prototype.
  2. The $cell() function can be used in two different ways, depending on the existence of the $cell key on the JSON argument.

With this addition, we should be able to achieve the 3 goals mentioned above.

1. Evolving into a cell

Let’s say we already have an HTML page, and we want to morph parts of it into Cells.

This is already somehow possible in a 100% declarative manner by using the “id” attribute But that approach assumes you’re using Cell as standalone, because it depends on the window.onload event.

Anyway, sometimes you may want to use Cell along inside an existing web app that dynamically loads modules, which means using window.onload event won’t work. We want to be able to instantiate these Cells inside each module after they’re loaded.

The new Element.prototype.$cell() function allows you to take ANY regular HTML element, pass the blueprint object (gene) as a parameter, and morph into a cell. Here’s an example: https://jsfiddle.net/euzrzk8n/1/

<html>
<script src="https://rawgit.com/intercellular/cell/plan/cell.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.9.1/d3.min.js"></script>

<div id="viz"></div>
<script>
document.querySelector("#viz").$cell({
  _sampleSVG: null,
  $cell: true,
  _r: 40,
  _cx: 50,
  _cy: 50,
  _width: 100,
  _height: 100,
  _color: 'red',
	$init: function(){
    var self = this;
    this._sampleSVG = d3.select(this)
      .append("svg")
      .attr("width", this._width)
      .attr("height", this._height);  
    this._sampleSVG.append("circle")
      .style("stroke", "gray")
      .style("fill", "white")
      .attr("r", this._r)
      .attr("cx", this._cx)
      .attr("cy", this._cy)
      .on("mouseover", function(){d3.select(this).style("fill", self._color);})
      .on("mouseout", function(){d3.select(this).style("fill", "white");});  
  }
})
</script>

</html>

Here’s another example: https://jsfiddle.net/3v1ttcga/2/

I took an example template from bootstrap website and just converted static components into Cells.

2. Create a new cell without directly adding it to the DOM.

Above method should cover a lot of the use cases where you want to manually create cells.

But since we’re at this, let’s go deeper.

The previous approach assumes that we already have an HTML node we want to transform. What if we just want to create a cell from scratch, without adding it to the DOM directly?

In this case, you can just get rid of the $cell attribute from the argument when calling the $cell() function, and it will only create the cells in memory.

Here’s an example of creating a <p> element from <body> without automatically appending : https://jsfiddle.net/vj5bwjxb/

let gene = { 
  $type: "p", 
  $text: "I will be created by 'body' but you'll have to manually add me to the DOM"
};
for(let index=0; index<10; index++){
  let cell = document.body.$cell(gene);
  document.body.appendChild(cell);
}

Notice:

  1. There is no $cell attribute on the gene object.
  2. You manually add them to the DOM with document.body.appendChild(cell)

Conclusion

By adding the additional $cell() method on window.Element.prototype we were able to avoid creating a global variable that creates cells.

This is also in line with the core philosophy since it is not some centralized entity that creates cells, but any node can create a cell, which means no compromise was made.

Let me know what you think

Here’s the branch for this feature: https://github.com/intercellular/cell/tree/plan

And you can try this version in action by including:

<script src="https://rawgit.com/intercellular/cell/plan/cell.js"></script>

I think these two features powered by a single addition of the $cell() function should address most of the problems brought up so far, while preserving the decentralized nature of Cell.

But I want to make sure I’m not missing anything, and also would love to hear your thoughts. Let me know. Thanks!

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Comments:16 (9 by maintainers)

github_iconTop GitHub Comments

2reactions
devsnekcommented, Jun 27, 2017

lgtm

1reaction
gliechtensteincommented, Jun 29, 2017

Re: adding the “window.Cell” global variable on the browser context,

@piranna I’m having hard time understanding what you are suggesting. Especially the following paragraph leaves me confused because I’m not sure how a global Cell variable can get rid of the God object.

What I ask for, is that these scripts don’t modify the global context and instead enable the gene directly instead of wating for window.onload. In fact, doing so would allow to remove it and almost fully remove God

I’m also confused because you mention polluting the global scope as a problem of the current approach, but the document.body.$cell() approach doesn’t involve any global variables. Rather, adding the Cell global variable is obviously polluting the global scope.

What would really help me understand your argument is if you could share an actual example you are envisioning, in code. I would appreciate it if you could provide an example of

  1. what your version of syntax will look like
  2. and how that approach can get rid of God.
  3. what that can achieve what document.body.$cell() or the original declarative approach cannot achieve

I am all for getting rid of God if it’s ever possible, I’m all listening. Thank you!

p.s.

BTW, just to be clear, I do understand the overall spirit of your suggestion, but I keep asking for an example because I think the document.body.$cell() and the {$cell: true} declarative approaches pretty much cover all the cases when combined, that even if we introduce a Cell variable I don’t know where that will be useful other than the fact that it’s shorter than document.body.$cell(). Which is why it would be better to have a discussion based on a tangible example.

Read more comments on GitHub >

github_iconTop Results From Across the Web

XSL Transformations (XSLT) Version 1.1 - W3C
When a template is instantiated, each instruction is executed and replaced by the result tree fragment that it creates. Instructions can select ...
Read more >
draft-ietf-atommib-atm2-01 - IETF Datatracker
1 ) defined in the SMI. In particular, each object type is named by an OBJECT IDENTIFIER, an administratively assigned name. · 2)...
Read more >
Cell.js Long Term Plan and Design Decisions · Issue #111 ... - GitHub
Here's what Cell is, in one sentence: Cell is a function that constructs a decentralized structure of ... Manual Instantiation of Cells :...
Read more >
User manual - IBM
This edition applies to VERSION 6.1, IBM Rational DOORS Requirements ... 3.2.1 WHAT IS A MODULE TYPE AND A TEMPLATE ? ... 3.1...
Read more >
MIMOSA user's manual (Draft version 1.4.0) - UR Green | Manualzz
1. most importantly, the initialization process has been factored out from the dynamics. It is now possible to specify how to initialize the...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found