Meteor show / hide element on button click - javascript

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');
}
});

Related

Vue.js can't toggle a font-awesome icon

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!

Default/Overridable content for Aurelia template (template parts)

I'm trying to dip my toes into the Aurelia framework. I'm trying to create a reusable pager component.
Is there a way to provide some default content, but allow it to be overridden if the user of the component so desires?
For instance, my pager.html looks like this:
<template>
<div class="pager-container">
<content select="pager-beginning"></content>
</div>
</template>
my pager-beginning.html looks like this:
<template>
<content>
<button type="button">|<</button>
</content>
</template>
I was thinking I should be able to do something like this:
<template>
<require from="components/pager/pager"></require>
<pager></pager>
</template>
and have the markup produced look like this:
<div class="pager-container">
<button type="button">|<</button>
</div>
or alternatively I should be able to do something like this:
<template>
<require from="components/pager/pager"></require>
<pager>
<pager-beginning><button type="button"><i class="glyphicon glyphicon-step-backward"></i></button></pager-beginning>
</pager>
</template>
and have the markup produced look like this:
<div class="pager-container">
<button type="button"><i class="glyphicon glyphicon-step-backward"></i></button>
</div>
The idea being that I could provide all of the functionality of a pager, all of the logic specific to a pager in my pager.js file and a default html rendering, but then allow the user of the component to override pieces of the html if they so desired.
What currently seems to be happening is that the markup inside of the pager-beginning.html <content></content> tag is always getting replaced. So I get markup rendered that looks like this:
<div class="pager-container"></div>
I can't figure out how to provide it with that 'default' content functionality.
Use the "template parts" feature. More info here (search for "template parts").
pager.html
<template>
<div class="pager-container">
<button type="button" click.delegate="gotoBeginning()">
<template replaceable part="goto-beginning-button-content">|<</template>
</button>
</div>
</template>
app.html
<template>
<require from="./components/pager/pager"></require>
<pager>
<template replace-part="goto-beginning-button-content">
<i class="glyphicon glyphicon-step-backward"></i>
</template>
</pager>
</template>

Using Meteor sessions to toggle templates

I'm brand new to JavaScript and Meteor, and am a little stuck in trying to create edit functionality for a list item to be able to modify properties of the list item (from a Mongo document). I can make it work if I make editMode a boolean property of the item itself, but I'd like it to be local and temporary rather than stored with the document itself.
Looking at the code below, you'll notice I'm still green and don't yet completely understand what I'm doing, but you should be able to understand what I'm trying to do :)
The code is somewhat there, but am trying to hook up changes in the session, based on toggling an edit button, to get the edit mode template to render. Here's what I have (stripped down to the relevant stuff):
// Item template
<template name="item">
<div class="item">
<span>{{itemText}}</span>
<span class="glyphicon glyphicon-pencil item-edit" aria-hidden="true"></span>
</div>
<div class="mod-table">
{{#if this.editMode}}
{{> modTable}}
{{/if}}
</div>
</template>
// Associated .js file
Session.set(this.editMode, false);
Template.item.events({
'click .item-edit': function() {
if (this.editMode) {
Session.set(this.editMode, false);
} else {
Session.set(this.editMode, true);
}
}
});
Don't use Session variables because they are global thus considered bad practice, you should use a ReactiveVar tied to your item template instead.
item.html :
<template name="item">
<div class="item">
<span>{{itemText}}</span>
<span class="glyphicon glyphicon-pencil item-edit" aria-hidden="true"></span>
</div>
<div class="mod-table">
{{#if editMode}}
{{> modTable}}
{{/if}}
</div>
</template>
item.js :
Template.item.created=function(){
this.editMode=new ReactiveVar(false);
};
Template.item.helpers({
editMode:function(){
return Template.instance().editMode.get();
}
});
Template.item.events({
'click .item-edit': function(event,template) {
var editMode=template.editMode.get();
template.editMode.set(!editMode);
}
});
Don't forget to meteor add reactive-var to your project !

Meteor combined with Session.set and jQuery in one call

I'm trying to get something very basic to work in Meteor, where when the user clicks a button a Session.set() is called and then a div is shown. The problem is that if I don't call the Session.set() the behaviour is as expected where the div is shown upon clicking the button. But once I include the Session.set() i need to click twice before the message is actually show. To make sure the behaviour can be reproduced make sure the page is a clean load and not a Meteor refresh!
The code for the HTML is:
<head>
<title>test</title>
</head>
<body>
{{> home}}
</body>
<template name="home">
<div>
<input type="button" id="addButton" value="add">
</div>
{{> popup}}
</template>
<template name="popup">
{{#with messageHelper}}
<div id="messageDiv" style="display: none">
message is {{message}}
</div>
{{/with}}
</template>
And here is the Javascript that makes it tick.
Template.home.events({
'click #addButton': function(){
// **** if this line is commented out, it will work
Session.set("messageHelper", {message: 'HERE'});
// ****
$('#messageDiv').show();
}
});
Template.popup.helpers({
messageHelper: function(){
return Session.get("messageHelper");
}
});
I think the changed session variable rerenders your template in its original state which sets style="display: none". The problem is discussed here. The solution there is to reorganise your template so the reactivity is not in the template where display is being toggled.
An easier solution might be just to use handlebars #if helper to display your template whenever there is a message:
<template name="popup">
{{#with messageHelper}}
{{#if message}}
<div id="messageDiv">
message is {{message}}
</div>
{{/if}}
{{/with}}
</template>
No need for .show() in the event handler then.

Ember.js hiding a divider with buttons, but maintaining didInsertElement functionality

I'm trying to show and hide some buttons in Ember.js as follows.
{{#view App.myView contentBinding="App.myObject"}
<div id="my_widget" class="well span3">
{{#if content.isConditionTrue}}
<button id="stop_button" class="btn btn-primary"> Stop </button>
<button id="start_button" class="btn btn-primary" > Start </button>
<button id="record_button" class="btn btn-primary">Record </button>
</div>
{{else}}
<div id="widget_warning_box" class="well">
<p> No cameras are open, open a camera in Add/Remove Cameras tab. </p>
</div>
{{/if}}
</div>
{{/view}}
and the View looks like:
App.myView = Ember.View.Extend({
didInsertElement: function() {
$record = $("#record_button")
$start = $("#start_button")
$stop = $("#stop_button")
$record.click(function(){
console.log("record clicked")
});
$start.click(function(){
console.log("start clicked")
});
});
And the myObject controller is
App.myObject = Ember.Object.create({
isConditionTrue : false;
});
This sort of works (the buttons are replaced by the text if myObject.isConditionTrue is false, and appear when isCondionTrue is true) but when the buttons are displayed, they have no functionality on click. I guess its because they are not in the DOM when didInsertElement is called. So, is there some way to fix this? Should I do child/parent View setup? Any pointers appreciated.
Well I'm definitely not the guy who can tell what's the right way to do this, because I'm just learning ember too, but here is my working approach on fiddle
http://jsfiddle.net/drulia/zkPQt/
Basically every time you switching myObject.isConditionTrue to false, ember removes your buttons from DOM and that's why your jQuery selectors stops working. I attached ember js {{action}} helper and placed functions responding to the events in myObject, now buttons are working and showing action result on the screen.

Categories

Resources