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

How to prototype a tag #1053

Closed
steelbrain opened this issue Jul 30, 2015 · 15 comments
Closed

How to prototype a tag #1053

steelbrain opened this issue Jul 30, 2015 · 15 comments

Comments

@steelbrain
Copy link
Contributor

The more I use riot, the more I feel like I am doing this

function something(){
  if(this === null) throw new Error("You must initialize using constructor")
  this.doSomething = function(){
    // do something
  }
}

instead of

function something(){
  if(this === null) throw new Error("You must initialize using constructor")
}
something.prototype.doSomething = function(){
  // do something
}

is it planned? or can I expect it to be implemented in riot?

@steelbrain
Copy link
Contributor Author

It would be nice to have something like this in riot

<my-tag>
  <script>
    Riot({
      created: function(){ console.log("created") },
      attached: function(){ console.log("attached") },
      detached: function(){ console.log("detached") },
      updated: function(){ console.log("updated") }
    })
  </script>
</my-tag>

this way it would execute that code on the top level immediately, and allow us to create and use prototypes, as well as provide a beautiful events API

@steelbrain steelbrain changed the title How to prototype for a tag How to prototype a tag Jul 30, 2015
@cognitom
Copy link
Member

I think Riot should move to ES2015 in the future. Then we'll be able to use class in JavaScript. I haven't think about it deeply yet, but something like this would be nice as a result of compilation, instead of riot.tag('my-tag', html, style, constructor).

class MyTag extends riot.Tag {
  constructor(opts) { ... }
  template() { ... }
  style() { ... }
  someMethod() { ... }
}

@nippur72
Copy link
Contributor

nippur72 commented Aug 3, 2015

@cognitom my nippur72/RiotTS does just this, it lets you use class and @decorators with a riot element. It's meant to be used with TypeScript, but I guess it should work with plain ES6 too.

To make Riot ES6 compatible, we need riot.tag to accept a function parameter also, not just a plain object. Also we need a riot.Tag class defined somewhere so that you can extend it. In my RiotTS repo I do some hacks to simulate it, but it would be nice to have them supported natively.

@pitaj
Copy link

pitaj commented Aug 24, 2015

Ugh. ES6 classes make me cry. Please, kill them before they take over my precious prototypal inheritance.

Please *don't use constructors or classes in your API. And please, please don't require new if you do.

If you choose to implement inheritability, use something like Method 3 or 4 shown here

@steelbrain
Copy link
Contributor Author

Ugh. ES6 classes make me cry. Please, kill them before they take over my precious prototypal inheritance.

@pitaj I am going to assume that you were kidding me.

@pitaj
Copy link

pitaj commented Aug 24, 2015

@steelbrain Well, you'd be assuming incorrectly. Classes, constructors, and any kind of classical inheritance patterns don't belong in JavaScript. It is a prototypal language and should be treated as such.

@steelbrain
Copy link
Contributor Author

@pitaj I am afraid you are misinformed my friend. Javascript is more than just a "prototypal" language. and I am sure that we agree that the people writing ECMA specifications aren't retards, there is a reason they put them in ES6. There is a reason v8 is shipping them, and that reason is that Classes are the way forward. They are so much simpler to use and work with. Besides If a person like me (and I am sure that I am not alone) writes both PHP (+ Hack) and Javascript, both languages feel like home to him.

@pitaj
Copy link

pitaj commented Aug 24, 2015

PHP ewwwwwwwwwwwwww

Classical inheritance sucks, new sucks, and classes suck. The only reason people like them is because other languages have them and they are a hospitality feature.

@cognitom
Copy link
Member

@steelbrain how about talking about this again a few month later? This issue seems the matter of Riot 3.0 or more. At least in 2.x we couldn't accept such a big change, I think. And by then, ES6 must be more accepted generally, too.

@steelbrain
Copy link
Contributor Author

@cognitom a few months is a long-long time. but anyways, until then I am using this snippet to "emulate" Polymer like interface

window.Riot = function(riot, proto) {
  if (proto.properties) {
    for (let key in proto.properties) {
      if (proto.properties.hasOwnProperty(key)) {
        const info = proto.properties[key]
        if (info !== true) {
          // Use predefined value
          riot[key] = info
          continue
        }
        // look in dataset and params
        const htmlKey = key.toLowerCase()
        if (typeof riot.opts[key] !== 'undefined') {
          riot[key] = riot.opts[key]
        } else if (riot.root.dataset.hasOwnProperty(key.toLowerCase(htmlKey))) {
          riot[key] = JSON.parse(riot.root.dataset[htmlKey])
          riot.root.removeAttr('data-' + htmlKey)
        } else if (riot.root.hasAttribute(htmlKey)) {
          riot[key] = JSON.parse(riot.root.getAttribute(htmlKey))
          riot.root.removeAttr(htmlKey)
        } else {
          throw new Error(`'${key}' parameter not found`)
        }
      }
    }
  }

  delete riot.properties
  extend(riot, proto)

  if (proto.attached) {
    riot.on('mount', proto.attached)
  }
  if (proto.detached) {
    riot.on('unmount', proto.detached)
  }
  if (proto.updated) {
    riot.on('update', proto.updated)
  }

  if (proto.created) {
    proto.created.call(riot)
  }
}
<some-tag>
  <div>Wow</wow>
  <script>
    Riot(this, {
      created: function(){
        console.log("I was created")
        console.log(this.SomeValue)
        console.log(this.PreDefinedValue)
      },
      updated: function(){
        console.log("I was updated")
      },
      attached: function(){
        console.log("I was attached")
      },
      detached: function(){
        console.log("I was detached")
      },
      properties: {
        SomeValue: true, // it'll extract the value from attributes or the riot mount arguments
        PreDefinedValue: "Hey There"
      }
    })
  </script>
</some-tag>

now lets mount it with different arguments

riot.mount('some-tag', {SomeValue: "asd"})
// > I was created
// > asd
riot.mount('some-tag', {SomeValue: false})
// > I was created
// > false
riot.mount('some-tag')
// > Error<SomeValue parameter not found>
domEl = document.createElement('some-tag')
riot.mount('some-tag')
// > Error<SomeValue parameter not found>
domEl = document.createElement('some-tag')
domEl.setAttribute("SomeValue", '{"key": "value"}')
riot.mount('some-tag')
// > I was created
// > {key: "value"}
// (it automatically tries to json decode it)

This gives us a way to define tags that can be constructed/initiated from both the DOM and custom mount. It has also reduced the pain of extracting argument in each tag, now I just have to write that function and I am done.

@cognitom
Copy link
Member

Oh, complicated. Why not use on as is?

@steelbrain
Copy link
Contributor Author

@cognitom: on is nice when you do it once or twice, sometimes you just want to hand it a list of things that are quite obvious to bind. Also it's only complicated in the declaration, the usage is quite simple

@cognitom
Copy link
Member

Thanks for your explanation. Can't we keep the .tag way more? I think there's some room to improve the compiled code, but the .tag file itself seems already sophisticated for me. That's why I like Riot.

@nippur72
Copy link
Contributor

@steelbrain nice polymer emulation (BTW if you're a Polymer fan, check out my PolymerTS repo). If you want to make it complete, you could override document.createElement() and document.createElementNS() so that they mount automatically your riot element. This can be pushed even further, allowing Riot to be called from WebComponents (the only question is, is it worth the complexity?).

Regarding events like attached() etc, I put my +1 as I do a similar thing in RiotTS (with different event names).

@GianlucaGuarini
Copy link
Member

With riot 2.3.0 we expose the internal Tag instance. I am closing this issue

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants
@cognitom @pitaj @GianlucaGuarini @nippur72 @steelbrain and others