Open mitar opened 9 years ago
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.
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 :)
}
});
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?
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());
}
});
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');
});
}
});
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 :)
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?
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 ;)
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
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.
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.
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).
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.
Make a reproduction please.
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.
Instead of Blaze.remove
analog, instance.removeComponent()
has been implemented in 21d9160e58b3380cb114a8ef47a489d501956ed8.
Provide equivalents to
Blaze.render
,Blaze.renderWithData
andBlaze.remove
.For now you can use for
Blaze.render
:or
For
renderWithData
:And
Blaze.remove
: