degree symbol not being rendered by vue component - vue.js

I have a vue component which displays a gauge. I need to include units on the display and have this as one of the props of the component. However, because there are a number of gauges with different formatting it is all stored in a vuex store that reads its settings from an API. This all works nicely apart from when I want to bring special symbols (such as degree signs) across. The vuex object is storing the formatting object as:
{"min":0,"max":50,"dp":1,"units":"°C"}
and I use it in my component as follows:
<svg-gauge v-else
v-bind:g-value="device.value"
v-bind:g-min="device.format.min"
v-bind:g-max="device.format.max"
v-bind:g-decplace="device.format.dp"
v-bind:g-units="device.format.units"
>
The problem is that this simply displays °C rather than a degree symbol. If I hard code the last line as
g-units="°C"
It all works as expected. I suspect it is that I am having to use v-bind to pass the property and this is messing things up. Is there a way to ensure that v-bind is treating my characters as I would like?
EDIT: Here is the svg-gauge component template where the units are actually rendered.
<template>
<b-row>
<b-card no-body class="bg-dark text-light border-0" align="center">
<b-card-body class="m-0 pt-0 pb-0">
<h5><slot name="title">Title</slot></h5>
<div class="row mini-gauge pt-3" align="center">
<vue-svg-gauge
class="mini-gauge"
:start-angle="-90"
:end-angle="90"
:value="gValue"
:separator-step="0"
:min="gMin"
:max="gMax"
base-color="#595959"
:gauge-color="[{ offset: 0, color: '#347AB0'}, { offset: 100, color: '#D10404'}]"
:scale-interval="5"
>
<div style="line-height: 11rem">{{gValue.toFixed(gDecplace)}} {{gUnits}}</div>
</vue-svg-gauge>
</div>
<div class="row mini-gauge">
<div class="col" align="left">{{gMin}}</div>
<div class="col" align="right">{{gMax}}</div>
</div>
</b-card-body>
</b-card>
</b-row>
</template>

Change this line to have a span with a v-html. Then in the v-html pass the gUnits prop
<div style="line-height: 11rem">
{{gValue.toFixed(gDecplace)}}
<span v-html="gUnits"></span>
</div>
You can find the reason by looking here.
Hope this helps!

Related

Vue Component that Can Override What Child Renders Dynamically

I am working on a component (InfoCard) that should be able to render any number of fields passed into it with a 'fields' prop, as an array of json objects with a name, value, and some styling options. For certain fields, I want to be able to override what component is used to render, but do it from the parent (Table) rather than inside the InfoCard component, as it should be generic. My first thought was to use a <component :is='field.component'></component>, where it will render as plaintext if field.component is not defined, but to my understand it will be difficult to pass in any potential children necessary for the <component/>. My second thought is to use named slots from within the parent, but I don't think this is possible either in a good way. I'll show my current code.
In my example, I want to be able to detect if the field being rendered is 'status', and if it is, use a different rendering mechanism than displayValue(attribute), without hardcoding it inside InfoCard; I want the parent to be able to override this rendering conditionally. Is there a way to do this in Vue? Thanks
From Table, where data.records is an array of JSON objects:
<info-card
v-for="(record,index) in data.records"
:key="index"
:fields="record"
>
<div v-for="key in Object.keys(record)" :key="key">
<template v-if="field.name=='status'" v-slot:[`${field.name}_value`]>
<p> Field is status !</p>
</template>
</div>
</info-card>
From InfoCard:
<template>
<el-col
:lg="3"
:md="3"
:sm="3"
:xs="3"
v-for="(attribute, index) in fields"
:key="index"
class="attribute"
>
<div
#click="$emit('fieldClicked', attribute)"
>
<el-row
:class="`mid-gray f6 clipped fw5-ns m-b-10 ${attribute.nameClasses}`"
:title="displayName(attribute)"
:style="attribute.nameStyle?attribute.nameStyle:''"
>
<slot
v-if="Object.keys($scopedSlots).includes(`${attribute.name}_name`)"
:name="$scopedSlots[`${attribute.name}_name`]"
>
</slot>
<div v-else>
{{ displayName(attribute) }}
</div>
</el-row>
<el-row
:class="`mid-gray f6 clipped fw5-ns m-b-10 ${attribute.valueClasses}`"
:title="displayValue(attribute)"
:style="attribute.valueStyle?attribute.valueStyle:''"
>
<slot
v-if="Object.keys($scopedSlots).includes(`${attribute.name}_value`)"
:name="$scopedSlots[`${attribute.name}_value`]"
>
</slot>
<div v-else>
{{ displayValue(attribute) }}
</div>
</el-row>
</div>
</el-col>
</template>

Vue refs undefined in modal

I do have a for loop and inside i want to have a modal for each image
<div class="col pb-3" v-for="(item, index ) in img" :key="index" style="width: 300px;height:auto;">
<img v-b-modal="'modal-'+index" :ref="'image'" #mouseover="mouseOver" #mouseleave="mouseOut" /><br>
<b-modal :id="'modal-'+index" title="BootstrapVue">
<p class="my-4">Hello from modal - {{index}}</p>
<img :ref="'imagex'" />
</b-modal>
<div class="progress pt-2 mt-1" style="width: 100px;margin: 0 auto;">
<div class="progress-bar " :style="[{'width':width[index]+'px'}]" role="progressbar"
aria-valuenow="0" ref="'progress'" aria-valuemin="0" aria-valuemax="100"></div>
</div>
</div>
In methods i am giving src value to each image that has :ref="'image'" and i can actually give a src to them its work fine but, i also want be able to give src value images in modal. Its called :ref="'imagex'" but its undefined.
this.$refs.image[i].src = event.target.result //works
this.$refs.imagex[i].src = event.target.result //undefined
What is the problem an is there anyway to solve it ?
When used on elements/components with v-for, the registered reference will be an Array containing DOM nodes or component instances.
An important note about the ref registration timing: because the refs themselves are created as a result of the render function, you cannot access them on the initial render - they don’t exist yet! $refs is also non-reactive, therefore you should not attempt to use it in templates for data-binding.
.
can use it in events and only at mounted function.
https://v2.vuejs.org/v2/api/#ref

vue-smart-route with vuetify

Am using the package vue-smart-route to create a search functionality in my app. For the styles am using vuetify. The problem is that when i use the vuetify components it doesn't works.
Using it with vuetify component:
<v-text-field v-model="search" v-smart-routes="routes" class="mr-4" :label="$t('search') + '...'" hide-details append-icon="mdi-magnify" />
That doesn't works.
Plain html
<input class="mr-4" type="text" v-model="search" v-smart-routes="routes">
This works however i want to add the vuetify styles.
How can i make the vuetify component work?
I'm 99% sure that vue-smart-route doesn't work with the Vuetify's <v-text-field/> because it probably expects an exact <input> HTML element, while <v-text-field/> creates a much more complex DOM structure. It's something like this and the <input> tag is deeply nested:
<div class="v-input v-input--is-label-active v-input--is-dirty theme--light v-text-field v-text-field--is-booted">
<div class="v-input__control">
<div class="v-input__slot">
<div class="v-text-field__slot">
<label for="input-1752" class="v-label v-label--active theme--light"
style="left: 0px; right: auto; position: absolute;">Label</label><input id="input-1752"
type="text" />
</div>
</div>
<div class="v-text-field__details">
<div class="v-messages theme--light">
<div class="v-messages__wrapper"></div>
</div>
</div>
</div>
</div>
So in this case I see 2 options: Create your own custom input similar to this one if you want to stick to these styles or branch vue-smart-route and make it work with this DOM structure. I hope this helps.

VueJS transition on computed values

I want to animate a block with posts that can be filtered.
Some filters trigger a computed method filteredPosts and they are assigned to a component liek that <block-article :posts="filteredPosts" />
In my <block-article> component I have something like that :
<template>
<div class="posts">
<div v-for="post in posts" :key="post.id"></div>
</div>
</template>
I want div .posts animate like a translation bottom/fade out on disappear and translation top/ fade in on appear.
I tried that :
<template>
<transition name="slide-fade">
<div class="posts">
<div v-for="post in posts" :key="post.id"></div>
</div>
</transition>
</template>
with corresponding css classes but it doesn't work.
I tried that :
<template>
<div class="posts">
<transition-group name="slide-fade">
<div v-for="post in posts" :key="post.id"></div>
</transition-group>
</div>
</template>
but my class .posts is a grid and here I lost the grid behavior.
THE AIM is to animate the entire div .posts rather than each elements of the v-for.
Any idea ?
Thanks all,
I finally achieve this with :
<transition name="slide-fade">
<div :key="posts.length" class="posts"></div>
</transition>
Nothe the :key="posts.length"
The problem is when posts.length doesn't change but it works in a lots of case. I will search how to fix this exception.
If you animate entire div, you should use transition, but in this case all inner elements not animated. If you want to animate all inner elements. You should use transition-group
In your case I think, need use all this method with build-in tag attribute.
Becouse in dock you can read
https://v2.vuejs.org/v2/guide/transitions.html
Unlike transition, it renders an actual element: a span by default. You can change the element that’s rendered with the tag attribute.
So you can write like this(its not full code, you must add name, key and other attrs)
<transition>
<transition-group tag="div" class="posts">
<div v-for="post in posts"></div>
</transition-group>
</transition>

preventing Vue from aggresively reusing dom-elements

Condider the following snippet:
<template v-if="tryIsMobile" >
<div class='device device-mobile-portrait' :class="deviceClass">
<div class="device-scroller-container">
<div class='device-scroller'>
<img id='tryit-img-mobile' :src="srcUrlMobile" v-on:load="onImgLoad" v-on:error="onImgError"/>
</div>
</div>
</div>
</template>
<template v-else>
<div class='device device-tablet-landscape' :class="deviceClass" >
<div class="device-scroller-container">
<div class='device-scroller'>
<img id='tryit-img-tablet' :src="srcUrlTablet" v-on:load="onImgLoad" v-on:error="onImgError"/>
</div>
</div>
</div>
</template>
This code conditionally renders one of the two images. Some user action results in the actual shown image to be toggled.
What I'm seeing is the following: When toggling from say, tryit-img-mobile to tryit-img-tablet, the image loaded as part of tryit-img-mobile will get displayed with different dimensions instantly. However, during the time the image loads it's new source :src="srcUrlTablet", the image with src :src="srcUrlMobile" still displays.
This is probably due to Vue using the same img-tag for both the templates. How can I prevent Vue from doing this, and instead use seperate img-tags?
In cases such as this, Vue uses a special key attribute that tells it not to reuse the same element. Give each element this attribute with a unique value, and Vue will no longer reuse the same element:
<div v-if="tryIsMobile"
class="device device-mobile-portrait"
:class="deviceClass"
key="mobile"
>
...
</div>
<div v-else
class="device device-tablet-landscape"
:class="deviceClass"
key="tablet"
>
...
</div>