Conditional lazy-loading with Nuxt.js - vuejs2

Nuxt.js comes with support for component lazy-loading, by prefixing the component name with lazy-. But is there anyway to conditionally lazy-load, short of duplicating the code with v-if/else?
E.g. something like this?
<list :lazy="shouldBeLazyLoaded">
...some complex markup
</list>
Currently I do it like this:
<lazy-list v-if="shouldBeLazyLoaded">
...some complex markup
</lazy-list>
<list v-else>
...some complex markup
<list>

Maybe your could use Vue's dynamic components for this:
<component v-bind:is="shouldBeLazyLoaded ? 'lazy-list' : 'list'">
...some complex markup
</component>

Related

Adding icons to a title attribute of Ant Designs Blazor (DescriptionsItem)

Ant Design Blazor has a descriptions component for generating key/value pair elements. I'd like to add an icon and tooltip as the key/title. The problem is, that the title is set as an attribute like this:
<DescriptionsItem Title="Billing Mode">Prepaid</DescriptionsItem>
I have tried to set Title as child element, cause this would allow me to add any other components inside it:
<DescriptionsItem>
<Title>My title</Title>
My Text
</DescriptionsItem>
But this doesn't work, <Title> is a standalone component for html titles like h1, h2, ... which means both got rendered in the value section without any title:
I couldn't find a way to render other components in the title attribute, it just works in the body/value like this:
<DescriptionsItem Title="Created">
<Tooltip Placement="#PlacementType.Bottom" Title="creationIconTitle">
<Icon Type="plus-circle" Theme="outline" />
</Tooltip>
#Community.Created.ToLocalTime().ToString("G")
</DescriptionsItem>
But I'd like to have the icon in the title. Is there a way to realize this?
You are in luck! The source says the developers thought of this and added a TitleTemplate RenderFragment. Thus you can do something like:
<Descriptions Title="Descriptions">
<DescriptionsItem>
<TitleTemplate>
<AntDesign.Tooltip Placement="#PlacementType.Bottom">
<AntDesign.Icon Type="plus-circle" Theme="outline" />
</AntDesign.Tooltip>
31.10.2008 17:04:32
</TitleTemplate>
</DescriptionsItem>
</Descriptions>
This TitleTemplate is used over the Title, if defined source

Component mounted twice

I have a simple component which is rendered by a click function, but it gets rendered twice, this is my code.
<SeeCompany
:is="create"
v-bind:companyId="companySelected"
#closeChild="closeModule"
/>
when i clicked in the button i change the create value to 'SeeCompany' so it gets mounted, but it repeats the same component text twice on the screen.
<b-button block
#click="create = 'SeeCompany'"
class="m-sides"
variant="outline-primary">
Ver
</b-button>
here is the image:
EDIT: Here is the code in the mounted
export default class SeeCompany extends Vue {
#Prop({ default: 0 }) private companyId !: number;
constructor() {
super();
}
private mounted() {
console.log(this.companyId); --> This is consoling two ceros (0) and the passed value for instance = 1;
}
}
There are two main uses for is.
Working around limitations in in-DOM templates.
Dynamic components.
For more information see https://v2.vuejs.org/v2/api/#is.
We can ignore the former case as it isn't relevant here.
Typically the second case looks a bit like this:
<component :is="childName" />
Here childName is a property of the component and determines the name of the child component to use. In your example you called it create.
The actual tag name used in the template doesn't really matter. It is common to use the dummy tag <component> for this purpose to avoid misleading future maintainers who may not immediately notice the :is. Whenever you see <component> you know you're in a dynamic component scenario.
When we talk about dynamic components it is important to appreciate exactly what we mean by 'dynamic' in this context. We are specifically talking about which component to use. We are not talking about determining whether or not to create the component in the first place.
In the code in the question the value of create is initially set to an empty string, ''. This is then passed to :is. If you inspect the DOM you'll find that this creates a comment node. While this does make some sense I am unclear if this is officially supported. I've not seen this behaviour documented anywhere and I suspect you may be getting lucky by falling down an internal code path that's intended for other things. It is not something I would be confident relying on in future versions of Vue.
The specific code of interest is:
<SeeCompany
v-bind:is="create"
v-bind:companyId="1"
/>
<SeeOther
v-bind:is="create"
v-bind:companyId="1"
/>
So if you inspect the DOM when create is '' you should find two comment nodes.
When create gets set to SeeCompany this is equivalent to:
<SeeCompany
is="SeeCompany"
v-bind:companyId="1"
/>
<SeeOther
is="SeeCompany"
v-bind:companyId="1"
/>
In turn this is equivalent to:
<SeeCompany
v-bind:companyId="1"
/>
<SeeCompany
v-bind:companyId="1"
/>
The result is the creation of two SeeCompany components. The original SeeOther tag is irrelevant here. This is why, as noted earlier, the convention exists to use a <component> tag to avoid being misleading.
Of course this isn't what you actually wanted the code to do. I'm unclear what the target behaviour is so I'm going to cover a few variations.
If you just want to show the components conditionally you'd use v-if instead:
<SeeCompany
v-if="create"
v-bind:companyId="1"
/>
<SeeOther
v-if="create"
v-bind:companyId="1"
/>
Usually you'd want create to be a proper boolean, false or true. So set the initial value to false with #click="create = true".
Of course this would show both SeeCompany and SeeOther at the same time. That may not be what you want either. Perhaps you only want to show one at once. For that you might do something like this:
<SeeCompany
v-if="create === 'SeeCompany'"
v-bind:companyId="1"
/>
<SeeOther
v-if="create === 'SeeOther'"
v-bind:companyId="1"
/>
Here the initial value of create should be a falsey value of some kind, possibly '', with #click="create = 'SeeCompany'" and #click="create = 'SeeOther'" on appropriate buttons.
If the props for the components are all the same, and especially if there are more than two components involved, you could try to simplify this using is:
<component
:is="create"
v-if="create"
v-bind:companyId="1"
/>
This is shorter but arguably not as clear.

Get which position a child has in its parent in Vue JS

Given I have:
<Parent>
<Child />
<Child /> <!-- this is Child 2 -->
<Child />
<Child /> <!-- this is Child 4 -->
</Parent>
how do I know within the Child component which position it has in its parent. I obviously could pass it down like this:
<Child :position="2" /> but that creates unnecessary markup when there is a computed way to do this.
I want to access within Child its -nth position in the parent.
It depends on why do you need this information: if you need some specific styles for nth component, it's better to use :nth-child or :nth-of-type CSS selectors.
Passing the position number within a prop is also a nice way to do it.
You also may refer to $parent and iterate over its children to find the current element, like [...this.$parent.$el.children].findIndex(child => child === this.$el) (make sure both parent and child have already been mounted), but this code is quite fragile so use it at your own risk.

Vue Dynamic Component Ordering?

Thanks in advance for the help.
I'm using the Vue's dynamic component tag in an application.
https://v2.vuejs.org/v2/guide/components.html#Dynamic-Components
The component list is provided via a computed property which I simply iterate over creating <component /> elements.
<component v-for="component in myComponents" :key="component" is="component />
The issue i'm running into is the components are loaded asynchronously and some components can take longer at times than others to load. Due to this sometimes the components load out of the desired order.
I was curious if anyone had a suggestion on how I might be able to force the components to display in a desired order?
You can try to use an ordered computed property, for exemple if you want to order your components by name.
In the template :
<component v-for="component in orderedComponents" :key="component" is="component />
In the script
computed: {
orderedComponents: function () {
return _.orderBy(this.myComponents, 'name')
}
}
This exemple is using Lodash but of course you can order it the way you want.
You can check the documentation : https://v2.vuejs.org/v2/guide/migration.html#Replacing-the-orderBy-Filter
Yes, you can enforce the order by setting :key to a number.
First, modify your for loop to use indexes:
v-for="(index, value) in myComponents"
Then set key to the index
:key="index"
This should keep the order in tact - if it doesn't, you could always create placeholders first then replace them in the runtime.

Keep list components alive in Vue

I have a list of components that I render using v-for. Not all the components are shown simultaneously. I page the the array of rendered components by using slice.
These components shouldn't be rerendered, as some of them have user inputted data and some of them do network related tasks.
I tried to use <keep-alive>. However, this renders only the first component.
<keep-alive>
<component :is="component.type" :key="component.id" v-for="component in components">
</keep-alive>
How do I keep a dynamic list of components alive?
<div v-for="comp in compList" :key="'container'+comp.keyId" >
<keep-alive>
<component :is="comp.type" :key="comp.keyId"></component>
</keep-alive>
</div>
this above works for me . Pushing elements to compList correctly creates new instances of their respective components. Moving an element's index within the array , maintaining key, does not call destroy/create and maintains state within each component
Tested the answer above in a fiddle and doesn't work. https://jsfiddle.net/z11fe07p/680/
<div v-for="component in myComponents" :key="component.id" >
<keep-alive>
<component :is="component.type"
:name="component.name">
</component>
</keep-alive>
</div>
Also i would avoid using vue reserved words such as components because there is a components key in the vue instance which tells what components the instance is using.
I read source code of Vue's <keep-alive>, and I created new Component which works very well with list.
package name is vue-keep-alive-global. Here is a link, https://www.npmjs.com/package/vue-keep-alive-global
How to use :
<KeepAliveGlobal>
<YourComponent
:key="uniqueKey"
/>
</KeepAliveGlobal>
With Array,
<template v-for="(item, index) of array">
<KeepAliveGlobal :key="`blah-blah-${index}`">
<YourComponent
:item="item"
:key="`your-component-${index}`"
/>
</KeepAliveGlobal>
</template>
KeepAliveGlobal will cache component by key.
There's note at Vue docs about your use case
Note, <keep-alive> is designed for the case where it has one direct
child component that is being toggled. It does not work if you have
v-for inside it. When there are multiple conditional children, as
above, ` requires that only one child is rendered at a
time.
https://v2.vuejs.org/v2/api/#keep-alive
Try v-once directive instead.
https://v2.vuejs.org/v2/api/#v-once