Prevent component from being created when inserted as content within another component (angular 2) - angular2-components

When inserting a component as ng-content of another component, how can I prevent it from being initialized until an expression is true?
<ng-content *ngIf="expression"></ng-content>
I basically only want the component's ngOnInit method to run once it's actually visible.

you need to wrap ng-content in'div' element to use *ngIf.
<div *ngIf="expression">
<ng-content ></ng-content>
</div>

Related

When exactly are keys useful in Vue?

I still don't quite get the use of keys even after reading through the docs but I understand that it has to do with keeping each item unique. I've been solely adding a key when using a v-for and only to the outer most parent (not the children of the v-for. Are there any other situations when keys should be used?
<div v-for="(person,index) in people" :key="index>
<div class="name-label>
<img :src="person.img/>
<div> {{ person.name }} </div>
</div>
</div>
The key directive alongside v-for, is used to uniquely identify each parent element rendered on the v-for operation.
The same happens in Reactjs:
{elements.map((value, index) => {
return <li key={index}>{value}</li>
})}
The key directive in Vuejs is also used to force Vuejs to re-render an element that contains it every time :key receives a new value.
As #firmino-changani indicated, other uses of key is if you want Vue to force a re-render or replacement of an element or component. See key special attribute documentation:
It can also be used to force replacement of an element/component
instead of reusing it. This can be useful when you want to:
Properly trigger lifecycle hooks of a component
Trigger transitions For example:
<transition>
<span :key="text">{{ text }}</span>
</transition>
When text changes, the <span> will always be replaced instead of patched, so a transition will be triggered.
Another example, lets say you have a dialog popup to add new data and another component that needs updating once the new data was entered via the popup. This second component might have logic inside lifecycle hooks like onMounted etc. Using a :key you can force re-rending this component once the dialog popup is saved e.g.
<template>
<page>
<add-data-popup #save="myKey++" />
<show-data :key"myKey" />
</page>
</template>
From the vueJs docs around Keys:
The key special attribute is primarily used as a hint for Vue’s virtual DOM algorithm to identify VNodes when diffing the new list of nodes against the old list.
To simply put it, if a div is rendered via a For-loop the div will be identical to each other div. When a user clicks on one of these Div's and you instantiate a #click function, how will vueJs know what differentiates each of these divs? How will vue know how to reorder these div's in the correct order if you remove a middle div?
VueJs uses the key to know how to reorder the DOM when an action is taken on one of these objects in your For loop.
Here is an article fully explaining about vueJs keys, and more details on the VueJS reactivity engine which is good to know about: https://www.telerik.com/blogs/in-vue-when-do-i-actually-need-the-key-attribute-and-why

Vue: watching a v-model's 'value' in a component

I have this component which has a v-model directive:
<my-component v-model="someData.someProp"></my-component>
Now, I'd like to be able to watch this piece of data inside the component and make changes based on this model changing via outside influences. So here's what I tried and it's not working:
watch : {
value (newVal, oldVal) {
// ...
}
}
It seems like it should work, or something comparable should be out there, but I just can't find it right now.
EDIT:
The most common answers I found and provided center on using the watcher to watch the data as if it's inside the parent component - but I'd like to watch it inside the child component without concern of what's going on in the parent.
I'm able to work around this by not using v-model and using simple named properties such as :my-data="someData.someProp" at which point I can successfully watch a myData variable inside the child component. I can also use #input to set the data back in the parent component if it's changed from within. But v-model is shorter and if there's a way to use that instead of a workaround that would be preferable.
From what I understand you are trying to pass a prop to your child component from your parent one and watch it in your child.
Assuming you have something like this in your parent:
<div>
<my-component v-model="someData.someProp"></my-component>
</div>
I understand you are getting undefined for that prop when you watch it, which is normal because you should pass it like this:
<my-component :myValue="someData.someProp"></my-component>
Then you should have access to the prop trought this.myValue.
If someData.someProp changes on the parent component it will automatically reflect on the child one.
Again this is what I could understand from your explanation and the amount of code you provided.

Vue force all children components to render

I’m using Vue & Vuetify to create my app. With vuetify I’m using v-expansion-panels to create an accordion style display. Each v-expansion-panel itself contains a custom component.
I have noticed these components are not created until the expansion panel is clicked for the first time. After that, using keep-alive allows all reactive properties and methods of the child component to be active (this is my desired behavior).
How can I force the child components to be created when the parent is created? This, any method triggered in the created() lifecycle hook of a child component should fire when the parent is created.
This Codepen is an example of the current behavior. Note: be sure to look at the console when you click the panel.
If you think about it, it actually makes sense to lazy load content of expansion panels since it is useless work if the user never opens them anyway. So probably the thing you try to accomplish has some better approach, but if you still like it then my advice is to find a way of programatically opening / closing the panel (as seen here) and quickly open it and close it when rendering parent component. In this way, you will have your child component created and the UI will remain the same.
A Vuetify solution should be achievable by adding the eager prop to a v-expansion-panel-content element in the Expansion Panel. This should force any components or content contained within the v-expansion-panel-content element to render on mounted.
<v-expansion-panels v-model="panels">
<v-expansion-panel>
<v-expansion-panel-content eager>
<custom-component />
</v-expansion-panel-content>
</v-expansion-panel>
</v-expansion-panels>

unable to access child component $refs inside b-modal

I am attempting to access a child component's $refs inside a b-modal.
On page load, I can see with vue dev tools that "agent-edit" has not been created. If I put the component outside of b-modal, it does show and I can access it -- however I need this to load inside a modal. How can I access $refs.editAgent? Can I force this child component to load with the page?
<b-modal id="editModal" ref="editModal" title="Edit Agent" size="lg">
<agent-edit ref="editAgent"></agent-edit>
<div slot="modal-footer" class="w-100"></div>
</b-modal>
Refs are relative to the component they are created in (not the child components)
// use this
this.$refs.editAgent
// Not this
this.$refs.editModal.$refs.editAgent
Note that b-modal is lazy by default, meaning the content is not rendered (instantiated) in the document until the modal is shown.
Once the modal is finished opening, you should have access to the refs (they don't exist until they are rendered into the DOM)
Listen for the modal's shown event, and then access the refs once that event is emitted.
I guess, that there is no <agent-edit> inside <b-modal>, when you try to call the method.
When the modal is hidden, there is no need to render the child components. Try to first show the modal and then access its children (maybe even with a Vue.$nextTick to make sure everything is finished).
In your case, this.$refs.editModal.$refs.editAgent should work.
But pay attemption to the use of $refs and think about emitting events.

Vue component not updating after parent state change

I have Vue component which receives json data from props, after this render child components using v-for and pass this data as prop. Everything works fine until i try to remove one item from this list, when i remove it, it removes element incorrectly. In vue devtools i see that parent receives data correctly but not renders it properly. can anyone help me?
here is my code: https://gist.github.com/giokaxo/3d291b9b7b8ef97f81dc83799c430302
Use "key" attribute when rendering elements using v-for, for example:
<p v-for="(product, index) in order.products" :key="i">..</p>
The relevant documentation is here:
You can directly use v-for on a custom component, like any normal
element:
<my-component v-for="item in items" :key="item.id"></my-component>
In 2.2.0+, when using v-for with a component, a key is now required.
However, this won’t automatically
pass any data to the component, because components have isolated
scopes of their own. In order to pass the iterated data into the
component, we should also use props:
<my-component
v-for="(item, index) in items"
v-bind:item="item"
v-bind:index="index"
v-bind:key="item.id">
</my-component>
The reason for not automatically injecting item into the component is because that makes the component
tightly coupled to how v-for works. Being explicit about where its
data comes from makes the component reusable in other situations.
And here:
When Vue is updating a list of elements rendered with v-for, it by
default uses an “in-place patch” strategy. If the order of the data
items has changed, instead of moving the DOM elements to match the
order of the items, Vue will simply patch each element in-place and
make sure it reflects what should be rendered at that particular
index.
...
To give Vue a hint so that it can track each node’s identity, and thus
reuse and reorder existing elements, you need to provide a unique key
attribute for each item. An ideal value for key would be the unique id
of each item.