Vue Components Losing Image When Rerendered - vue.js

I have a sidebar component located in a view-router view component that renders several child component buttons. It renders the child components using v-for loops.
<div v-for="type in this.facebookLightButtons" :key="type">
<SidebarButton :type="type"/>
</div>
<div v-for="type in this.facebookButtons" :key="type">
<SidebarButton :type="type"/>
</div>
facebookLightButtons is simply an array of strings containing the text of the button (which is also the name of the image source of the button). The result is the following:
The corresponding code in the SidebarButton components are:
<template>
<div>
<router-link :to="'/' + capitalizeFirstLetter(type)" class="sidebar-button">
<table>
<tr>
<td>
<img class="sidebar-button-icon" :src="getImage" />
</td>
<td>
<p>{{capitalizeFirstLetter(type)}}</p>
</td>
</tr>
</table>
</router-link>
</div>
</template>
and
computed: {
getImage() {
return require('#/assets/images/SidebarIcons/' + this.type + '.png');
}
}
When I change to a new view-router view and then return to the home page where this sidebar is located, the images are all missing:
I am trying to figure out how to keep the images there when returning to the home page.
Things I have tried:
Clearing and setting the image source in the SidebarButton components in the created, beforeMount, mounted, activated, deactivated, updated, and unmounted lifecycle hooks (non of which seemed to work).
Clearing the image, waiting for 2000 milliseconds using setTimeout() and then setting the image to the correct image source
Wrapping both the contents of the SidebarButton template as well as the v-for loop in the parent component with a <keep-alive> tag
Hard coding each SidebarButton component instead of using a v-for loop
It seems like something must be missing. What can I do to ensure that these images stored locally on my computer not only show when first displayed, but also appear when the user returns to the homepage after entering a separate view-router view?

Related

vue dom doesn't re-render

I use v-for to render table rows
<table>
<tr v-for="(item, i) in data">
<td>
<button v-if="...">....</button>
<el-tooltipplacement="right">
<button v-if="..." >...</button>
<div slot="content">
<button #click="...">...</button>
<button #click="...">...</button>
</div>
</el-tooltip>
</td>
</tr>
</table>
The tooltip is a component of element ui,
the tooltip will display after hovering the button.
The table data is gotten by ajax call,
and there are several pages,
it will get the data by ajax call while turning page,
the problem occurs after turning page.
Some tooltip will not display after hovering,
I assumed the problem is doms don't re-render,
I used $forceUpdate() after ajax call,
but it had no effect.
If you can please help me out.
element ui Tooltip component and exmaple: https://element.eleme.io/#/en-US/component/tooltip

vue.js Mount component to app root

I have a modal.vue component as follows:
<template>
<transition name="modal-transition">
<div class="modal-body" v-if="displayed">
<div class="modal-overlay" #click="displayed = false"></div>
<div class="modal-content">
<slot/>
</div>
</div>
</transition>
</template>
How do I mount this component to the applications root element rather than in place?
For crude inaccurate example:
<body>
<div id="app">
<div class="header"></div>
<div class="nav"></div>
<div class="stage">
<div class="sub-nav"></div>
<div class="content">
<modal :display.sync="display">MY MODAL</modal> <-- Don't mount here...
</div>
</div>
<-- Mount here instead...
</div>
</body>
The current issue is that my sites header and navigation is layered on top of my modal and it's darkened full screen overlay instead of layered behind the modal overlay.
Update for Vue 3
There is now a built in feature called teleport which allows mounting parts of your component template to any DOM element.
The example from the OP would look like something like this
<!-- MyModal.vue -->
<template>
<transition name="modal-transition">
<div class="modal-body" v-if="displayed">
<div class="modal-overlay" #click="displayed = false"></div>
<div class="modal-content">
<slot/>
</div>
</div>
</transition>
</template>
<!-- SomeDeeplyNestedComponent.vue -->
<template>
<teleport to="#app">
<!-- Can still receive props from parent -->
<MyModal :my-prop="foo">
<!-- slot content -->
</MyModal>
</teleport>
</template>
Vue 2
Move the elements own self to the element of applications root may be achieved in two ways, Using a portal as a preferred solution or using an append.
Using a Portal (Preferred Method)
PortalVue is a set of two components that allow you to render a
component's template (or a part of it) anywhere in the document - even
outside the part controlled by your Vue App!
https://portal-vue.linusb.org/
Using an Append (Not best practice)
If adding a portal library is too heavy, using an append is allowed but lightly discouraged officially in the VUE docs.
Typically this particular mount position will satisfy a z-index overlay for your own modal or dialog popup that you require to render over the top of the entire app. You can always substitute this.$root.$el in this example for a different element target using standard getElementBy or querySelector functions.
Here the element is being moved not destroyed and re-added, all reactive functionality will remain in tact.
<script>
export default {
name: 'modal',
...
mounted: function() {
this.$root.$el.append(this.$el);
},
destroyed: function() {
this.$el.parentNode.removeChild(this.$el);
}
}
</script>
On mounted the element is moved inside of where the top level VUE app instance is mounted.
On destroyed removes the placeholder DOM comment for the migrated component from the new parent to prevent orphaned duplication each time the component remounts it's self.
VUE officially states not to destroy an element outside of VUE so this is not to be confused with that statement, here the component has already been destroyed.
This DOM comment duplication will typically happen when for example switching views with vue-router as this mechanism mounts and dismounts all components in a router view each time vue-router view state changes.
This behaviour is a bug cause by vue-router, the object is destroyed properly by VUE render manager but an index reference remains by mistake, using a portal package resolves this issue.
Here is the result:

Aurelia router-view inside dialog not working on reopening dialog

As per example given in aurelia documentation I am opening dialog box with viewmodel (say prompt ). This prompt has view inside in which I am adding "router-view" tag.
My routes are already configured. So when first time I open dialog it opens correct views as configured in routes and everything works well. But when I close dialog and re-opens dialog, It's not showing first route view. If I click other route link and come back to first route it works.
I have observed it's not creating instance of view model of first route( when opened dialog second time).
How to fix this issue?
Prompt html
<template><div class="row">
<left-menu ></left-menu>
<router-view></router-view>
</div></template>
<template>
and left-menu.htm
<template>
<div class="list-group">
<template repeat.for="item of items">
<a class="list-group-item ${$parent.selectedNav === item.routeName ? 'active' : ''}" route-href="route.bind: item.routeName;" click.delegate="$parent.select(item)">${item.text}</a>
</template>
</div>
Using a router inside a modal window seems off to me. The router is used to manage pages, but a modal is used to manage content within a page.
I would suggest building a modal window component, you can use <slot> tags to inject content and set it's model bindings to any data within the current view model.
Here's an example of my component that I use for this.
<template>
<div show.bind="visibility" class="modal-window">
<div>
<button class="btn btn-danger btn-sm close-button" click.delegate="close()">close</button>
</div>
<slot></slot>
</div>
<div show.bind="visibility" class="modal-window-overlay" click.delegate="close()"></div>
</template>
-
import { bindable } from 'aurelia-framework';
export class ModalContent {
#bindable visibility: boolean;
close(){
this.visibility = false;
}
}
<modal-content id.bind="'add-variant-window'">
<h4>Modal Content</h4>
<div>you can bind this to things on the current viewmodel</div>
</modal-content>

Transition table with VueJS

I tried to continue with the example on the vuejs website. I tried to add images and a transition state when I sort data.
However it doesn't work. I have tried to add the following line to make it works but it doesn't:
<tbody name="table-row" is="transition-group">
Do you have some ideas for me?
https://codepen.io/wooza/pen/wezqXP
https://codepen.io/anon/pen/gRmxwJ
https://v2.vuejs.org/v2/guide/transitions.html#List-Transitions
Unlike <transition>, it renders an actual element: a <span> by
default. You can change the element that’s rendered with the tag
attribute.
<transition-group tag="tbody" name="table-row">
<tr v-for="entry in filteredData" :key="entry.name">
//...
</tr>
</transition-group>

Access custom widget from nested element in template file (DOJO)

I am having an interesting issue with dijit widgets and widgets inside widgets. I have a custom widget that I created that houses a dijit.dialog with a form. Here is a sample
<div dojoattachpoint="WorkinProgress">
<div dojoType="dijit.Dialog" id="formDialog" title="Agent Note" >
<table>
<tr>
<td>
<label for="desc">
Description:
</label>
</td>
<td>
<input dojoType="dijit.form.Textarea" style="width:400px" type="text" name="desc" id="desc">
</td>
</tr>
<tr>
<td align="center" colspan="2">
<button dojoType="dijit.form.Button" type="submit" dojoattachevent="onclick: createNote >
SAVE
</button>
<button dojoType="dijit.form.Button" type="button" onClick="dijit.byId('formDialog').hide();">
CLOSE
</button>
</td>
</tr>
</table>
As you can see the dialog is nested inside my WorkInProgress widget. The form in the dialog itself needs to be able to call a function inside the WorkInProgress widget to post the data of the form to a webservice. I have tried using dojoattachevent but have not gotten anywhere. How do I, from the my template file, get access to the parent of the widget the dojo button resides in. Any help would be greatly appreciated. Thanks!
If you're adding the child widget dynamically, try to use dijit._Widget.placeAt function to include the DOM nodes inside. Apparently, just adding the HTML string to the parent widget only adds the root DOM of the child widget (in your case, the second div). After doing that, you should be able to get the parent info from the dojoAttachPoint, etc.
Why not give the buttons, dialog and outermost widget dojoAttachPoint attributes and then in the postCreate function of your custom widget do something like:
dojo.connect(this.createNoteButton, "onclick", this, this._createNoteHandler);
Then create a function on your custom widget called '_createNoteHandler' and do what you need to do there.
The advantages of this approach are that you can use pub/sub rather than dojo.connect for multiple handlers for 1 event, you are in greater control over the interaction, you don't litter your markup with the names of functions that may change (better separation of concerns)...