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

dynamically loaded child tags don't get into the parents tags object #1174

Closed
MartinMuzatko opened this Issue Aug 29, 2015 · 19 comments

Comments

Projects
None yet
@MartinMuzatko
Contributor

MartinMuzatko commented Aug 29, 2015

See http://plnkr.co/edit/gvpVcQ8a8dfDQc0MBlFt?p=preview

While the dynamically loaded tag gets displayed, it won't be accessible via this.tags.
Are children tags only added to this.tags object (in parent scope) if their HTML is present when the mount event gets called?

@MartinMuzatko MartinMuzatko changed the title from dynamically loaded tags don't get into the children tag list to dynamically loaded child tags don't get into the parents tag list Aug 29, 2015

@MartinMuzatko MartinMuzatko changed the title from dynamically loaded child tags don't get into the parents tag list to dynamically loaded child tags don't get into the parents tags object Aug 29, 2015

@rsbondi

This comment has been minimized.

Show comment
Hide comment
@rsbondi

rsbondi Aug 29, 2015

Member

Looks like parent of news tag is undefined, need a mechanism for preserving on dynamic mount.

In the mean time you could do this

var p = riot.mount(page)[0]
app.tags[page] = p
Member

rsbondi commented Aug 29, 2015

Looks like parent of news tag is undefined, need a mechanism for preserving on dynamic mount.

In the mean time you could do this

var p = riot.mount(page)[0]
app.tags[page] = p
@GianlucaGuarini

This comment has been minimized.

Show comment
Hide comment
@GianlucaGuarini

GianlucaGuarini Aug 30, 2015

Member

I am not sure supporting this feature makes really sense. It means that any tag mounted should search whether in its parents there is a riot tag instance, and this could slow down a lot the code especially in the loops. I think the @rsbondi solution is a good workaround. But that's just my opinion

Member

GianlucaGuarini commented Aug 30, 2015

I am not sure supporting this feature makes really sense. It means that any tag mounted should search whether in its parents there is a riot tag instance, and this could slow down a lot the code especially in the loops. I think the @rsbondi solution is a good workaround. But that's just my opinion

@rsbondi

This comment has been minimized.

Show comment
Hide comment
@rsbondi

rsbondi Aug 30, 2015

Member

#1145 may be related where @AndrewSwerlick says that "the riot.mount strategy breaks parent child relationships", not sure if this is what he was referring to.

I think it would be nice to have an elegant, efficient solution, and maybe exposing more of the internals as suggested in the PR would be a reasonable approach. My example above is a shorthand version but there is more that goes on and it would be good to fully evaluate the tags in a common way.

Member

rsbondi commented Aug 30, 2015

#1145 may be related where @AndrewSwerlick says that "the riot.mount strategy breaks parent child relationships", not sure if this is what he was referring to.

I think it would be nice to have an elegant, efficient solution, and maybe exposing more of the internals as suggested in the PR would be a reasonable approach. My example above is a shorthand version but there is more that goes on and it would be good to fully evaluate the tags in a common way.

@AndrewSwerlick

This comment has been minimized.

Show comment
Hide comment
@AndrewSwerlick

AndrewSwerlick Aug 30, 2015

@rsbondi That's exactly what I'm referring to. This is a good workaround, but I also think it deserves to be supported in an elegant and efficient way like you suggest. But knowing this workaround exists means that we could make a utility library external to riot that handles that process. My only concern would be is the .tags property considered to be a stable/public part of the api? It's not documented anywhere that I'm aware of. @GianlucaGuarini can you comment?

UPDATE:

As I'm thinking about this, it still doesn't solve the problem the on the child tag I don't think .parent will be defined, but that's slightly less important.

AndrewSwerlick commented Aug 30, 2015

@rsbondi That's exactly what I'm referring to. This is a good workaround, but I also think it deserves to be supported in an elegant and efficient way like you suggest. But knowing this workaround exists means that we could make a utility library external to riot that handles that process. My only concern would be is the .tags property considered to be a stable/public part of the api? It's not documented anywhere that I'm aware of. @GianlucaGuarini can you comment?

UPDATE:

As I'm thinking about this, it still doesn't solve the problem the on the child tag I don't think .parent will be defined, but that's slightly less important.

@rsbondi

This comment has been minimized.

Show comment
Hide comment
@rsbondi

rsbondi Aug 30, 2015

Member

p.parent = app, like I said it was a shorthand version, for illustration.

For me the appeal of riot is the simplicity, the limited api and short learning curve. So I think the key here is finding a balanced way that will not add too much to the api if at all, or handle internally, which has its challenges as @GianlucaGuarini mentioned.

Maybe a simple solution would be a tag.mountChild method that can wrap to riot.mount. My only concern is that we don't make a habit of adding to the api unless the benefit drastically outweighs the cost. Maybe a mountChild mixin would be best. It seems like all that is missing is the tags property getting updated and the parent getting set.

Member

rsbondi commented Aug 30, 2015

p.parent = app, like I said it was a shorthand version, for illustration.

For me the appeal of riot is the simplicity, the limited api and short learning curve. So I think the key here is finding a balanced way that will not add too much to the api if at all, or handle internally, which has its challenges as @GianlucaGuarini mentioned.

Maybe a simple solution would be a tag.mountChild method that can wrap to riot.mount. My only concern is that we don't make a habit of adding to the api unless the benefit drastically outweighs the cost. Maybe a mountChild mixin would be best. It seems like all that is missing is the tags property getting updated and the parent getting set.

@rsbondi

This comment has been minimized.

Show comment
Hide comment
@rsbondi
Member

rsbondi commented Aug 30, 2015

@MartinMuzatko

This comment has been minimized.

Show comment
Hide comment
@MartinMuzatko

MartinMuzatko Aug 31, 2015

Contributor

Using this.tags[page] = riot.mount(page)[0] works well enough in my case. I like the mixin too (We definitely need a mixin library in the 2.3 docs or examples, or I'll create an external one 😃 )

I'd agree with @rsbondi to avoid additional public methods in riot to solve this problem.

We could perhaps use additional triggers in riot, so users can decide to manually enable that behavior, kind of like an extended update block. As you probably want to avoid to have such a heavy load on every update event. Then again, the mixin solution or getting the element when mounting works for me just as well, so it is up to you guys.

Contributor

MartinMuzatko commented Aug 31, 2015

Using this.tags[page] = riot.mount(page)[0] works well enough in my case. I like the mixin too (We definitely need a mixin library in the 2.3 docs or examples, or I'll create an external one 😃 )

I'd agree with @rsbondi to avoid additional public methods in riot to solve this problem.

We could perhaps use additional triggers in riot, so users can decide to manually enable that behavior, kind of like an extended update block. As you probably want to avoid to have such a heavy load on every update event. Then again, the mixin solution or getting the element when mounting works for me just as well, so it is up to you guys.

@jayk

This comment has been minimized.

Show comment
Hide comment
@jayk

jayk Sep 1, 2015

I would like a clean way to do this as well. The use case I have in mind is generating a form dynamically from data using set of riot-based form widgets. I understand the mixin idea and I'm using mixins in other places, but I am concerned that the mixin is too tightly bound to how Riot does things right now... which means when riot gets an update it can easily break, as will all the things that use the mixin. :-(

What we are really talking about is the ability to add a riot-tag after the parent has been mounted, and that seems pretty within the Riot core wheelhouse to me.

From what I can tell, this is really just exposing an official way to trigger 'initChildTag' from riot/lib/browser/tag/util.js - am I missing something there?

Again, since tag handling is pretty much what Riot does, it seems to be adding some simple mechanism to ensure tags can be added properly, even after initial mount, is well within scope. I'm happy to take a swing at a patch, if it is likely to be incorporated.

Edit: I'm totally fine with making it a manual thing (thus avoiding the performance hit on update) I'd just like an official way to do it without creating a hard-binding from my tags to the implementation details of Riot.

jayk commented Sep 1, 2015

I would like a clean way to do this as well. The use case I have in mind is generating a form dynamically from data using set of riot-based form widgets. I understand the mixin idea and I'm using mixins in other places, but I am concerned that the mixin is too tightly bound to how Riot does things right now... which means when riot gets an update it can easily break, as will all the things that use the mixin. :-(

What we are really talking about is the ability to add a riot-tag after the parent has been mounted, and that seems pretty within the Riot core wheelhouse to me.

From what I can tell, this is really just exposing an official way to trigger 'initChildTag' from riot/lib/browser/tag/util.js - am I missing something there?

Again, since tag handling is pretty much what Riot does, it seems to be adding some simple mechanism to ensure tags can be added properly, even after initial mount, is well within scope. I'm happy to take a swing at a patch, if it is likely to be incorporated.

Edit: I'm totally fine with making it a manual thing (thus avoiding the performance hit on update) I'd just like an official way to do it without creating a hard-binding from my tags to the implementation details of Riot.

@rsbondi

This comment has been minimized.

Show comment
Hide comment
@rsbondi

rsbondi Dec 24, 2015

Member

This really is not a bug, more of the design of how riot.mount works, riot is mounting, not the tag. I think the solution is implementing riot-tag={ expression }, right now riot-tag is not dynamic and only evaluated on riot.mount. I have a WIP version(sorry for the complex example) that I need to clean up and hopefully submit by year end. I think this is a good solution, it is not really adding to the API, just combining existing API features.

Member

rsbondi commented Dec 24, 2015

This really is not a bug, more of the design of how riot.mount works, riot is mounting, not the tag. I think the solution is implementing riot-tag={ expression }, right now riot-tag is not dynamic and only evaluated on riot.mount. I have a WIP version(sorry for the complex example) that I need to clean up and hopefully submit by year end. I think this is a good solution, it is not really adding to the API, just combining existing API features.

@aMarCruz

This comment has been minimized.

Show comment
Hide comment
@aMarCruz

aMarCruz Jan 23, 2016

Member

@rsbondi , I like the mountChild solution, it is simple. riot-tag with expressions looks ugly and guess will generate issues because the life cycle of the context. I'm thinking in the expression returning undefined.

Member

aMarCruz commented Jan 23, 2016

@rsbondi , I like the mountChild solution, it is simple. riot-tag with expressions looks ugly and guess will generate issues because the life cycle of the context. I'm thinking in the expression returning undefined.

@crisward

This comment has been minimized.

Show comment
Hide comment
@crisward

crisward Jan 26, 2016

Member

I realise there is a fix in the pipeline for the dynamic riot-tag, and the if statement issue. But in the meantime I've release this very small tag which can be used to fix both issues. I had 5 subtags with if statements on, and performance became a real issue, subtag is much faster.

https://github.com/crisward/riot-subtag

npm install riot-subtag
Member

crisward commented Jan 26, 2016

I realise there is a fix in the pipeline for the dynamic riot-tag, and the if statement issue. But in the meantime I've release this very small tag which can be used to fix both issues. I had 5 subtags with if statements on, and performance became a real issue, subtag is much faster.

https://github.com/crisward/riot-subtag

npm install riot-subtag
@kokujin

This comment has been minimized.

Show comment
Hide comment
@kokujin

kokujin Mar 30, 2016

Any news on dynamically added sub-tags?

kokujin commented Mar 30, 2016

Any news on dynamically added sub-tags?

@GianlucaGuarini GianlucaGuarini added this to the 3.0.0 milestone Mar 30, 2016

@rsbondi rsbondi referenced this issue Jul 20, 2016

Closed

Events not triggering in child-tag. #1896

1 of 7 tasks complete

@rsbondi rsbondi added the fixed label Jul 24, 2016

@crisward

This comment has been minimized.

Show comment
Hide comment
@crisward

crisward Nov 22, 2016

Member

looks like riot-subtag is now redundant! - will leave live for riot.2.* users. Updated Readme.

Member

crisward commented Nov 22, 2016

looks like riot-subtag is now redundant! - will leave live for riot.2.* users. Updated Readme.

@rsbondi rsbondi referenced this issue Dec 9, 2016

Closed

Child tag not updated when parent did update. #2151

0 of 5 tasks complete
@giantbits

This comment has been minimized.

Show comment
Hide comment
@giantbits

giantbits Feb 17, 2017

Hi

I still don't get if and how it's possible to do something like:

on('mount', function(){
    var focus = document.createElement(“focus”)
    this.mid.appendChild(focus)
    riot.mount(“focus”, {currentFocus: opts.currentFocus})
}

The “focus” tag get's rendered as expected but it has no parent and it does not become a part of root.tags[]. I guess that is why the focus tag never gets updated on root.update().
My goal is to have a fully dynamic layout where I can put several different tags into different grid locations – loaded from a Json config file.

If I do it this way:

on('mount', function(){
    var focus = document.createElement(“focus”)
    this.mid.appendChild(focus)
    var subTag = riot.mount(“focus”, {currentFocus: opts.currentFocus})[0]
    this.tags['focus'] = subTag
}

Then I get this result: this.tags[sometag:Tag$1, focus:Object]. I need focus to be a “Tag$1” too for it to work correctly, right?

Sorry for bringing this up again. Any help is much appreciated.

Thanks

giantbits commented Feb 17, 2017

Hi

I still don't get if and how it's possible to do something like:

on('mount', function(){
    var focus = document.createElement(“focus”)
    this.mid.appendChild(focus)
    riot.mount(“focus”, {currentFocus: opts.currentFocus})
}

The “focus” tag get's rendered as expected but it has no parent and it does not become a part of root.tags[]. I guess that is why the focus tag never gets updated on root.update().
My goal is to have a fully dynamic layout where I can put several different tags into different grid locations – loaded from a Json config file.

If I do it this way:

on('mount', function(){
    var focus = document.createElement(“focus”)
    this.mid.appendChild(focus)
    var subTag = riot.mount(“focus”, {currentFocus: opts.currentFocus})[0]
    this.tags['focus'] = subTag
}

Then I get this result: this.tags[sometag:Tag$1, focus:Object]. I need focus to be a “Tag$1” too for it to work correctly, right?

Sorry for bringing this up again. Any help is much appreciated.

Thanks

@Joylei

This comment has been minimized.

Show comment
Hide comment
@Joylei

Joylei Feb 17, 2017

Contributor

@giantbits

you're looking for data-is directive

<my-tag>
  <!-- dynamic component -->
  <div data-is={ component }></div>
  <button onclick={ switchComponent }>
    Switch
  </button>

  <script>
    this.component = 'foo'

    switchComponent() {
      // riot will render the <bar> component
      // replacing <foo>
      this.component = 'bar'
    }
  </script>
</my-tag>
Contributor

Joylei commented Feb 17, 2017

@giantbits

you're looking for data-is directive

<my-tag>
  <!-- dynamic component -->
  <div data-is={ component }></div>
  <button onclick={ switchComponent }>
    Switch
  </button>

  <script>
    this.component = 'foo'

    switchComponent() {
      // riot will render the <bar> component
      // replacing <foo>
      this.component = 'bar'
    }
  </script>
</my-tag>
@giantbits

This comment has been minimized.

Show comment
Hide comment
@giantbits

giantbits Feb 17, 2017

@Joylei

Thank you for your input but that's only half of what I'm searching for. Your solution depends on having the tag already in the DOM

div(data-is={ component })

In my scenario I have an undefined number of custom tags that should be rendered. Think of the riot-bulma flex layout example. You have six tiles there. How can I achieve this to be fully dynamic with riot without knowing number and type of the tiles before mounting.

I have a elements.json which defines for example 4 different tag-types (but there could be any number):

[
    { type: 'news-tag'},
    { type: 'faq-tag'},
    { type: 'faq-tag'},
    { type: 'todo-tag'}
]

Now I have one app.tag where I want to mount all these tags dynamically after loading the elements.json. How could that be done with riot?

Or can I do something like:

<my-tag>
  <!-- dynamic component -->
  <div each={ component in opts.components  } data-is={ component }></div>
 
  <script>
  </script>
</my-tag>

I think I'm searching for a way to do a mountTo() after my tag was mounted. Something like:

<my-tag>
  <div id="target"></div>

  <script>

      on.('mount', function() {
            var focus = document.createElement(“focus”)
           documentGetElementByID('target').appendChild(focus)
           riot.mountTo(this, “focus”, {currentFocus: opts.currentFocus})
      })

  
  </script>
</my-tag>

Thanks again. I'm clueless and stuck with that problem.

giantbits commented Feb 17, 2017

@Joylei

Thank you for your input but that's only half of what I'm searching for. Your solution depends on having the tag already in the DOM

div(data-is={ component })

In my scenario I have an undefined number of custom tags that should be rendered. Think of the riot-bulma flex layout example. You have six tiles there. How can I achieve this to be fully dynamic with riot without knowing number and type of the tiles before mounting.

I have a elements.json which defines for example 4 different tag-types (but there could be any number):

[
    { type: 'news-tag'},
    { type: 'faq-tag'},
    { type: 'faq-tag'},
    { type: 'todo-tag'}
]

Now I have one app.tag where I want to mount all these tags dynamically after loading the elements.json. How could that be done with riot?

Or can I do something like:

<my-tag>
  <!-- dynamic component -->
  <div each={ component in opts.components  } data-is={ component }></div>
 
  <script>
  </script>
</my-tag>

I think I'm searching for a way to do a mountTo() after my tag was mounted. Something like:

<my-tag>
  <div id="target"></div>

  <script>

      on.('mount', function() {
            var focus = document.createElement(“focus”)
           documentGetElementByID('target').appendChild(focus)
           riot.mountTo(this, “focus”, {currentFocus: opts.currentFocus})
      })

  
  </script>
</my-tag>

Thanks again. I'm clueless and stuck with that problem.

@GianlucaGuarini

This comment has been minimized.

Show comment
Hide comment
@GianlucaGuarini

GianlucaGuarini Feb 17, 2017

Member

Or can I do something like:

<my-tag>
  <!-- dynamic component -->
  <div each={ component in opts.components  } data-is={ component }></div>
 
  <script>
  </script>
</my-tag>

Yes, this is supported

Member

GianlucaGuarini commented Feb 17, 2017

Or can I do something like:

<my-tag>
  <!-- dynamic component -->
  <div each={ component in opts.components  } data-is={ component }></div>
 
  <script>
  </script>
</my-tag>

Yes, this is supported

@giantbits

This comment has been minimized.

Show comment
Hide comment
@giantbits

giantbits Feb 17, 2017

@GianlucaGuarini Nice. And thanks. Sometimes talking (writing) about a problem brings up the solution on its own. Now I just have to deal with "how do I pass specific opts to those dynamic components"-problem.

Thanks @ALL!

giantbits commented Feb 17, 2017

@GianlucaGuarini Nice. And thanks. Sometimes talking (writing) about a problem brings up the solution on its own. Now I just have to deal with "how do I pass specific opts to those dynamic components"-problem.

Thanks @ALL!

@sourcegr

This comment has been minimized.

Show comment
Hide comment
@sourcegr

sourcegr Feb 17, 2017

I also had the need to do such a thing, but I never had to have access to the created tags through the this.tags array.

Anyway, I could see even then that If we want to make full dynamic components, we really need such a feature. So, I believe this should be supported in some version, because the proposed workaround may serve us for a specific case, but not allways.

  1. Because, with the workaround proposed above, we cannot have control on the exact position in the DOM where the new tag should be inserted at, so if we want to inject two different tags in different positions,we are aut of luck here...

  2. We cannot add a new component later, without altering the opts.components causing an update and thus, unmounting the allready existing tags - this is not something that should happen

I also believe that it should be as simple as

this.mount('nice-component', opts})

from the tag's scope.

any thoughs?

sourcegr commented Feb 17, 2017

I also had the need to do such a thing, but I never had to have access to the created tags through the this.tags array.

Anyway, I could see even then that If we want to make full dynamic components, we really need such a feature. So, I believe this should be supported in some version, because the proposed workaround may serve us for a specific case, but not allways.

  1. Because, with the workaround proposed above, we cannot have control on the exact position in the DOM where the new tag should be inserted at, so if we want to inject two different tags in different positions,we are aut of luck here...

  2. We cannot add a new component later, without altering the opts.components causing an update and thus, unmounting the allready existing tags - this is not something that should happen

I also believe that it should be as simple as

this.mount('nice-component', opts})

from the tag's scope.

any thoughs?

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