I'm having trouble understanding when access to the shadowRoot of a component is available. Here is an image of a set of nested components:
So there are a handful of components:
I've instrumented the created and attached methods with log message printing the shadowRoot ( sr => ...) and I get the following:
mortgageDetails [FINE]: MortgageDetails created sr => null (:1)
moneyInput [FINE]: MoneyInput created sr => null (:1)
numWithUnitsInput [FINE]: NumWithUnitsInput created sr => null (:1)
rateInput [FINE]: RateInput created sr => null (:1)
paymentSchedule [FINE]: PaymentSchedule created sr => null (:1)
dateInput [FINE]: DateInput created sr => null (:1)
mortgageDetails [FINE]: MortgageDetails attached with sr => Instance of 'ShadowRoot' (:1)
The logging makes sense. Components are created in a reasonable order and then the attaching starts. The problem is though, the mortgage details is attached before its contained moneyInput is attached. If I add one more log statement in the MortgageDetails.attached I can see that its contained MoneyInput object has a shadowRoot:
mortgageDetails [FINE]: Composed moneyInput sr => Instance of 'ShadowRoot' (:1)
This is a problem for the way I'm doing things. I need some initialization event in the MoneyInput component to reach into the shadowRoot and attach some handlers. I can't use created because shadowRoot is not even set yet. I'm trying to use attached. I have code like this currently in the attach of MoneyInput:
_amountElement = shadowRoot.querySelector('#money-amount')
..onBlur.listen((evt) => reformatAmount())
..onFocus.listen((evt) => reformatAmount())
..onKeyUp.listen((evt) { if(evt.which == 13) reformatAmount(); });
Since MortgageDetails is being attached and MoneyInput has not yet been attached, an instance of MortgageDetails can not use the contained MoneyInput as it would like as it is not fully initialized. For example, in MortgageDetails activate I have:
(mortgageAmountInput = $["mortgage-amount"] as MoneyInput)
..label = r" $ Amount of Loan"
..onBlur.listen((_) => recalc())
..onFocus.listen((_) => recalc());
This fails because MoneyInput's activate has not been called. I think what I really need is an event that says the shadowRoot has been set, then on that event I could do my initialization.
What am I missing on the lifecycle of polymer elements?
Typically, if you're extending PolymerElement
the order of the main callbacks will be ready
, created
, attached
. These happen top-down: they're called on ancestors before descendants. It might confusing that ready
is called first, but it's because PolymerElement.created
calls it after setting up the DOM and even handlers, and because of constructor ordering that happens before your classes created
constructor.
Polymer also adds a domReady
method that you can override which is called when your element's children are guaranteed to have been created. That might be what you need.
See http://www.polymer-project.org/docs/polymer/polymer.html#lifecyclemethods for details on the lifecycle methods.
First I would see if you can sidestep the ordering issue with data-binding and declarative events, though.