I am trying to toggle a font awesome icon based on a boolean value but it seems that the font-awesome icon remains on the screen after it is drawn:
https://jsfiddle.net/50wL7mdz/200312/
HTML:
<script src="https://unpkg.com/vue"></script>
<script defer src="https://use.fontawesome.com/releases/v5.0.8/js/all.js" integrity="sha384-SlE991lGASHoBfWbelyBPLsUlwY1GwNDJo3jSJO04KZ33K2bwfV9YBauFfnzvynJ" crossorigin="anonymous"></script>

<div id="app">
<input v-model="marked" type="checkbox"/>
<i v-if="marked" class="far fa-check-square"></i>
</div>
JS:
new Vue({
el: '#app',
data: {
marked: false
}
})
Am I doing something wrong or is there a bug in font-awesome or vue.js?
I ran into this issue recently when using Vue.js 2.5.x with FontAwesome 5.5.x — the icon classes were not being updated as expected.
After switching from the FontAwesome Web Fonts + CSS implementation to SVG + JS, the following code no longer worked:
<i :class="[sortByFirstNameAsc ? 'fa-chevron-up' : 'fa-chevron-down', 'fa']"></i>
What would happen is that FontAwesome JavaScript would fire and wrap the <i> tag and replace it with an SVG element, as in the following simplified example:
<span data-v-2614dbd6="">
<svg data-v-2614dbd6="" class="svg-inline--fa fa-caret-up" ...">
...
</svg>
<!-- <i data-v-2614dbd6="" class="fa fa-caret-up"></i> -->
</span>
Unfortunately, the active class was being toggled on the inner, hidden <i> tag and not the outer, visible SVG element.
The workaround that restored the dynamic active class toggling was to wrap the FontAwesome icons in a span and use the v-show directive, as illustrated in the following code snippet:
<span v-show="sortByFirstNameAsc"><i class="fa fa-caret-up"></i></span>
<span v-show="sortByFirstNameDesc"><i class="fa fa-caret-down"></i></span>
The FontAwesome documentation now recommends using their Vue component to avoid conflicts in the DOM:
Compatibility Heads Up!
If you are using Vue you need the vue-fontawesome package or Web Fonts with CSS.
The SVG core package is helpful and recommended in the following cases:
to subset a large number of icons into only the icons that you are using
as base libraries for larger integrations with tools like React, Angular, Vue, or Ember (in fact our own components use these packages)
as CommonJS/ES6 Modules bundling tool like Webpack, Rollup, or Parcel
as UMD-style loader library like RequireJS
directly on the server with CommonJS (see our Server Side Rendering docs
"i" tag comments out after fire turning to svg, use some wrap <span v-if="marked"><i class="far fa-check-square"></i></span>
This answer applies to using Font Awesome with SVG.
For some reason, you need to wrap the i tag twice. For example, instead of this:
<div v-if="condition">
<i class="fal fa-caret-left"></i>
</div>
<div v-else>
<i class="fas fa-caret-left"></i>
</div>
do this:
<template v-if="condition">
<div>
<i class="fal fa-caret-left"></i>
</div>
</template>
<template v-else>
<div>
<i class="fas fa-caret-left"></i>
</div>
</template>
Not entirely sure why you need to wrap it twice since I'd think you decouple the i tag enough by wrapping it once, but it worked for me this way so there's apparently something else going on.
Also, keep in mind that the inner div can't be replaced with template for obvious reasons (template tags do not get rendered).
The Font Awesome library you used doesn't know about Vue. It takes the <i> that you wrote and turns it into an <svg>, and at that point, it's been stolen from Vue. Vue no longer controls it. The answer that Dialog gave was a good one: wrap it with a <span>. But then you pointed out another scenario it doesn't work for.
To solve your scenario where wrapping with a <span> still doesn't work, use key="fa-sort-up". This will force Vue to re-render the wrapper, at which point Font Awesome will update the icon. Here's the updated jsFiddle for your example:
<span key="fa-sort-up" v-if="sort && descending"><i class="fas fa-sort-up"></i></span>
<span key="fa-sort-down" v-else-if="sort"><i class="fas fa-sort-down"></i></span>
<span key="fa-sort" v-else><i class="fas fa-sort"></i></span>
You can use anything you want for the key, as long as it's unique.
Toggle a checkbox in vue with FontAwesome
<style>
.fa-check-square::before {
color: green;
}
</style>
<script>
Vue.component('some',
{
data:
function() {
return{
isclick: false
}
},
methods: {
isClicked: function() {
this.isclick = !this.isclick;
}
},
template: '<div id="test" v-on:click="isClicked">' +
'<i v-if="isclick" class="fa fa-check-square" style="font-size: 40px;"></i>' +
'<i v-if="!isclick" class="far fa-square" style="font-size: 40px;"></i>' +
'</div>'
});
</script>
<div id="componentsDemo">
<some></some>
<some></some>
<some></some>
<some></some>
</div>
<script>
new Vue({ el: '#componentsDemo' });
</script>
I fixed this by creating a template for each icon, then loading either template conditionally based on a boolean.
Here's my main template:
<div v-if="minimised">
<maximise-icon>
</maximise-icon>
</div>
<div v-if="!minimised">
<minimise-icon>
</minimise-icon>
</div>
Then just create the icons like so:
FontAwesomeConfig = { autoReplaceSvg: 'nest' }//important addition!
Vue.component('minimise-icon', {
template:
`
<i class="fas fa-minus "></i>
`
})
Vue.component('maximise-icon', {
template:
`
<i class="fas fa-plus "></i>
`
})
If there's a more elegant way I'm all ears!
Related
I'm trying to implement this - https://github.com/nicolasbeauvais/vue-social-sharing in my nuxt (+vuetify) application.
I've created a file - vue-social-sharing.js in plugins folder:
import Vue from "vue";
import VueSocialSharing from "vue-social-sharing";
Vue.use(VueSocialSharing);
included them in nuxt.config.js
plugins: [
"#/plugins/vuetify",
"#/plugins/vue-social-sharing.js"
],
I'm trying to beautify the buttons with Vueity (this works fine - https://jsfiddle.net/menteora/evydLj65/1/)
<social-sharing url="https://vuejs.org/"
title="The Progressive JavaScript Framework"
description="Intuitive, Fast and Composable MVVM for building interactive interfaces."
quote="Vue is a progressive framework for building user interfaces."
hashtags="vuejs,javascript,framework"
twitter-user="vuejs"
inline-template>
<div>
<v-btn><network network="email">
<i class="fa fa-envelope"></i> Email
</network></v-btn>
<v-btn><network network="facebook">
<i class="fa fa-facebook"></i> Facebook
</network></v-btn>
</div>
</social-sharing>
But I'm running into the below errors:
[Vue warn]: The client-side rendered virtual DOM tree is not matching
server-rendered content. This is likely caused by incorrect HTML
markup, for example nesting block-level elements inside <p>, or
missing <tbody>. Bailing hydration and performing full client-side
render.
[Vue warn]: Unknown custom element: <v-btn> - did you register
the component correctly? For recursive components, make sure to
provide the "name" option.
I think it is an issue with configuration, v-btn is not available with social-sharing is being rendered, please suggest.
Thanks
I haven't installed those, but I can almost bet that the fact you are wrapping network components with a button is a problem.. Because the markup that it generates most likely looks like this:
<ul class="networks">
<button>
<li></li>
</button>
</ul>
Have you tried to replace this markup with:
<social-sharing>
<div>
<network network="facebook">
<v-btn><i class="fa fa-fw fa-facebook"></i> Facebook</v-btn>
</network>
<network network="facebook">
<v-btn><i class="fa fa-fw fa-twitter"></i> Twitter</v-btn>
</network>
</div>
</social-sharing>
So the question I have right now is if I have a parent component that is JSX but I have the need to include an html tag such as <i class="fa fa-window-maximize" aria-hidden="true"></i> which I believe is not supported by react JSX(correct me if I am wrong) could I mix the jsx and non jsx components some how? I personally prefer JSX and haven't done any work without it really so I wouldn't want to stray away from this unless I have to.
<Parent>
<Child>
Mix non jsx code in here??
</Child>
</Parent>
You can mix JSX and non-JSX (HTML!) how you want! For example:
<div>
<MyComponent>
<input type="button>
</MyComponent>
</div>
If you want to style an element you must use className instead of class. So your i tag must look like this
<i className="fa fa-window-maximize" aria-hidden="true"></i>
Have in mind that these "non-JSX" HTML elements are actually JSX elements too, have a look at JSX In Depth
<div className="sidebar" />
Compiles to:
React.createElement(
'div',
{className: 'sidebar'},
null
)
To include classes in these components you need to use className instead of class, like this:
<i className="fa fa-window-maximize" aria-hidden="true" />
And yes, you can also mix them with your custom React components:
<MyComponent>
<i className="fa fa-window-maximize" aria-hidden="true" />
</MyComponent>
I am developing an angular2 application using PrimeNG. I have a tree view and in certain scenarios I need to add more than one icon to specific tree node. Any help on this is appreciated.
Example:
Icon TreeNode1
Icon1 Icon2 TreeNode2
I want the icons to be clickable so that I can perform actions like showing tooltips or show popup dialog etc...
Open on suggestions regarding any other technology that can be used to achieve above funtionality
I was looking for something related and found your question right now.
To add multiple icons to a branch (or leaf) of the tree you need to make your own templates for each type of TreeNode.`
<ng-template let-node pTemplate="group">
<i class="fa fa-bolt" (click)="doSomething('do lighting stuff')></i>
<span>{{node.label}}</span>
</ng-template>
<ng-template let-node pTemplate="node">
<i class="fa fa-signal" (click)="doSomething('do signal stuff')></i> <i class="fa fa-info" (click)="doSomething('do info stuff')></i>
<span>{{node.label}}</span>
</ng-template>
`
Check here the TreeNode interface https://github.com/primefaces/primeng/blob/master/src/app/components/common/treenode.ts
I had this in Vue 1.0:
<div v-for="menuItem in menu" class='vnav-item-wrapper'>
<i v-show="menuItem.icon" class="vnav-icon fa fa-{{menuItem.icon}}"></i>
</div>
But now the {{}} construct is obsolete in Vue 2.0.
I just read through the Class and Style guide and it simply doesn't seem to be possible for me to bind a class to "fa-" + menuItem.icon. I can't bind to an expression.
The closest I got was calculated data properties but then again, I'm in the middle of a v-for, I can't create "calculated variables".
How do I solve that?
I think this other question shows you how. It was definitely not obvious to me that once you apply v-bind: (or the : short-hand) to an attribute it's all interpreted as though it were JavaScript. So I think what you want is:
<div v-for="menuItem in menu" class='vnav-item-wrapper'>
<i v-show="menuItem.icon" :class="'vnav-icon fa fa-' + menuItem.icon"></i>
</div>
I have a template like so:
<template name="Menus">
<button class="btn_create new_menu_toggle"><i class="fa fa-plus"></i> Create a new menu</button>
<button class="btn_cancel new_menu_toggle"><i class="fa fa-times"></i> Cancel</button>
{{> NewMenu}}
</template>
What I'm trying to accomplish is that initially only the btn_create is shown. If .btn_create is pressed, the {{> NewMenu}} is rendered and the .btn_create is replaced with btn_deny. Vice versa for the behaviour of btn_deny.
How would I go about doing this in Meteor I know I could probably accomplish this by adding / changing classes in vanilla Javascript, but I'd like to know if there's an easier method using Meteor / Blaze.
A simple pattern is to use a session variable to track the state
html:
<template name="Menus">
{{#if createMode}}
<button class="btn_create new_menu_toggle"><i class="fa fa-plus"></i> Create a new menu</button>
{{> NewMenu}}
{{else}}
<button class="btn_cancel new_menu_toggle"><i class="fa fa-times"></i> Cancel</button>
{{/if}}
</template>
In your javascript you need to set up some event handlers to toggle the state:
Template.Menus.events({
'click .btn_create'(ev){
session.set('createMode',true);
},
'click .btn_cancel'(ev){
session.set('createMode',false);
}
});
When the template is rendered you need to initialize the session variable:
Template.Menus.onRendered(function(){
session.set('createMode',true);
});
Finally you need the helper your template can use for the spacebars conditional expression:
Template.Menus.helpers({
createMode(){
return session.get('createMode');
}
});