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

Provide equivalents to Blaze.render, Blaze.renderWithData and Blaze.remove #36

Open
mitar opened this issue Apr 25, 2015 · 16 comments
Open

Comments

@mitar
Copy link
Member

mitar commented Apr 25, 2015

Provide equivalents to Blaze.render, Blaze.renderWithData and Blaze.remove.

For now you can use for Blaze.render:

renderedView = Blaze.render MyComponent.renderComponent parentComponent

or

renderedView = Blaze.render BlazeComponent.get('MyComponent').renderComponent parentComponent

For renderWithData:

renderedView = Blaze.renderWithData MyComponent.renderComponent(parentComponent), data

And Blaze.remove:

Blaze.remove renderedView
@bensmeets
Copy link

In the meantime, can you help me out with this example:

ViewStackComponent = BlazeComponent.extendComponent({
  add: function (name) {
    var comp = BlazeComponent.getComponent(name).renderComponent(this);
    var view = Blaze.renderWithData(comp, data, this.firstNode());
    // Insert question here :)
  }
});

In that code, I want to animate IN the panel that is being created. I know about the insertDomElement, but I don't want to use that route, since the code I need to animate is inside the component class of the panel. So getting to the question, how can I get a reference to the component instance of the "comp" I just created?

If I try it after BlazeComponent.getComponent(name); I get a Class back (not the instance). If I try it after renderComponent() I get a Blaze.Template back. If I try it after renderWithData() I get a Blaze.View back. Is what I'm trying even possible? Views...Templates...Instances...Components...oh my :)

Tnx.

@mitar
Copy link
Member Author

mitar commented Apr 28, 2015

You can do:

ViewStackComponent = BlazeComponent.extendComponent({
  add: function (name) {
    var component = new (BlazeComponent.getComponent(name))();
    var renderedComponent = component.renderComponent(this);
    var view = Blaze.renderWithData(comp, data, this.firstNode());
    // Insert question here :)
  }
});

@bensmeets
Copy link

Got it, tnx!

Does components change anything in the lifecycle of that view? I need to wait for that view to be rendered on screen before talking to it. In my old version of mentioned stack, i used "view.onViewReady()" for that. If I use that in this example, it's not called. Or am I missing something.

ViewStackComponent = BlazeComponent.extendComponent({
  add: function (name) {
    var comp = new (BlazeComponent.getComponent(name))();
    var renderedComponent = comp.renderComponent(this);
    var view = Blaze.renderWithData(comp, data, this.firstNode());

    view.onViewReady(function() {
      console.log('I am never called');
      comp.callFirstWith(null, 'doAnything');
    });
  }
});

P.S. I tried listening to the comp instead of to the view. But there is no equal method for components right? I can't call say

comp.onRendered(function() {
   console.log('Do this as well when rendered');
});

Can I?

@mitar
Copy link
Member Author

mitar commented Apr 28, 2015

I do not get you? You have onRendered method in your component you can use.

class MyComponent extends BlazeComponent
  @register 'MyComponent'

  template: ->
    'MyComponent'

  onRendered: ->
    console.log 'I am never called'
    @callFirstWith null, 'doAnything'
ViewStackComponent = BlazeComponent.extendComponent({
  add: function (name) {
    var comp = new (BlazeComponent.getComponent(name))();
    var renderedComponent = comp.renderComponent(this);
    var view = Blaze.renderWithData(comp, data, this.firstNode());
  }
});

@mitar
Copy link
Member Author

mitar commented Apr 28, 2015

Alternatively, you can also do:

ViewStackComponent = BlazeComponent.extendComponent({
  add: function (name) {
    var comp = new (BlazeComponent.getComponent(name))();
    var renderedComponent = comp.renderComponent(this);
    var view = Blaze.renderWithData(comp, data, this.firstNode());
    this.autorun(function (computation) {
      return unless comp.isRendered();
      computation.stop()
      console.log('Do this as well when rendered');
    });
  }
});

@bensmeets
Copy link

That last one did the trick. Thanks!

Food for thought, maybe match the Blaze api for this regarding "extending" onRendered callbacks like:

comp.onRendered(function() {
});

But maybe that's just a nice to have :)

@mitar
Copy link
Member Author

mitar commented Apr 28, 2015

No. This is not reusable nor composable. You should put hooks into the class method, so that you can reuse it in your children implementations. If you attach hooks like Blaze does, then you have issues when you want to reuse those hooks. You have to copy them. And you have to manually decide how to override/extend them.

Reactive isRendered on the other hand allows you nice composition. Combining onRendered with onClick is pretty hard. But if you have return unless @isRendered() && @isClicked() in the autorun, it is pretty easy to combine.

In your case, I would suggest you just use the first approach. Class method onRendered. Why you prefer the second?

@bensmeets
Copy link

Well, it's just a matter of seperation of concerns (kind of). My parent lays out it's children (simply saying). The parent is the only one that should know of all it's children and I want it to be able to tell each child where to go.

When a child is added, I want the parent to be able to tell it "you need to go to this position". But telling it to that child can only be done once it's rendered.

So I was stuck :) But the workaround works, it just feels a bit verbose. I'm not sure if I understand how that would make it less composable either. But will try to read it again later, just in case ;)

@mitar
Copy link
Member Author

mitar commented Apr 28, 2015

But the workaround works, it just feels a bit verbose.

Which workaround? My snippets above are not workaround, but normal code. :-)

I'm not sure if I understand how that would make it less composable either.

Hooks would make it less composable. This is why we do not provide them. :-)

The parent is the only one that should know of all it's children and I want it to be able to tell each child where to go.

But why don't you create an explicit API for this?

class ChildComponent
  ...
  onRendered: ->
    @componentParent().tellMeWhereToGo()

You could also do something like this in the parent:

listOfRenderedChildren = []
@autorun =>
  newListOfRenderedChildren = (child for child in @componentChildren() when child.isRendered())
  for child in newListOfRenderedChildren when child not in listOfRenderedChildren
    child.iAmTellingYouWhereToGo()
  for child in listOfRenderedChildren when child not in newListOfRenderedChildren
    @myChildHasBeenRemoved child
  listOfRenderedChildren = newListOfRenderedChildren

@bensmeets
Copy link

Which workaround? My snippets above are not workaround, but normal code. :-)

You're right, not a workaround. It's just different related to Blaze, which is fine. But for me as a noob, it's intimidating :) Might be for others as well.

But why don't you create an explicit API for this?

Because that's a hard link between the child and the parent. It must be my old school thinking, but to me that seemed inappropriate since it's possible that the child is used without the wrapping parent. Then it call's a function that won't exist?

I'm using the autorun example now, works great.

@mitar
Copy link
Member Author

mitar commented Apr 28, 2015

Because that's a hard link between the child and the parent. It must be my old school thinking, but to me that seemed inappropriate since it's possible that the child is used without the wrapping parent. Then it call's a function that won't exist?

But when it is used with the wrapping parent, how does the wrapping parent interact with the component? Make that into an explicit API and this is it. Or calling it from the child, or calling it from the parent.

@bensmeets
Copy link

True, exactly what I was trying to do. But had a hard part with waiting for the child component to be rendered (because the explicit API I'm calling from the parent to the child, needs that child's DOM).

@bensmeets
Copy link

Quick note, currently when you use Blaze.remove and directy after that call something that (e.g.) loops through this.componentChildren(), I'm getting weird errors about DOM not being available. This might be related to the package dispatch:kernel I use though.

@mitar
Copy link
Member Author

mitar commented May 3, 2015

Make a reproduction please.

@bensmeets
Copy link

TL;DR I think this works.

It must be something else, hidden within one of the packages or animations I use. Will dig deeper to find out the exact cause of the DOM exception, but in this http://meteorpad.com/pad/fiBufg8EQiyPJX8r9/Remove%20then%20loop example it works.

@mitar
Copy link
Member Author

mitar commented Oct 12, 2015

Instead of Blaze.remove analog, instance.removeComponent() has been implemented in 21d9160.

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

No branches or pull requests

2 participants