Parent template onRendered function invokes before child template. How to execute parent templates function after child template rendered.
<template name="parent">
{{#each object}}
{{> child}}
{{/each}}
</template>
<template name="child">
<img src="someurl" data-src="someurl">
</template>
Now I need to execute some document ready function so
Template.parent.onRendered(function() { // doesnt invokes after child template
$("img").unveil();
$(window).trigger("lookup");
});
A combination of autorun and afterFlush is probably what you need. Give something like this a try:
Template.parent.onRendered(function() {
this.autorun(function() {
// replace the find with whatever is in your helper
// which returns the children array/cursor
if (Children.find().count()) {
// this should run after the child templates have been rerendered
Tracker.afterFlush(function() {
$('img').unveil();
$(window).trigger('lookup');
});
}
});
});
While using Tracker.afterFlush is an option that produces the needed behavior, the best way to do something like this is to just make use of the child template's onRendered function. As soon as the child template is rendered then needed code will execute.
Template.child.onRendered(function() {
this.$('img').unveil();
this.$(window).trigger('lookup');
});
This approach is more natural and allows the child template to be used in any other template as well without "breaking"
Related
Context
I have two Angular Components, a parent and a child. The parent passes an optional <ng-template> TemplateRef to the child as an #Input. The child then either renders this template or renders its default template if no input was given.
parent.component.html
// Pass in template as #Input
<child [customTemplate]="parentTemplate"></child>
// Define Custome Template
<ng-template #parentTemplate>
<div #container class="container">HELLO FROM CUSTOM CONTAINER</div>
</ng-template>
child.component.html
// Render correct template
<ng-container *ngTemplateOutlet="customTemplate || defaultTemplate;">
</ng-container>
// Define default template
<ng-template #defaultTemplate>
<div #container class="container">HELLO FROM DEFAULT CONTAINER</div>
</ng-template>
child.component.ts
export class ChildComponent implements AfterViewInit {
#Input() public customTemplate!: TemplateRef<HTMLElement>
#ViewChild("container")
containerRef!: ElementRef;
ngAfterViewInit() {
console.log(this.containerRef?.nativeElement.offsetWidth)
}
}
The Issue
I'd like to be able to access DOM Elements rendered by my child component. For example, say I want to find the width of an HTML Element currently rendered on screen.
If I define a template variable #container in both the customTemplate & the defaultTemplate, and then try to access that element using #ViewChild('container) or ContentChild('container), it returns undefined on the customTemplate but returns the correct Element Reference with the default.
How do I access these embedded DOM elements, regardless of whether or not the template is passed in as an #Input or if we use the default?
Check out the Stackblitz below for an example of what I'm trying to accomplish. Hopefully it'll be self-explanatory.
Stackblitz Example with full project
I modified the code and I am using only a child component instead of two like your example, in my solution I used ternary operator to decide which template I need to render.
My solution on stackblitz
app.componen.ts
<!-- Call to Child Component -->
<div class="childComponent">
<child [customTemplate]="displayCustom ? parentTemplate : null"></child>
</div>
i have a menu with many items. on item click I create a new Vue element (with Vuex store). The question is:
on close witch is outside the vue instance do I have to call this.$destroy or v-if="false" the root element.
<template>
<div v-if="closeVar">
....
</div>
</template>
<script>
export default {
...,
data() {
return {
closeVar: true
};
},
methods: {
onWindowsClose() {
this.$destroy() OR this.closeVar = false; ????
}
},
created() {
window[id + 'onWindowsClose'] = this.onWindowsClose;
}
}
</script>
From the $destroy docs:
In normal use cases you shouldn’t have to call this method yourself. Prefer controlling the lifecycle of child components in a data-driven fashion using v-if and v-for.
So the answer from the docs is that it's preferred to use v-if. Furthermore, you should place that v-if on the component's tag in the parent, not within the component on the root element:
Parent
<child v-if="closeVar"></child>
Otherwise, you'd only be removing the child's content rather than the entire component.
The difference there is, for example, the child's created hook would not be called again if you repopulated that particular instance with content (which would be possible because the component wouldn't have been destroyed.)
I have two components, one inside the other one. I have a click event on the parent component that should change the data value of the child component.
<template>
<div>
.....
.....
<my-component
:options="options">
</my-component>
</div>
.....
.....
</template>
<script>
...
...
data(){
}
methods:{
clickEvent(array_from_child){
this.array = array_from_child; //array is in my-component
}
}
components:{
....
}
</script>
I want to trigger the clickEvent method on child's element change. how to do that?
It seems like you're asking two different questions.
First, accessing a child's data from its parent:
If possible, you should pass the array to the child component using the child's props. Then simply change the array in the parent and any changes will be reflected in the child. If the array really needs to be in the child, then you can define a method to retrieve it.
<template>
<child-component ref="child">
</child-component>
</template>
methods: {
onClick() {
const myArray = this.$refs.child.getMyArray();
}
}
And then, in the child
methods: {
getMyArray() {
return this.myArray;
}
}
Second, triggering a change in the parent from the child
In this case, Flame's answer is most idiomatic. Emit a custom event in the child and listen for that event in the parent.
When you are going from a child to a parent, you should use events:
{
methods: {
clickEvent()
{
this.$emit('click', mydata);
}
}
So in your parent element, you can then attach your own callback to the emitted event like so:
<template>
<my-child-component #click="theParentMethod" />
</template>
You could also use some reactivity by passing an object reference from the parent to the child, so if you change the object in the child, the parent can detect the changes. However this comes with some reactivity gotcha's, see https://v2.vuejs.org/v2/guide/reactivity.html .
I'd like to declare attributes directly on a Polymer element, which are then passed inside the element and are readable/accessible outside of the element script.
I'd like to use the values of such attributes for deciding how to register the element.
tl;dr
I'm having an issue where I need to register an element some time after the whole page has loaded - i.e I want to manually register the element.
A solution for registering elements on demand:
<dom-module id="foo-element">
<template>
<span> Foo element </span>
</template>
</dom-module>
<script>
window.addEventListener("app-ready", function() {
"use strict";
Polymer({
is: "foo-element",
properties: {
//..... rest of element properties, methods, etc
Explaining what I'm doing above:
Instead of using HTMLImports.whenReady(<element-registration-code>), I use addEventListener(event, <element-registration-code>
I broadcast app-ready when I want the registration to happen
This allows me to register the element, on-demand
The reusability problem of the above solution
This poses a severe reusability problem - while this element in one of context needs to be registered at some specific point in time, in other context it might not - it should register itself using the standard HTMLImports.whenReady(<elementCode>) method.
An ideal example:
<!-- Registers automatically when `HTMLImports` are ready, the "regular" way-->
<foo-element></foo-element>
<!-- Registers only when it picks up an `app-ready` event-->
<foo-element no-auto-register register-on-event="app-ready"></foo-element>
and the element could look something like this:
<dom-module id="foo-element">
<template>
<span> Foo element </span>
</template>
</dom-module>
<script>
// if `no-auto-register` is set on the element,
// do not use `HTMLImports.whenReady()` and use
// `addEventListener` to register when an event
// with the value of `register-on-event` property fires.
</script>
Long story, short
Is there any way to declare a flag/property/attribute directly on the element which would decide how the registration should happen?
Passing an attribute to element might not work as element needs to be in ready state for that. Below are three ways that might help you with what you are trying
One way to lazy register your elements in Polymer is to use Polymer.Class instead of Polymer constructor. This way until you register your element manually it will not get rendered. Below is an working example of same.
<base href="https://polygit.org/components/">
<script src="webcomponentsjs/webcomponents-lite.js"></script>
<link rel="import" href="polymer/polymer.html">
<dom-module id="poly-form">
<template>
<style></style>
I am parent Element
<div>
<button onclick='register()'>Save</button>
</div>
<show-form></show-form>
</template>
</dom-module>
<script>
Polymer({
is: 'poly-form',
});
//element to lazy register
var showForm = Polymer.Class({
is: 'show-form',
ready: function() {
this.textContent = 'I am working...';
}
});
function register() {
var form = document.querySelector('poly-form');
//lazy register the element
document.registerElement('show-form', showForm);
}
</script>
<poly-form></poly-form>
In this example show-form element does not render until the button is clicked.
Note I've never really tried this with element containing dom.
Second way this should also be possible with importHref method.
Third way is global setting lazy-register where element gets register only when its first instance is called.
Sorry, the snippet is not as well constructed as it could have been. Hope it helps.
Class Style Constructor
If you want to set up your custom element's prototype chain but not register it immediately, you can use the Polymer.Class function. Polymer.Class takes the same prototype argument as the Polymer function, and sets up the prototype chain, but does not register the element. Instead it returns a constructor that can be passed to document.registerElement to register your element with the browser, and after which can be used to instantiate new instances of your element via code.
var MyElement = Polymer.Class({
is: 'my-element',
// See below for lifecycle callbacks
created: function() {
this.textContent = 'My element!';
}
});
document.registerElement('my-element', MyElement);
// Equivalent:
var el1 = new MyElement();
var el2 = document.createElement('my-element');
I am creating an unordered list in an Ember.js view by using the each helper. I also have some Javascript that runs on the didInsertElement hook, which selects each list item. However, that selector fails to select any elements if I generate the list using the each helper. Any thoughts on how to get results from the selector?
relevant view:
<script type="text/x-handlebars" data-template-name="slider">
<ul>
{{#each slide in controller}}
<li><img src="./resource/locations/1/test.jpg"></li>
{{/each}}
</ul>
<br>
</script>
relevant JS:
App.SliderView = Ember.View.extend({
templateName: "slider",
didInsertElement: function() {
var self = this;
var items = $(">ul>li", self.$());
//...
}
});
Update:
I believe the problem arises because I am loading the models that populate my controller asynchronously. Is there any way to listen to the event that occurs when the model is first populated?
My Model Code:
App.Location = DS.Model.extend({
thenSlider: DS.hasMany("slide", {async: true})
});
App.Slide = DS.Model.extend({
//...
});
Looks good to me, you're probably facing a problem where your data is populating after didInsertElement has fired. didInsertElement only fires when Slider has been inserted initially, not necessarily when the elements inside of it are inserted/modified.
http://emberjs.jsbin.com/liluxece/1/edit
Since it's async, I'd use render and hook it up to another view, that way you can use didInsertElement on a case by case situation for each item.
{{render 'template' context}}
Here's an example
http://emberjs.jsbin.com/liluxece/5/edit