Discussions in Hackernews about small virtual-dom frameworks

901 views
Skip to first unread message

oren

unread,
Jan 23, 2015, 4:04:34 AM1/23/15
to mith...@googlegroups.com
Riot – A React-like, 2.5K user interface library  - https://news.ycombinator.com/item?id=8928433
Tuxedo.js – A Framework Built on React and Flux- https://news.ycombinator.com/item?id=8930752

Good discussions and insightful comments by Leo.

Leo Horie

unread,
Jan 23, 2015, 1:54:56 PM1/23/15
to mith...@googlegroups.com
I was poking around a little bit w/ Riot 2.0 last night. It has a very similar philosophy to Mithril.

In general, I like the concept, but I think some of the design choices are a bit unfortunate. Specifically:

- template scope system: it uses a mustache-like scope drill-down mechanism (i.e. given `things = [{foo: "bar"}]`, you drill down using `<div each="{things}">{foo}</div>` (notice that the `thing.foo` reference omits the handle to `thing`). I've toyed around w/ this kind of scoping mechanism for an old experimental project of mine ( https://github.com/lhorie/wu-template ). Wu handles scope resolution it via dynamic lookups, but in Riot, it looks like this chore is offloaded to the developer (which adds some brittleness)
- control flow: `if` and `each` are element attributes. These are basically ng-repeat and ng-show from Angular. There's no ng-if equivalent (and by extension, no `else`), and as far as I can tell, there's no equivalent to ng-repeat-start/end.

What this boils down to is that you end up with a bunch of unsupported/clunky edge cases because HTML attributes are a poor candidate for mapping scope semantics.

Also, as was pointed out in the HN thread, two somewhat big problems w/ its current diff engine are that it lacks a key algorithm and mechanisms for diff optimization, which make its speed claims somewhat suspicious.

I wasn't particularly excited about Tuxedo though. It's basically React with a bunch of things added on top, so it's not exactly a small framework.

Pat Cavit

unread,
Jan 23, 2015, 2:01:46 PM1/23/15
to mith...@googlegroups.com
Looking at the Riot issues is pretty amusing. They uses regexes to parse their HTML templates, and it causes all the sorts of issues you'd expect when using a regex to try and parse HTML.

I was a bit thrown off by mithril-style templates in the beginning, but now I'm firmly in the "they're awesome" camp.

Lawrence Dol

unread,
Jan 23, 2015, 2:25:42 PM1/23/15
to mith...@googlegroups.com
What immediately jumps out to me is that Mithril has no good (or at least obvious) approach to building components (Mithril.elements notwithstanding). It nags me as something significant that is missing.

Leo Horie

unread,
Jan 23, 2015, 2:40:49 PM1/23/15
to mith...@googlegroups.com
@Lawrence yeah, I've been thinking a lot about how best to approach this. Mithril elements is great, but I agree that there needs to be something in core to address the issues around positional identity of components

pyke...@gmail.com

unread,
Jan 23, 2015, 3:29:29 PM1/23/15
to mith...@googlegroups.com
I've been using this pattern to create reusable components with Mithril.

https://gist.github.com/JoshGalvin/3f952cd67fdd497d2107

This pattern allows you to ship components as Common/AMD(.js) modules.

Lawrence Dol

unread,
Jan 23, 2015, 4:00:22 PM1/23/15
to mith...@googlegroups.com
@Leo,

When you're ready to propose a solution or some ideas, I'd be more than happy to hash out the details.

If I come up with anything concrete in the meantime, I'll be sure to bring it up -- I am actively working on componentization in my application. My sense so far is that the controller is the major roadblock; where it's singular there's too tight a coupling between the pieces, but where it's per-component there it results in clumsy and verbose interactions. Maybe Mithril needs an message bus of some kind.

ste...@activitystream.com

unread,
Jan 23, 2015, 4:35:58 PM1/23/15
to mith...@googlegroups.com
As a "casual" member of this group, a user that has not contributed much (if anything), I would like to add that seeing this discussion, on reusable components, is almost a relief.

Mithril elements seems quite interesting but such a core component does, in my opinion, belongs to the core project and seeing the architectural premise on which Mithril is built and the decisions made here before on "what belongs/is correct" I trust that the outcome will be top notch.

Thank you once more for this nifty framework.

Regards,
-Stefan

Andreas Söderlund

unread,
Jan 24, 2015, 9:51:39 AM1/24/15
to mith...@googlegroups.com
This is my ordinary comments that you probably know by now, but in case there are some newcomers to the group... :)

See how object semantics is visible in the Riot components? Maybe we're getting closer to what I hope will be a completion of the MVC circle - a view is an object and an object can be one or more of M, V and C.

Lawrence, how is the controller a roadblock? If you're separating it to its own object/namespace, what does it do except handle commands (requests, messages to the model, etc) and coordinate views?

I'm also curious how a message bus would solve the problem with componentization and clumsy interactions? (what is an interaction here?)

Interested to hear your thoughts, I'm eager to find a good solution in this area as well.

/Andreas

Kelvin Ishigo

unread,
Jan 24, 2015, 11:54:57 AM1/24/15
to Andreas Söderlund, mith...@googlegroups.com
Upfront: One thing I always worry about is when the "right thing" becomes dogmatic.

What I observe in software architectures is that there is tension between performance and coupling.
Strong type, direct function/method call => tight coupling => difficult componentization, but typically higher performance
Loose type, callback approaches => mid level coupling, typically mid level performance but can approach tight coupling
Message bus approaches => loose coupling => easier componentization since you typically only interface with data => lowest performance

I know mithril abhors the listener observer event model (message bus) but my one cent thought here is that you should apply appropriate paradigms to a particular problem set.  For certain classes of components, message buses might be appropriate.

This is just my view from having lived/programmed long enough to see the functional, then O-O, now functional again, waves of dogmatism sweep the world of programming.

We had a particular use case where we have a render engine that we want to drive from several input sources, browser, mobile, chromecast sender app.  In this case we decided to use a message bus protocol to abstract the engine (mithril) from the input sources via an adaptor layer.  The adaptor layer to engine interface was a message bus.

--
You received this message because you are subscribed to the Google Groups "mithril" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mithriljs+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Philo Kramer

unread,
Jan 24, 2015, 12:16:37 PM1/24/15
to mith...@googlegroups.com
Mithril stands out because it's minimalistic (and fast). If component communication is (hopefully) incorporated into core, I hope it is just as minimal. Something like routing -- basic but functional. For those who need more functionality there's moria -- and hopefully more options in the future. 

Andreas Söderlund

unread,
Jan 24, 2015, 2:43:42 PM1/24/15
to mith...@googlegroups.com, gaz...@gmail.com
Then we should be fair and admit that loose coupling can be a quite dogmatic design perspective too, right? :) I'm not sure that another layer of indirection can solve this problem, because we're actually going in a more concrete direction here, when expressing components and their relationships as suggested by Elements and Riot. It is useful information that shouldn't be abstracted away, either behind a design pattern that will hide system behavior, or in an extreme case, a single <application/> element.

So a general solution for this may be difficult, since it seems like we're delving into architecture here. But Mithril has done everything right so far, maybe something great will come up soon!

Barney Carroll

unread,
Jan 24, 2015, 2:54:33 PM1/24/15
to Philo Kramer, mith...@googlegroups.com
@Lawrence I think the problem with controllers is that everybody has different interpretations as to what they should do. Leo advocates thin controllers as a general principle but this leaves a gap in the author's mental model as to where state should be managed. If you're instantiating Mithril with m.route, then controllers reset on route change. All state and logic that sits outside of the route change loop needs external management mechanisms. The big problem is that whereas Mithril's core mechanisms take care of connecting a controller's lifecycle with its counterpart view automatically, all lower-order modules need to have their instances bound up manually with a lot of arduous boilerplate. The community (as far as this list indicates) is hard at work on this problem.

@Andreas again, you're seeing things from a level beyond developers' API preference which is hard to make sense of from anything but an abstract perspective. Riot looks a lot like Angular to me — you can write a lot of strawmen scenarios with very little code that doesn't introduce any cumbersome concepts you don't immediately care about. I think this would be a good bet if I had a large team of programmers all doing relatively simple things, but it doesn't address the kind of rapid redevelopment and scalability I want as a one-man-team on an application with rapidly evolving expectations. Your comment stands out from the rest as having a beatific insight beyond our smaller concerns, but it's really difficult to imagine what you might be seeing from up there!

@Kelvin the path of least resistance AFAICT is to use URIs as a message bus and allow controllers to interpret those changes and pass appropriate structures downstream. Of course, the URI is ultimately a very limited string and it won't scale for granular and/or esoteric requirements. But when messaging isn't truly global and can't pass to an available reference, I perceive a code smell and deficient architecture. I think it's another one of those things where a large team may be better able to read from the same hymn sheet, but I tend to want concrete references or explicit fallbacks at all times. What I really hate is the failure case of a component behaving perfectly within its own testable use cases only because the meaningful interactions aren't verifiable — ie deferred responsibility that ultimately no single component can account for.

@Philo Moria is taking a break at the moment, but will be back with a major version change in a month or so with functionality for optional multi-tenancy (ie multiple route-dependent nodes with independent route maps), concise route modifiers (ie change these route parameters, regardless of the rest), dynamic routing (modify router logic on the fly [and revert] based on temporary conditions) and a guide for conveniently separating route logic functions from view-specific controllers. Bait your breath (but also don't hesitate to email if you have specific concerns)!


On Saturday, 24 January 2015, Philo Kramer <pelon...@gmail.com> wrote:
Mithril stands out because it's minimalistic (and fast). If component communication is (hopefully) incorporated into core, I hope it is just as minimal. Something like routing -- basic but functional. For those who need more functionality there's moria -- and hopefully more options in the future. 

--
You received this message because you are subscribed to the Google Groups "mithril" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mithriljs+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


--
Regards,
Barney Carroll

barney....@gmail.com
+44 7429 177278

barneycarroll.com

Lawrence Dol

unread,
Jan 24, 2015, 3:49:25 PM1/24/15
to mith...@googlegroups.com
I must confess, I design in components -- for all types of systems. It's one of the reasons I like OO programming. I conceptualize any system of any complexity in terms of the major parts I can hold in my head at one time. Then I conceptualize any given part in terms of it's parts, and so on down until I have parts small enough and well-defined enough to actually code in terms of very a specific function -- this is a component (a programmatic atom, if you will). Then as each part is completed, the parts are assembled into larger parts, and so on to finally make the whole.

If rightly designed and implemented the components are reusable, or can be refactored into reusable components while preserving their public API (or morphing the actual uses if those are tightly contained).

When I look at Riot, I immediately begin to see simple, well-defined components making up a more complex system. I desire that in Mithril.

In theory, Mithril can do this much better since everything is JS, so a component, whatever it looks like should be a JS module/object. CSS should be purely decorative and HTML (as in HTML source) is nothing more than a page template to hold the application. In it's simplest, the entire HTML is:

<html>
   
<head>
        ...
    </head>
    <body>

       
<script id="page-script">MyComponent.create(document.getElementById('page-script').parentNode,MyView);</script>
    </body>
</html>

While Mithril speaks of a "module" which would be analogous to what I call a component, it's amorphous and ill-defined.

My sense, and at this point it's an intuition (inexpert in terms of JavaScript, expert in terms of overall programming experience), is that MVC tends towards lots of disparate pieces where no one of those pieces actually delivers any specific function.

In terms of UI the evolution of Java Swing from MVC to M-UI may be of interest. In particular, their comment that "We quickly discovered that this split [MVC] didn't work well in practical terms because the view and controller parts of a component required a tight coupling (for example, it was very difficult to write a generic controller that didn't know specifics about the view). So we collapsed these two entities into a single UI (user-interface) object". This might very possibly explicate my problems in creating components with Mithril -- especially given my background in Java GUI apps and with writing a GUI toolkit in Java.

What do others think about this idea of M-UI instead of MVC? Especially given the ease with which variant views can be plugged into a UI component (I've actually done this for one of my applications and it worked quite well to get three variations on the same essential data).

Andreas Söderlund

unread,
Jan 24, 2015, 6:54:43 PM1/24/15
to mith...@googlegroups.com
Barney and Lawrence, thank you for your answers! After some additional thinking, I've concluded my point: What we call components here may be the "VC", "VM" or "MVC" objects I'm talking about, and by ignoring the object semantics that shows up, we run into problems like this.

It's similar to how the focus on classes (not objects) prevents real OO, and forces structurally awkward solutions like the myriad of design patterns.

So the M-UI objects sounds very much like View-Controllers, which is allowed in MVC (I noted this in an older thread, "Back to MVC"). I don't think replacing MVC is a good idea, it's a thoroughly researched pattern that we should embrace instead of thinking that it has limitations.

Ok, compressing my point: We should let components be what they seem to be - objects. :)

Leo Horie

unread,
Jan 24, 2015, 10:22:30 PM1/24/15
to mith...@googlegroups.com
Ok, I'll bite. I'm working on a proof of concept in this branch: https://github.com/lhorie/mithril.js/blob/components/mithril.js

This branch basically implements a version of Mithril.Elements in core, so you can do this:

```
m.tags["MyComponent"] = {
controller: function(node) {
this.test = "test"
},
view: function(ctrl) {
return m(".component", ctrl.attrs, [ ctrl.children, ctrl.test ])
}
}

var App = {
controller: function() {},
view: function() {
return m("MyComponent", {class: "hello"}, "Hello world ")
}
}

m.module(document.body, App) // spits out <div class="component hello">Hello world test</div>
```

The main differences between this proof of concept and Mithril.Elements are:

- instead of registering via Mithril.Elements' `m.element` function, you register a component by adding it to the `m.tags` map
- the controller extends the virtual dom element, and no extra parameters are passed to the view

The main things it brings to the table are:

- it leverages the virtual dom cache tree, so it has the same positional identity semantics as everything else in Mithril (i.e. no need for hard coding component ids, ever)
- it supports controller.onunload

There are a few loose ends that may or may not be issues:

1) Mithril.Elements allow returning arrays from component templates. This version doesn't. i.e. `m("foo")` can expand to `[m("a"), m("b")]` in Mithril.Elements. In my version, it can only expand to something with a single root element. The primary reason is that it's really hard to support controller.onunload. The only argument I can come up with for allowing arrays as return values is consistency. But neither Angular or React support replacing the component's tag with multiple elements, and you can't really support onunload properly in that case given that DOM elements can be moved and removed, disintegrating the "component". So maybe not allowing arrays as template return values is a reasonable limitation.

2) Mithril.Elements supports escaped versions of custom tags (i.e. you can make table do magic, and $table be the fallback regular table tag. Personally, I don't like the idea of overriding regular tags because it can lead to WTF moments for no good reason. Instead, it's trivial to register a `$table` element that does the magic and leave `table` alone. And I think it's a lot more obvious where the magic comes from, when reading the code.

3) This proof of concept always uses "replace" semantics (i.e. `m("MyComponent")` never spits out a <MyComponent> tag to the DOM, instead it replaces it with the component's markup). Riot has non-replace semantics all the time. Angular has a `replace` option in its directive API, Having always-replace semantics means you can't use the component's tag name to namespace it for CSS styling. This is trivial to get around (just add a class to the root element of the component), but technically it's still a weakness. I don't like Riot's approach because then you can't add attributes to the component tag itself, and the tag always pollutes the DOM tree. Angular's feature is, imho, the kind of thing that seems good on paper, but contribute to its death-by-a-thousand-paper-cuts complexity issue in reality. So I think always-replace semantics is the way to go.

So, yeah. Thoughts?

Martin Pengelly-Phillips

unread,
Jan 25, 2015, 5:39:13 AM1/25/15
to mith...@googlegroups.com
Looks interesting Leo.

Personally I think your reasoning for 1, 2 and 3 is sound and they are reasonable limitations, though (1) and (3) seem to counter each other slightly (you end up with a developer specified polluting tag in certain situations).

Could you expand a bit more on:

* The "node" argument to the controller and how that could be used?
* How you see view models fitting into this pattern?


Martin

Clemens Capitain

unread,
Jan 25, 2015, 6:22:25 AM1/25/15
to mith...@googlegroups.com
Leo, this looks very promising, as it streamlines the way we define and consume recuring structures aka custom elements and keeps our code DRY, adhearing to mithrils principles. I think it could be compared to angulars transclusion directives. 
Why don't you make the m.tag a private registry, and register elements with a function like m.defineElement(elementName, module)? This would avoid people messing arround with the tags-container and offer a single entrypoint. I think this is what people concerns the most, how to defne/write a reusable component....


The main differences between this proof of concept and Mithril.Elements are:

- instead of registering via Mithril.Elements' `m.element` function, you register a component by adding it to the `m.tags` map
- the controller extends the virtual dom element, and no extra parameters are passed to the view

This is a could one, no more inner state like with mithril.elements m('hello', { state:{ myInnerThingy: true, ... } }, children)


The main things it brings to the table are:

- it leverages the virtual dom cache tree, so it has the same positional identity semantics as everything else in Mithril (i.e. no need for hard coding component ids, ever)
- it supports controller.onunload

The strength of mithrils simplicity. 

There are a few loose ends that may or may not be issues:

1) Mithril.Elements allow returning arrays from component templates. This version doesn't. i.e. `m("foo")` can expand to `[m("a"), m("b")]` in Mithril.Elements. In my version, it can only expand to something with a single root element. The primary reason is that it's really hard to support controller.onunload. The only argument I can come up with for allowing arrays as return values is consistency. But neither Angular or React support replacing the component's tag with multiple elements, and you can't really support onunload properly in that case given that DOM elements can be moved and removed, disintegrating the "component". So maybe not allowing arrays as template return values is a reasonable limitation.

I can't think of a use case either. 

2) Mithril.Elements supports escaped versions of custom tags (i.e. you can make table do magic, and $table be the fallback regular table tag. Personally, I don't like the idea of overriding regular tags because it can lead to WTF moments for no good reason. Instead, it's trivial to register a `$table` element that does the magic and leave `table` alone. And I think it's a lot more obvious where the magic comes from, when reading the code.

This was confusing from the beginning, I'm with you here.
 
3) This proof of concept always uses "replace" semantics (i.e. `m("MyComponent")` never spits out a <MyComponent> tag to the DOM, instead it replaces it with the component's markup). Riot has non-replace semantics all the time. Angular has a `replace` option in its directive API, Having always-replace semantics means you can't use the component's tag name to namespace it for CSS styling. This is trivial to get around (just add a class to the root element of the component), but technically it's still a weakness. I don't like Riot's approach because then you can't add attributes to the component tag itself, and the tag always pollutes the DOM tree. Angular's feature is, imho, the kind of thing that seems good on paper, but contribute to its death-by-a-thousand-paper-cuts complexity issue in reality. So I think always-replace semantics is the way to go.

As I said before, alot like angular's tranclusion. In order to offer fully encapsulated, self contained components wrapped in their own shiny tag, mithril would need to support CustomElements via document.registerElement API, and shadowDOM. 

The document.registerElement API is well supported. Here is a link to a small implementation https://github.com/WebReflection/document-register-element#document-register-element. The author has written another lib called restyle http://webreflection.blogspot.co.uk/2014/08/self-contained-custom-elements-via.html, which allows for encapsulation of css style definition together with a customElement. 

That way we could have almost complete customElements with transclusion and styles, minus encapsulting shadowDOM, which is still a spec proposal and not widely supported supported (FF only with webcomponents.enabled, Safari no support, IE under consideration). 

Have a look at the source to this demo https://webreflection.github.io/custom-element/

Greets,
Clemens

Phil Toms

unread,
Jan 25, 2015, 10:22:34 AM1/25/15
to mith...@googlegroups.com
@Leo this is great news, I'm so glad you've finally taken that bite, and very excited about the outcome. 

I think the key thing to take from Mithril.Elements is the distinction between element type components and model or app type components (who owns the element - app or DOM?). Your solution has access to the cache that makes this distinction work without arbitrary identity. This absolutely has to be the way forwards. 

Before settling on my own design I tried many view patterns - and included several in the starter kit by way of demonstration. I was influenced by my experiences with Angular directives - both positives and negatives. One of the positive takeaways was composability - really the essential point of custom elements if you think about it. I think the way you handle composability through the children property is very neat and clever. I haven't grok'd the full implications yet but it feels right. Compositional order might be an issue though. A tag designer might need to establish a parent context before building the child view. And this leads to the idea of tightly coupled tag assemblies (tabset / tab etc) which might be harder to achieve without control over order.

Another directive positive (for me) is the ability to process templates. Different from inline composition, a template might be a custom tag or any inline element group, uncompiled and passed into another custom tag, to be mapped over an array of data for example (this is the other use for Mithril.Elements escape mechanism).

With regards to those loose ends:

I can see why point 1 has to be the way it is. I think its very elegant. Allowing for arrays means 1 less element level in the markup. As you say Angular and React have the same issue. Maybe for another discussion - abstract elements?

By my own fault, everybody seems to have the escape mechanism the wrong way round. Its not for creating specialised tables - 'myTable' will do that. Its for augmenting (or even replacing) built in element types. Angular allows directives to be applied to existing element and attribute types for the same reason. Without arguing the pros and cons of such functionality, the escape mechanism is a cheap way to support this, i.e., you escape from within the component if you need to augment the original tag. Given this is already provided idiomatically, it is difficult to come up with a trivial but useful example, but consider that the pattern can be used to escape other custom (possibly 3rd party) tags as well. To me it seems like a door worth being kept open.

Replace semantics I think could also be discussed on another thread at another time. I'm all for Mithril keeping its options open with regards to the future of the web but I don't think it would be a breaking change to support custom DOM tags in the future.

So yes, I am very excited. I hope that Mithril.Elements has been useful to you in the design of this concept.

Phil

Leo Horie

unread,
Jan 25, 2015, 12:50:20 PM1/25/15
to mith...@googlegroups.com
@Martin the node argument is the virtual dom element (i.e. `{tag: "MyComponent", attrs: {class: "hello"}, children: "Hello world "}` in the example. `this` points to the same thing. For the purposes of igniting some discussion I had both in place so people could chime in on the different mechanisms.

View models, I think, can work the same way as they did before (global object for global concerns, factory pattern for per-instance mapping)

@Clemens, there was someone using that w/ Mithril. I recently added support for the `is` attribute to help support that use case. Custom elements and this system overlap quite a bit in functionality, to be sure, though.

@Phil first of all, thank you for releasing Mithril.Elements. It brought to light a lot of cases that I was overlooking in my ruminations.

re: table vs $table, I think the augmenting pattern in Angular is mostly a way of supporting an unobtrusive js style of markup. In my experience, I'd typically use that w/ the restrict:"A" option (and I'd imagine "C" exists for this use case as well). Angular does use it w/ restrict "E" in some directives within framework space (e.g. in A tags to disable empty href attributes, and form tags to hook up the validation system), but I'm not sure I'd ever want to have elements do custom things based on their tag name in app space (again because of the surprise factor). For unobtrusive augmentation, maybe something like the mechanism described in this article ( http://lhorie.github.io/mithril-blog/extending-the-view-language.html ) might be more appropriate? One problem of shoehorning into component is that you can't emulate restrict: "A".

While fiddling, I realized that we have a pretty powerful version of Angular's compile/transclude thing. Here's an example:

```
m.tags["List"] = {
controller: function() {},
view: function(ctrl) {
return m("ul", [
ctrl.attrs.items.length ? ctrl.attrs.items.map(ctrl.children[0]) : ctrl.children[1]()
])
}
}

//somewhere else
m("List", {items: [1, 2, 3]}, [
function(item) {
return m("li", item)
}, function() {
return m("li", "nothing here")
}
])

```

List is basically a for/else monad. The first child is repeated if there are items, otherwise the second item shows up.

The problem, as you can see, is that the monadic application (the for/else semantics) is tied to the tag name "List". Ideally, it should be tied to "items" (which, in turn, might need to have a less generic name), so that you could bind to a UL that is declared outside of the component (rather than hard coding m("ul) in it).

I suppose one could argue that a component that ONLY implements an abstract control flow monad shouldn't even be a component to begin with and that people should stick w/ plain js control flow for that and they should only componentize things with tangible UI value.

Philo Kramer

unread,
Jan 25, 2015, 1:18:00 PM1/25/15
to mith...@googlegroups.com
To allow the instance to choose the type of list, why not:

```
m.tags["List"] = {
controller: function() {},
view: function(ctrl) {
return m(ctrl.attrs.type, [
ctrl.attrs.items.length ? ctrl.attrs.items.map(ctrl.children[0]) : ctrl.children[1]()
])
}
}

//somewhere else
m("List", {type: "ul", items: [1, 2, 3]}, [

Philo Kramer

unread,
Jan 25, 2015, 1:21:18 PM1/25/15
to mith...@googlegroups.com
@Phil

Compositional order might be an issue though. A tag designer might need to establish a parent context before building the child view. And this leads to the idea of tightly coupled tag assemblies (tabset / tab etc) which might be harder to achieve without control over order.

If an instance of tab included attrs: {key: 'tab0'} couldn't tabset maintain coupling without the need to control order?

@Leo
Would it be possible for a component to be given a globally unique ID upon request?

m("MyComponent", {class: "hello", key: "random"}, "Hello world ")

becomes 

{tag: 'MyComponent', attrs: {className: 'hello', key: '4gyof834fhoedh'}, children: ['Hello world']).

Not only could a child be targeted specifically by it's parent, if you decide to incorporate namespaces on a universal level, a component would be addressable from another disparate component, maybe from a 3rd party.


Phil Toms

unread,
Jan 25, 2015, 2:50:43 PM1/25/15
to mith...@googlegroups.com
@philo

To allow the instance to choose the type of list, why not:

```
m.tags["List"] = {
controller: function() {},
view: function(ctrl) {
return m(ctrl.attrs.type, [
ctrl.attrs.items.length ? ctrl.attrs.items.map(ctrl.children[0]) : ctrl.children[1]()
])
}
}

seems like a simple solution to that problem. And passing extra information through attrs is the same as the keyed state property in Elements except this approach doesn't support passed through DOM attributes. For example:

```
//somewhere else
m("List", {style:{color:"blue"}, type: "ul", items: [1, 2, 3]}, [])
```
The style attribute would be lost.

@Leo,  yes, I see that compositional order handling is the same as Elements!  There are some view patterns that are not obviously supported, but as @philo has so easily demonstrated, there are always other ways to solve a problem. I'll go away and try to re-implement the sample code in Mithril-starter-kit using your POC and let you know how I get on.

Jonathan Buchanan

unread,
Jan 25, 2015, 2:58:13 PM1/25/15
to mith...@googlegroups.com
Would it be possible to support directly passing an object defining the custom component, rather than passing a string and having a central registry of names?

That avoids potential last-to-load-wins clashes where multiple components want use the same name, and missing a require() or <script> becomes a ReferenceError in the template rather than an error from within Mithril itself.

I'm glad to see you're considering a TitleCase convention for custom component names - this works well in React, particularly with JSX. MSX will be updated if necessary to make sure it supports whatever component approach you go with, regardless.

Jonny.

Clemens Capitain

unread,
Jan 25, 2015, 3:52:48 PM1/25/15
to mith...@googlegroups.com
I've created a little wrapper around m.tags to register tags in multiple ways:
m.customTag = function customTag(elementName, definition) {
    var module = {};
    if (definition.toString() === '[object Object]') {
        module.controller = definition.controller || function(node) {};
        module.view = definition.view;

    } else if (typeof definition === 'function') {
        module.controller = function(node) {};
        module.view = definition;

    } else if (typeof definition === 'string') {
        module.controller = function(node) {};
        module.view = function(ctrl) {
            return m(definition, ctrl.attrs, ctrl.children);
        };

    } else {
        throw new Error('invalid tag definition given');

    }
    m.tags[elementName] = module;
};

// calls go like this, empty controllers will be added
// 1. simple tag
m.customTag('appbar', 'header.appbar'); //=> m('appbar', attrs, children) => <header class="appbar"></header>

//2. nested view
m.customTag('labeledCheckbox', function(node) {
    return m('label', [
        node.attrs.label,
        m('input[type=checkbox])
    ]);
});

//=> m('labeledCheckbox', { label: 'check me' }) => <label>check me<input type="checkbox" /></label>

// 3. fullblown module
m.customTag('myFullblownTag', {
    controller: function(){ ... },
    view: function(ctrl) { ... }
});


One thing that comes to my mind is, since the inner controller/node passed to the inner view is a virtualDOM node which is extendend by passed in attributes at hand, one has to be careful not to use attribute names consumed by html, e.g. id, title etc.

Now I percept that Mithril.elements might be right passing an extra state object to setup the inner view. What do you guys think?

regards,
Clemens

Leo Horie

unread,
Jan 25, 2015, 9:56:14 PM1/25/15
to mith...@googlegroups.com
> this approach doesn't support passed through DOM attributes

@Phil it's doable, but it's a task left to the component developer. I think both approaches (i.e. separate state obj vs everything as attributes) are valid, but they just look at components from different angles: the everything-as-attributes approach considers components to be analogous to function calls first, and DOM elements second. The separate-state-object approach considers components to be DOM elements first, and argument passing as a special case attribute. The trade-off for each approach is that the secondary concern will take more code to handle (i.e. state incurs the penalty of needing that extra property in all components, everything-as-attributes incurs the penalty of having to separate HTML attributes from data arguments.

I like that the everything-as-attributes makes component consumption look virtually identical to regular tags, and I like that the separate-state-object yields less component boilerplate. With the everything-as-attributes approach, you could create a `state` argument to implement the separate-state-object pattern yourself, but it would be nice if we could somehow have the shorter consumption interface, while keeping attributes and data separated.

@Clemens sorry, I forgot to address your question about the defineElement idea. `m.tags` being just a public js map has a couple of advantages: 

- it supports all map operations out of the box(e.g. `delete m.tags["Foo"]` is useful in unit tests' teardowns)
- it's introspectable (e.g. `console.log(m.tags)` will tell you what components exist)
- it's less code

I'm not sure if there's a huge benefit in preventing access to that map. I'd trust that people messing w/ m.tags know what they're doing (or at least that they would notice their apps breaking if they did something dumb) :)

@Johnathan yeah I think that's pretty trivial to add. Would MSX be able to tell whether a <Foo> means m("Foo") or m(Foo) or would we need to choose between the two?



Jonny Buchanan

unread,
Jan 26, 2015, 5:39:27 AM1/26/15
to mith...@googlegroups.com
@Leo It wouldn't be able to tell.

If custom components are supported by passing either a name or an object, then MSX will have a flag added to control its output and the choice will be which option to make the default.

Phil Toms

unread,
Jan 26, 2015, 2:04:40 PM1/26/15
to mith...@googlegroups.com

On Monday, 26 January 2015 02:56:14 UTC, Leo Horie wrote:
@Phil it's doable, but it's a task left to the component developer. 

No worries,  It can be defined in and published as the component signature.

Update on the MSK conversion: MSK.Bootstrap is now running happily with the new core. I needed to rethink the tabset / tab relationships due to a different compilation sequence but otherwise there were no surprises. I will post a full report when the project conversion is completed. First impressions are good though!

Phil

Lawrence Dol

unread,
Jan 26, 2015, 7:41:33 PM1/26/15
to mith...@googlegroups.com
I think this makes some sense, provided components are instantiated and expose a "mithril module", and the user of the component assigns the tag (as opposed to components self-registering). I mean like this:

StandaloneComponent: {
    controller
: function() {
       
// ...
   
},
    view
: function(ctl) {
       
//...
   
},
}

And then the use of the component is something like:

App: {
    controller
: function() {
        m
.tags["widget"]=StandaloneComponent;
   
},

    view
: function(ctl) {
       
return m("div.container", [
           
...
            m
("widget", ...),
           
...
       
]);
   
}


The assumption being that the component's controller is instantiated when added to the parent's view and its `onunload` called when it is removed.

But as it now, there is a namespace collision inherent in the tag list. As a component developer, how do I name my component's sub-components without colliding with anyone using my component? I think the custom tags list should be a property of the controller using them, not a Mithril global.

That said, I am not at all convinced that this brings a whole lot to the table over instantiating a subcomponent in my controller and using its view function in my view like this:

App: {
    controller
: function() {
       
this.widget=new StandaloneComponent.controller;
   
},

    view
: function(ctl) {
       
return m("div.container", [
           
...
           
return StandaloneComponent.view(ctl.widget),
           
...
       
]);
   
}

My sense is that Mithril can do something better.

Leo Horie

unread,
Jan 26, 2015, 9:55:19 PM1/26/15
to mith...@googlegroups.com
@Lawrence Johnathan's suggestion would address the collision issue. I think the component scoping is better handled by your favorite dependency resolution system (i.e. RequireJS, Browserify+CommonJS, whatever), since that gives you a testability ecosystem for free. 

I actually agree that, technically speaking, Mithril.Elements-like components aren't much more than sugar, but I'd argue that it's a lot easier to consume them than the current componentization pattern in the docs (the one in your last snippet) - with Mithril.Elements-like components, as soon as you learn how to write a m("div") tag, you already learned how to use 3rd party black-box components.

I think what you're suggesting in terms of a bus is something like the observer pattern? At a high level, I'd imagine this would work like so:

1 - in the model, subscribe to an event (or intent or controller action or whatever you want to call it) and add a handler to mutate the model data
2 - in an event handler in the view, broadcast the respective event
3 - the handler gets called, the model data changes, Mithril redraws and puts everything back in sync

I say "at a high level", because there's a bunch of different variations of the concept (Flux, MVI, Meteor collections)

Lawrence Dol

unread,
Jan 26, 2015, 10:39:12 PM1/26/15
to mith...@googlegroups.com
@Leo,

My suggestion on Git-Hub was ill-conceived in large part because I hadn't yet though really hard about why Mithril as it is currently conceived seems to be difficult to componentize -- ignore it.

I have just finished a detailed suggestion for how I think Mithril should do components on this list, which I think is far better and to which I'd very much appreciate you giving some serious consideration. I think it conceptually far better than our community efforts so far. (But I am fairly severely sleep deprived, so hopefully I haven't missed anything glaringly obvious.)

Andreas Söderlund

unread,
Jan 26, 2015, 10:42:39 PM1/26/15
to mith...@googlegroups.com
@Leo thanks for your great work and analysis to find simple, useful solutions. I agree that we shouldn't make a big thing about name collisions, it's something that the programmer should handle explicitly anyway. Namespaces aren't very useful today, mostly a remnant from the days when there were multiple String implementations in C++. If someone makes a generic, useful component (accordion?) and a programmer decides to include it in his/her project, naturally it should be the only component with that name. 

This could actually be an argument to use a function instead of a map to assign components, to disallow name collisions and avoid runtime surprises. What do you think?

About the bus/observer, anyone else than me seeing such a pattern as a 21:th century GOTO? :) It's extremely hard to reason about runtime behavior when the execution can jump as in a global event handler. I'm not sure in this case (devil in the details), but it could be another case of being overly reliant on loose coupling. It is clear what problem are we really trying to solve here? It's a bit fuzzy for me at the moment!

/Andreas

Leo Horie

unread,
Jan 27, 2015, 9:45:17 AM1/27/15
to mith...@googlegroups.com
@Andreas re: observers - funny thing, I had said the same thing about observers way back in one of the threads on the github issue tracker (and that's one of the reasons I had not put an implementation of it in Mithril in the first place despite being a relatively easy to implement pattern).

Regarding collisions w/ string-based tags, the poor man's solution is to prefix tag names. Angular does this with directives (e.g. core directives have the `ng-` prefix), and even the upcoming HTML5 custom elements standard requires following the prefix-dash convention for the same purpose. As I mentioned, allowing `m(Foo)` is pretty trivial, and if it's the only componentization syntax available, it would not even require the m.tags map. The only downside is that you lose the quotes around the "tag name", so the syntax or components ends up being slightly different than normal tags.

Philo Kramer

unread,
Jan 27, 2015, 3:15:59 PM1/27/15
to mith...@googlegroups.com
@Phil
@Leo

Jumping back a bit.

Regardless of the future of mithril components It dawns on me that the style:{} attribute should be able to carry the full CSS of its component.
A 'List' component should be able to establish style for both its 'ul' and 'li' constituents.

Thus, wouldn't this be more useful:

m('List', { items: [ 1, 2, 3 ],
             type: 'ul',
             style: {
                        ul: { padding: '0', margin: '0' },
                        li: { text-decoration: 'none' },
                        'li a:hover': { color: 'red' }
                      }
   }, [ ] )

I'm not sure how this would be implemented internally,
that third line of CSS might be a nightmare and best left to normal CSS,
but the first two seem logical and doable.  

This would solve the "losing" style attributes problem from below:


m.tags["List"] = {
controller: function() {},
view: function(ctrl) {
return m(ctrl.attrs.type, [
ctrl.attrs.items.length ? ctrl.attrs.items.map(ctrl.children[0]) : ctrl.children[1]()
])
}
}

eddyystop

unread,
Jan 27, 2015, 3:44:01 PM1/27/15
to mith...@googlegroups.com
I suggest you give the parent a component specific class name, like .List, and style the sub-components in the CSS.

mithril-components was obsessed with passing configurable CSS class names into components and their sub-components. The component documentation became [voluminous](https://github.com/eddyystop/mithril-components/tree/master/components/tabs#view).

An additional point is that components can render different HTML e.g. a dropdown can be a button dropdown, or a nav bar dropdown, etc.




Philo Kramer

unread,
Jan 27, 2015, 4:05:14 PM1/27/15
to mith...@googlegroups.com
@Eddyystop

Components should work out of the box, so it seems to me that some styling might be necessary at instantiation.
In the case of  .List for example, list-style-type:none can make or break the layout.
 
Re your additional point:
We are working inside the view, so it seems appropriate that button dropdown vs navbar dropdown should be dealt with on a case by case basis.

Lawrence Dol

unread,
Jan 27, 2015, 4:13:09 PM1/27/15
to mith...@googlegroups.com
> It dawns on me that the [component] should be able to [specify it's] full CSS.

If the component truly should specify the styles, it can do so in it's view in the normal way. But if the style should be customizable by the the user of the component it must be external and the component should specify appropriate classes. No way, I think should the user of the component be specifying styles into the component's parts, which would be fragile and break encapsulation. This perceived need is, I think, a design-smell which is contra-indicative for this direction for building components.


Lawrence Dol

unread,
Jan 27, 2015, 4:15:50 PM1/27/15
to mith...@googlegroups.com
> I had said the same thing about observers way back ...

I wish I'd not raised the idea of an observer service in Mithril; it's entirely orthogonal and should be entirely the responsibility of the application as one means of wiring together the separate MVx parts should the developer choose to use an MVx flavor.

Lawrence Dol

unread,
Jan 27, 2015, 4:26:58 PM1/27/15
to mith...@googlegroups.com
The entire concern is orthogonal to the structure of a component, I think.

Visual styling is, of course, a perennial point of tension in any UI system, and in this case what is possible and feasible is dictated by the technologies providing the UI, namely CSS and HTML. However it is done, having a component lock in a visual style is sufficiently limiting so as to practically preclude it's reuse outside of the application for which it was built. The HTML/CSS solution to this is to provide the styles in a separate file, clumsy and sub-optimal though that typically is in practice it's usually workable. This would be my recommendation in this world.

One alternate possibility that a component writer could use is to let styles be set from properties, and then have a reasonable set of built-in defaults. Not saying this is a great idea, but just floating it as a suggestion.

A more Mithral alternative might be for the component to specify styles in an abstract form and run the view through a decorator to cement them, allowing the component user to provide it's own cementer. Again, just an off-the-cuff suggestion.

Phil Toms

unread,
Jan 27, 2015, 4:31:43 PM1/27/15
to mith...@googlegroups.com
On Tuesday, 27 January 2015 20:15:59 UTC, Philo Kramer wrote:
A 'List' component should be able to establish style for both its 'ul' and 'li' constituents.


Leo suggested an opt in strategy, it could be HTML biased, it could be model biased, either way the component gets to call the shots so it knows how to prune the attrs property down. Mithril.Elements takes the more practical view that the majority of view / component relationships were going to require both state initialisation and style guidance, and so provides a 'hard' convention that allowed the framework to do the heavy separation thus reducing the component boilerplate. Its a difficult call. Maybe Mithril tags should provide a helper function to split attrs on an arbitrary key, maybe we should write that function ourselves. I would approach the problem posed by @Philo through composition.

Composition is a powerful and flexible pattern and whether it was discussed in the Mithril group before I chanced upon this framework, or whether it was just left unsaid, I immediately recognised Mithril's functional leanings because that's the direction to which I had been looking (actually it was Elm >> virtual-dom >> Mercury >> Mithril). Sometimes when I mention composition I'm thinking about declarative page composition, and sometimes its lower level at the function pipeline. But really its the same thing. I would prefer to use composition to 'pipe' a list through a function that changes the observable characteristics of that list without changing its essential listiness - so that I could take the output and pipe it through another function, and so on and so on..

I published a list component in MSK based on previous Mithril efforts, the occlusionScroller as a demonstration of functional and elemental composition. The idea was simple but powerful: build your huge list into this component and let it handle render performance. The component doesn't know or care about the style of your list. It doesn't much care about content either. Mithril (not especially Elements) allows you to build these kind of patterns to the point where you're thinking "I've got a really useful, reusable component here". 

Philo Kramer

unread,
Jan 27, 2015, 4:34:59 PM1/27/15
to mith...@googlegroups.com
@Lawrence

I didn't intend that a component reach into other child components. Each component should only be responsible for it's own styling, if required. But in the definition of a component when the children are basic HTML tags that cannot be styled without external CSS, and their styling affects the workings of the component, it seems that some CSS should be internalized.

Later use of the component should not allow access to it's inner styling except through external CSS.

But I take your point, it does smell a little.


Philo Kramer

unread,
Jan 27, 2015, 5:06:13 PM1/27/15
to mith...@googlegroups.com
@Lawrence

> The HTML/CSS solution to this is to provide the styles in a separate file, clumsy and sub-optimal though that typically is in practice it's usually workable.

After a quick glance over the bootstrap code, it's clear that no direct styling is contained in the components. Classes are used everywhere for everything. Since bootstrap is stable, well tested, and has been refactored into all sorts of frameworks, I acquiesce. I was wrong.

I have always been adverse to the extra baggage of bootstrap.css, but I now agree with you that external CSS is a necessary evil. I am, however, still concerned about how a small component built by Bob in Bangkok will be packaged (js+css) for use by me in Mexico. Bootstrap is a library of components and can afford the one-file CSS requirement to handle everything on the page. Of course, Browserify makes such things trivial, but it still seems clumsy to require 10 tiny CSS files for my 10 drop-in 3rd party components. 

Lawrence Dol

unread,
Jan 27, 2015, 6:18:40 PM1/27/15
to mith...@googlegroups.com
@Philo,

I agree with you entirely.

However, the example you gave, "m('List', { items: ..." is an example of component use not component definition. The styling in the example is reaching into the component with assumed knowledge of the components concrete constituent elements, which knowledge the user of a component should not need or have (ideally; again CSS+HTML makes this black-boxing hard to do, and harder to do right).

The design of CSS & HTML actually makes it hard to specify default styling in the view (which would naturally be `style` attributes) without precluding external styling via a CSS file, unless the CSS file specifies "!important" all over the place. This is a deficiency in CSS not Mithril, but it's a deficiency we're stuck with.


On Tuesday, January 27, 2015 at 1:34:59 PM UTC-8, Philo Kramer wrote:
@Lawrence

I didn't intend that a component reach into other child components. [...]

Philo Kramer

unread,
Jan 27, 2015, 6:49:27 PM1/27/15
to mith...@googlegroups.com
@Lawrence

Sorry. I meant for my example of component use to imply the structure of the component definition.
Message has been deleted

eddyystop

unread,
Jan 28, 2015, 9:18:50 AM1/28/15
to mith...@googlegroups.com
@Philo
Re your additional point:
We are working inside the view, so it seems appropriate that button dropdown vs navbar dropdown should be dealt with on a case by case basis.

The number of cases can expand as seemingly straightforward components can be complicated. For example, the Bootstrap dropdown component can contain item, header and separator entries which all need different HTML.


@all
I suggest that a library component should either be styled for one particular CSS framework, or it should use generic class names in association with sample CSS.

You cannot, for example, expect to write many components that work with both Zerb Foundation (Bootstrap's inspiration) and Bootstrap. They expect different HTML for some common components. I would expect more differences comparing to other CSS frameworks.

I liked @Leo's CSS decorator blog posting. Its works for the generic CSS case. However it would run into the same problem with cross-CSS framework components.


Lawrence Dol

unread,
Jan 28, 2015, 2:44:24 PM1/28/15
to mith...@googlegroups.com
> I suggest that a library component should either be styled for one particular CSS framework, or it should use generic class names in association with sample CSS.

I completely concur.

Jussi Arpalahti

unread,
Jan 28, 2015, 3:21:49 PM1/28/15
to mith...@googlegroups.com


keskiviikko 28. tammikuuta 2015 1.18.40 UTC+2 Lawrence Dol kirjoitti:
@Philo,

<snip>
 

The design of CSS & HTML actually makes it hard to specify default styling in the view (which would naturally be `style` attributes) without precluding external styling via a CSS file, unless the CSS file specifies "!important" all over the place. This is a deficiency in CSS not Mithril, but it's a deficiency we're stuck with.


There might be a way around this problem, since Mithril templates generate pure Javascript data structures.

For fun I did some testing and created this fiddle:
http://jsfiddle.net/1wk898j3/3/

Basically this removes all style attributes from the template a view has build, and introduces them back as CSS declarations in a STYLE tag, dynamically before every render.

I see few use cases for this approach:

 * With components written without regard to CSS of the surrounding app, remove its offending styles
 * Write components that use @id and @class for apropriate CSS hooks, but uses @style for default styling, thus allowing you to use and extend its styles
 * Combine styles from whole app's views to one minifiable CSS and compile templates without any internal styling for production
 * Support LESS style variables and other constructs

I have not tested this beyond my simple example, but Leo talks about template transformation for CSS here:
http://lhorie.github.io/mithril-blog/when-css-lets-you-down.html

Leo mentions, that one could do transformation cheaply using Sweet.js, but I can't fathom how that could work. My version just recurses through entire template.

I envision that I could write my components with necessary styling easily with regular @style, and then re-use these changing styles with transformation where necessary. Transformation could also allow the component to support different CSS frameworks, provided that the logic and overall structure stays relatively same. If I get an use case to test this concept, I'll probably try to conjure something. In addition, if your view is building hundreds of elements each with different styling according to some complex logic, I'd much rather change the view code than try to make sense of its resulting template.

Hope this interest some of you! I believe Mithril is unique among JS frameworks in allowing this kind of template control.

Paul Tiseo

unread,
Jan 28, 2015, 3:25:59 PM1/28/15
to mith...@googlegroups.com
That is my current big adoption roadblock. I would like to build a complex app with something like Mithril, but this thing has a couple of master pages, a variety of "pages" to put into the one viewport in each master page, then the various components in each viewport (dashboard, work order management, etc...). I'm in analysis paralysis on how to tackle this via Mithril.

What would be good to have is a starter that actually looks like a real app would, heavily nested, with a) some API help from Mithril, and b) useable as guidance. Then, if others have a different approach, it could be a second or third "starter".

PS: The starter should contain a clean Grunt or Gulp workflow that is not hardcoded. This and the above would probably grease the rails on adoption for hopefully more than just me. :)

On Friday, January 23, 2015 at 2:40:49 PM UTC-5, Leo Horie wrote:
@Lawrence yeah, I've been thinking a lot about how best to approach this. Mithril elements is great, but I agree that there needs to be something in core to address the issues around positional identity of components

Lawrence Dol

unread,
Jan 29, 2015, 4:32:28 PM1/29/15
to mith...@googlegroups.com
Andreas,


> we shouldn't make a big thing about name collisions, it's something that the programmer should handle explicitly anyway

If a component, X itself makes use of subcomponents, then it must register the tag names; the programmer using X cannot control the tags registered by X, nor can they disambiguate collisions between component X and component Y. However, if tags used by X are contained within X and tags used by Y are contained within Y there is no problem.

In short, a global name registry is naive and doomed to fail in the community marketplace.

Barney Carroll

unread,
Jan 29, 2015, 7:22:40 PM1/29/15
to Jussi Arpalahti, mith...@googlegroups.com
Jussi, your idea might be more viable than you think — have you considered using the scope attribute in conjunction with the style element? https://developer.mozilla.org/en-US/docs/Web/HTML/Element/style#attr-scoped
--
You received this message because you are subscribed to the Google Groups "mithril" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mithriljs+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


--
Regards,
Barney Carroll

barney....@gmail.com
+44 7429 177278

barneycarroll.com

Lawrence Dol

unread,
Jan 29, 2015, 8:19:01 PM1/29/15
to mith...@googlegroups.com, jussi.a...@gmail.com
Barney,

> have you considered using the scope[sic] attribute in conjunction with the style element?

The last I checked the `scoped` attribute is not supported by any browser other than Firefox and has been dropped from the standards track. Google Chrome support for it was dropped in version 34 "due to code complexity".

Andreas Söderlund

unread,
Jan 30, 2015, 4:43:18 AM1/30/15
to mith...@googlegroups.com
Do you have something to back that claim with? I don't see many apparent failures in similar situations. As Leo said Angular uses prefixed directives if needed, Objective-C seems to hold up quite well (interesting reading here especially at the end). Node packages, which are much more susceptible as actual libraries, makes away with namespacing through require() and handles eventual package issues with a dispute policy, and I haven't seen many of those disputes, but correct me if I'm wrong.

For components, I think the issue is simpler. The marketplace won't have these problems if collisions are disallowed when registering the component. Then it will be obvious there are naming issues, that shouldn't be tucked away into namespaces imho. If you have time, I'd be interested in an example that shows a component naming collision without any semantic or hierarchical relationship (which highlights the underlying problem).

(Side note: I'd love to see some analysis on how often collisions really occurs if flattening the namespaces in different projects, and what those collisions are.)

/Andreas

Leo Horie

unread,
Jan 30, 2015, 10:38:00 AM1/30/15
to mith...@googlegroups.com
Re: collisions, I just wanted to point out that jQuery, Underscore, etc have had a working collision resolution pattern for years (`.noConflict()`).

Technically speaking, dealing w/ conflicts is as easy as:

//in the component code
var old = m.tags.TheComponent
m.tags.TheComponent = newComponent
newComponent.old = old

//application code
m.tags.TheComponentNoConflict = m.tags.TheComponent.old

With that being said, having worked on a large codebase with a legacy of using Prototype alongside jQuery, I've run into other issues (e.g. juniors getting stuck on `$(foo).addClass is not a function` errors).

And again, implementing Johnathan's suggestion solves this problem properly.

Lawrence Dol

unread,
Jan 30, 2015, 3:13:40 PM1/30/15
to mith...@googlegroups.com
Leo,

No it's not that easy, because code which may be inside the component is referring to components by that name.

Using a slightly exaggerated example, let's say I make a TodoList component with uses a Filter component and a List component. In the context of a "TodoList" the names "Filter" and "List" seem natural and perfectly fine. Now someone wants to use my TodoList component, but they already have a "Filter" component (or they are using some other component that has a "Filter" component).

This kind of collision is outside of the control of the one using the various components.

Philo Kramer

unread,
Jan 30, 2015, 7:25:31 PM1/30/15
to mith...@googlegroups.com
Several days ago I was playing with mithril.elements, worrying about this same problem. It was clear that the elements Array, here the m.tags Object, should hold Pointers to modules and not just carry Strings. I was experimenting with directly addressing components and unique namespacing was the solution for everything but identical siblings. It's true that siblings could be addressed sequentially, but I ran into situations where order mattered. So I came up with a couple of easily implemented solutions:  

m.tags[ myCoolComponent: {module: 'pointerToMyCoolComponent.controller', id: '8437502347' } ]

and

m.tags[ 'myAwesomeComponent' ] with internal representation as m.tags[ 'myAwesomeComponent_id750918345' ]

Something I didn't try: An MD5 hash could essentially make every component unique, which might be advantageous for the ecosystem down the road. Since registration through m.tags happens only once in the life of an app, the overhead shouldn't be too costly.

And dare I say, a sequential number could be added to those IDs to make each component instance unique. But that's a different discussion.

Lawrence Dol

unread,
Jan 30, 2015, 8:25:10 PM1/30/15
to mith...@googlegroups.com
Arguably, since the tag map is simply a means of resolving an object with a controller constructor, it could be dispensed with by providing that object instead of a name; that would at least push the problem back to that of defining objects in JavaScript, which is an extant problem with numerous solutions already there. So, instead of the tags map, simply:

   view=function() {
        return m("div", [
            ...
            m(MyComponent, ...),
            ]);

rather than `m("MyComponent", ...)`.

Thought that would preclude dynamic substitution of some other "compatible" component.

Leo Horie

unread,
Jan 30, 2015, 11:55:10 PM1/30/15
to mith...@googlegroups.com
FYI, I'm working on exposing a different API that takes into account some of the discussions that have been happening.

You can see the code for that on the [latest commit in the components branch](https://github.com/lhorie/mithril.js/blob/components/mithril.js)

In this version, there are a few additions to the existing API and a rework on the mounting API:

1 - m.module now accepts splat arguments, which get passed as parameters to the constructor

```javascript
var Foo = {
  controller: function(arg1, arg2) {
    console.log(arg1) // logs {a: 1}
    console.log(arg2) // logs "hello world"
  },
  view: function() {}
}
m.module(document.body, Foo, {a: 1}, "hello world")
```

2 - There's a function called `m.module.create`, which is a sugar helper to instantiate a module

```javascript
var Foo = {
  controller: function(arg) {
    this.test = arg.a
  },
  view: function(ctrl) {
    return m("h1", ctrl.test)
  }
}
var controller = m.module.create(document.body, Foo, {a: 1})
console.log(controller.test) // logs 123
console.log(controller.view()) // logs {tag: "h1", attrs: {}, children: [123]}
```

3 - m.module has a new overload. Omitting the DOM element argument is to m.module.create what Function.prototype.bind is to Function.prototype.call

```javascript
var Foo = {
  controller: function(arg) {
    this.test = arg.a
  },
  view: function(ctrl) {
    return m("h1", ctrl.test)
  }
}
var FooWithArgs = m.module(Foo, {a: 123})
var controller = new FooWithArgs()
console.log(controller.test) // logs 123
```

4 - The `m.tags` registry no longer exists. Instead the diff engine can detect modules, so components are now mounted like this:

```javascript
function myView() {
  return m("div", [
    Foo, // no arguments, therefore it's possible to use it directly
    m.module(Foo, {a: 1}) //Foo, with a partial application on the controller
  ])
}
```

5 - Controllers for modules that go through the m.module call (via either of the two overloads) get augmented with an `args` property. This can be used to pass arguments through to the view without a controller

```javascript
var Foo = {
  view: function(ctrl) {
    return m("h1", ctrl.args[0], ctrl.args[1])
  }
}

function myView() {
  return m("div", [
    m.module(Foo, {class: "test"}, "hello world") //yields <h1 class="test">hello world</h1>
  ])
}
```

6 - Those controllers can also define a onupdate method (if you need to do something to arguments on every redraw before they reach the view)

The bad parts:

1 - this proposal gets a lot things magically attached to controllers (`.args`, `.onupdate` and `.view`)
2 - more magical things attached to controllers (`module.controller.$$args`)
3 - `.args` overlaps in scope with `.onupdate`
4 - `onupdate` differs from config API (i.e. `isInitialized`)

The good parts:

1 - no tag registry, so if you use CommonJS/AMD/ES6 modules/whatever then you'll never run into tag name conflicts
2 - no m("Foo") / m(Foo) duality, so it's MSX friendly
3 - modules look more homogeneous regardless of how you use them
4 - parameterizable module controllers
5 - splat arguments means you can separate data arguments from HTML attributes easily

Leo Horie

unread,
Jan 31, 2015, 12:04:16 AM1/31/15
to mith...@googlegroups.com
small correction: in the example for item 3, it should be `new FooWithArgs.controller()`, not `new FooWithArgs()`. I've pasted the corrected snippet in the Github discussion thread ( https://github.com/lhorie/mithril.js/issues/413#issuecomment-72304725 )

Barney Carroll

unread,
Jan 31, 2015, 4:46:18 AM1/31/15
to Leo Horie, mith...@googlegroups.com
Leo, about the 'bad parts', these have been off interest to me since Mithril.Elements. Functors could totally solve this.

My idea worked on the premise that function 1 serves to identify the component instance, returning function 2 which invokes that component with whatever args are passed.

Sig:
mod( component [, contextById [, keyWithinContext ] ] )( ...args );

Ignore the syntax for instance retrieval. This doesn't care whether the component contains a controller, a view, or both. Invocation, internally, works like this:

var ctrl = retrievedInstance || new ( noop.bind.apply( undefined, [ component.controller || noop ].concat( args ) )();

return component.view ? component.view.apply( undefined, [ ctrl ].concat( args ) ) : ctrl;

This means component signature becomes :

{ controller(...args), view(ctrl,...args) }

On Saturday, 31 January 2015, Leo Horie <leoh...@gmail.com> wrote:
small correction: in the example for item 3, it should be `new FooWithArgs.controller()`, not `new FooWithArgs()`. I've pasted the corrected snippet in the Github discussion thread ( https://github.com/lhorie/mithril.js/issues/413#issuecomment-72304725 )

--
You received this message because you are subscribed to the Google Groups "mithril" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mithriljs+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Reply all
Reply to author
Forward
0 new messages