V-model not updating while removing a value from SemanticUI multiselect dropdown when allowAdditions is true - vue.js

V-model is not updating when we remove a value from Semantic UI multiselect search dropdown.
If we enter any value that is not present in the dropdown and press enter/tab then the value is added to the array normally but when we remove this value it doesn't get deleted from the array. However, it works fine if we remove any of the values that were selected from the dropdown options.
Here is my code:
new Vue({
el: "#app",
data: {
lists:[]
},
})
$('.ui.dropdown')
.dropdown({
allowAdditions: true,
});
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
li {
margin: 8px 0;
}
h2 {
font-weight: bold;
margin-bottom: 15px;
}
del {
color: rgba(0, 0, 0, 0.3);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.css" />
<div id="app">
<h3>Selected values: {{lists}}</h3>
<select name="skills" multiple="" v-model="lists" class="ui search fluid dropdown">
<option value="">Skills</option>
<option value="Angular">Angular</option>
<option value="CSS">CSS</option>
<option value="HTML">HTML</option>
</select>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.js"></script>
Follow the steps to recreate the example:
Click on the dropdown to see the options
Select 'Angular', 'CSS' and 'HTML' one by one
After selecting, enter 'Abcd' in the dropdown and press tab or enter (Pillbox for this value will be created)
Now you will see 4 values in the array
Remove 'Angular' and 'CSS' by clicking on the Remove(x) icon - These will be removed from array too automatically
Now remove 'Abcd' - This wont get removed until we add or remove any values that are present in the dropdown.
Can someone help please?

I would recommend using the vue-semantics given by semantics itself because this seams like options and selected values are binded properly in backend, but meanwhile here is a work around to tighten this use case.
Just use the onRemove callback to remove the unselected element from the list. This will ensure that the element is removed successfully.
new Vue({
el: "#app",
data: {
lists:[],
options : ["Angular", "CSS", "HTML"]
},
mounted: function(){
$('.ui.dropdown').dropdown({
allowAdditions: true,
onRemove : (removedValue, removedText, $removedChoice) => {
this.verifySelected(removedValue);
}
});
},
methods : {
verifySelected : function(removedValue){
var index = this.lists.indexOf(removedValue);
if(index > -1){
this.lists.splice(index, 1);
console.log(this.lists);
}
}
}
})

Related

Vue Accordion with dropdown select filter. How to bind data

I am trying to get a local Vue project working where there is an E-Library Accordion with 3 types of media and a dropdown button to filter between the types of media. I am also trying to get a color coding system going and I attempted this through created a class and trying to bind it (not working). I am trying to filter the media with a method and then applying that method as an #click event to each dropdown button, but I know this is probably poor practice or just not the correct way to do this. Can anyone point me in the right direction as to how to get these two features working correctly? Much appreciated.
This is my page that I am routing to and creating the code on for the Vue project:
<template>
<label for="accordion">Choose type of media:</label>
<select name="accordion-types" id="types">
<option #click="filteredAccordion(index)" value="book">Book</option>
<option #click="filteredAccordion(index)" value="dvd">DVD</option>
<option #click="filteredAccordion(index)" value="stream">Streaming Video</option>
</select>
<div v-for="(accordion, index) in accordions" :key="index">
<button :class="{red: accordions.type === 'Book' }" #click="toggleOpen(index)">
{{ accordion.title }}
</button>
<div class="panel" :class="{open: accordion.isOpen}">
<p>{{ accordion.content }} </p>
<p>{{ accordion.type }}</p>
</div>
</div>
</template>
<script>
import axios from "axios";
export default {
data() {
return {
accordions: []
};
},
created: function () {
const appData = this;
axios.get("data.json").then(function (response) {
appData.accordions = appData.addIsOpen(response.data.accordions);
});
},
methods: {
addIsOpen: function(items) {
return items.map(function(item) {
item.isOpen = false;
return item;
});
},
toggleOpen: function(index) {
this.accordions[index].isOpen = !this.accordions[index].isOpen;
}
},
computed: {
filteredAccordion: function(items) {
return this.accordions.filter(accordions => !accordions.type.indexOf(this.type));
}
}
};
</script>
<style>
.accordion {
background-color: #eee;
color: #444;
cursor: pointer;
padding: 18px;
width: 100%;
text-align: left;
border: none;
outline: none;
transition: 0.4s;
}
.active, .accordion:hover {
background-color: #ccc;
}
.panel {
padding: 0 18px;
background-color: white;
display: none;
overflow: hidden;
}
.panel.open {
display: block;
}
.red {
color: red;
}
</style>

Detect radio input getting unchecked (by clicking another radio input) in Vue

I want to give my label a border when the radio input is selected but I can only detect when the radio input is being activated. I know it's possible to do this in CSS with some tricks but I want to do it through Javascript.
<fieldset>
<label>
<input type="radio" name="test" #change="someMethod">
test
</label>
<label>
<input type="radio" name="test" #change="someMethod">
test
</label>
</fieldset>
methods: {
someMethod () {
console.log('something')
}
}
I was hoping to call the someMethod method when the input state would change between checked and unchcked, but this change is only called when the input is clicked (checked).
https://jsfiddle.net/alucardu/qhbpg2yj/1/
Use $refs to check all the radio inputs and use an array to store the previous state, then we can manage to do it:
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!',
previous_refs:[],
},
methods:{
someMethod(){
for(const key in this.$refs){
if(this.previous_refs[key]!==undefined &&
this.previous_refs[key].checked===true &&
this.$refs[key].checked===false){
console.log(this.$refs[key].id+' un-checked, do something');
}
if(this.$refs[key].checked===true){
console.log(this.$refs[key].id+' checked, do something');
}
if(this.previous_refs[key]===undefined){
this.previous_refs[key]={checked:this.$refs[key].checked};
}
else{
this.previous_refs[key].checked=this.$refs[key].checked;
}
}
},
}
})
Please check https://jsfiddle.net/sLw4e0p8/92/ to see details.
Managed to do it through CSS: https://jsfiddle.net/alucardu/qhbpg2yj/7/
Trick was adding a before to the checked input:
label {
display: flex;
position: relative;
input:checked {
&::before {
content: '';
position: absolute;
width: 100%;
height: 100%;
border: 1px solid blue;
}
}
}

Vue onclick display specific item

I have a question about Vue.
I want to add a class to a specific item:
<p v-on:click="display = !display">Rediger joke</p>
Display is False before and it change it to true.
And it works. But my problem is, that this onclick is inside an v-for loop, and i only want to put "display" on one "update-site" and not all of them. Can i do this or do I have to try a different setup?
Thanks a lot
I have this idea that might help you. The idea is you extend post object with for example visible property and when you click event triggered you change this property and add .display class. Please check this jsfiddle
template
<div id="app">
<article v-for="post in filteredPosts" :key="post.id">
{{post.name}}
<button #click="display(post)">show</button>
<div class="post-content" :class="{display: post.visible}">this is the part I want to display onclick</div>
<hr />
</article>
</div>
css
.post-content {
display: none;
}
.post-content.display {
display: block;
}
code
new Vue({
el: '#app',
data() {
return {
posts: []
};
},
created() {
//when you load posts. add visible property.
setTimeout(() => {
//posts from server
var postsFromServer = [{
id: 1,
name: 'Post One'
},
{
id: 2,
name: 'Post Two'
}
];
//add visible proprty.
this.posts = postsFromServer.map(post => {
return {
...post,
visible: false
};
});
}, 1000);
},
computed: {
filteredPosts() {
//do your filters
return this.posts;
}
},
methods: {
display(post) {
this.$set(post, 'visible', !post.visible);
}
}
});
I have an article, and i get the data from Firebase.
<article v-for="post in filteredPosts" :key="post.id">
{{post.name}}
<p v-on:click="display = !display"></p>
<div>this is the part I want to display onclick</div
</article>
updateInputs has display:none, but onclick I want it to be display as block:
.updateInputs.display {
display: block;
position: absolute;
top:0;
left:0;
bottom: 0;
background-color: white;
box-shadow: 4px 4px 10px black;
width: 100%;
height: auto;
overflow: hidden;
overflow-y: scroll;
padding-bottom: 10px;
}

How to add stripe elements in vue2 from?

In Laravel 5.8 / vuejs 2.6 / vuex / mysql app I need to add stripe elements from https://stripe.com/docs/stripe-js,
http://prntscr.com/phflkd
and for this in resources/views/index.blade.php I added line:
#include('footer')
<script src="{{ asset('js/jquery.min.js') }}"></script>
<script src="{{ asset('js/bootstrap.bundle.min.js') }}"></script>
<script src="{{ asset('js/waves.min.js') }}"></script>
<script src="{{ asset('js/jquery.slimscroll.min.js') }}"></script>
<script src="{{ asset('js/powerange.js') }}"></script>
<script src="{{ asset('js/appInit.js') }}"></script>
<script src="{{ asset('js/app.js') }}{{ "?dt=".time() }}"></script>
<script src="https://js.stripe.com/v3/"></script>
</html>
and in my vue form I init stripe in initStripe() method, which is called in mount event :
<template>
<div class="page-content col-md-offset-2">
<div class="sign-up container-fluid justify-content-center" style="max-width: 460px;">
<hr>
<hr>
<form action="/charge" method="post" id="payment-form">
<div class="form-row">
<label for="card-element">
Credit or debit card
</label>
<div id="card-element">
<!-- A Stripe Element will be inserted here. -->
</div>
<!-- Used to display form errors. -->
<div id="card-errors" role="alert"></div>
</div>
<button>Submit Payment</button>
</form>
<button type="button"
class="btn btn-outline-pink btn-round waves-effect waves-light cancel-btn mr-5" #click.prevent="cancelSelectedSubscription()">
<i :class="getHeaderIcon('cancel')"></i> Cancel
</button>
</div>
</div>
</template>
<script>
import {bus} from '../../../../app';
import appMixin from '../../../../appMixin';
import Vue from 'vue';
export default {
data: function () {
return {
is_page_loaded: false,
}
},
name: 'selectedSubscription',
created() {
if ( typeof this.currentLoggedUser.id != 'number' ) {
this.showPopupMessage("Access", 'Your session is expired !', 'error');
this.$store.commit('logout');
}
this.message = '';
}, // created) {
mounted() {
this.is_page_loaded = true
this.setAppTitle("Selected Subscription", 'Selected Subscription Details', bus);
this.initStripe();
}, // mounted() {
mixins: [appMixin],
methods: {
cancelSelectedSubscription() {
this.$router.push({path: '/personal-details'});
},
initStripe()
{
console.log("Stripe -1::")
// Create a Stripe client.
var stripe = Stripe('pk_test_NNNN');
console.log("Stripe -2::")
// Create an instance of Elements.
var elements = stripe.elements();
console.log("Stripe -3::")
// Custom styling can be passed to options when creating an Element.
// (Note that this demo uses a wider set of styles than the guide below.)
var style = {
base: {
color: '#32325d',
fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
fontSmoothing: 'antialiased',
fontSize: '16px',
'::placeholder': {
color: '#aab7c4'
}
},
invalid: {
color: '#fa755a',
iconColor: '#fa755a'
}
};
// Create an instance of the card Element.
var card = elements.create('card', {style: style});
console.log("Stripe -4::")
// Add an instance of the card Element into the `card-element` <div>.
card.mount('#card-element');
// Handle real-time validation errors from the card Element.
card.addEventListener('change', function (event) {
var displayError = document.getElementById('card-errors');
if (event.error) {
displayError.textContent = event.error.message;
} else {
displayError.textContent = '';
}
});
console.log("Stripe -5::")
// Handle form submission.
var form = document.getElementById('payment-form');
form.addEventListener('submit', function (event) {
event.preventDefault();
stripe.createToken(card).then(function (result) {
if (result.error) {
// Inform the user if there was an error.
var errorElement = document.getElementById('card-errors');
errorElement.textContent = result.error.message;
} else {
// Send the token to your server.
this.stripeTokenHandler(result.token);
}
});
});
}, // initStripe() {
// Submit the form with the token ID.
stripeTokenHandler(token) {
// Insert the token ID into the form so it gets submitted to the server
var form = document.getElementById('payment-form');
alert( "stripeTokenHandler form::"+var_dump(form) )
var hiddenInput = document.createElement('input');
hiddenInput.setAttribute('type', 'hidden');
hiddenInput.setAttribute('name', 'stripeToken');
hiddenInput.setAttribute('value', token.id);
form.appendChild(hiddenInput);
// Submit the form
form.submit();
},
}, // methods: {
computed: {
currentLoggedUser() {
return this.$store.getters.currentLoggedUser;
},
...
} //computed: {
}
</script>
<style lang="css">
/**
* The CSS shown here will not be introduced in the Quickstart guide, but shows
* how you can use CSS to style your Element's container.
*/
.StripeElement {
box-sizing: border-box;
height: 40px;
padding: 10px 12px;
border: 1px solid transparent;
border-radius: 4px;
background-color: white;
box-shadow: 0 1px 3px 0 #e6ebf1;
-webkit-transition: box-shadow 150ms ease;
transition: box-shadow 150ms ease;
}
.StripeElement--focus {
box-shadow: 0 1px 3px 0 #cfd7df;
}
.StripeElement--invalid {
border-color: #fa755a;
}
.StripeElement--webkit-autofill {
background-color: #fefde5 !important;
}
</style>
As result in browser's console I see console messages, “Credit or debit card” label and
uncolored “Submit Payment” button : https://imgur.com/a/TRWc23I
If to click on “Submit Payment” button I see :https://imgur.com/a/CdBSfMC
Is way I added Stripe to my vue form invalid? Which is valid way?
I suppose that I do not have to insert any additive elements/code in this block :
<div id="card-element">
<!-- A Stripe Element will be inserted here. -->
</div>
and they must be uploaded automatically on my form upload ?
and which method have I to use as in my app I save my data with axios?
I made all correctly, it was styles issue.
I replaced css from the example with lines :
#card-element {
line-height: 1.5rem;
margin: 10px;
padding: 10px;
}
.__PrivateStripeElement {
min-width: 300px !important;
min-height: 40px !important;
color: $text_color;
}
and my form was ready for payment!

How to do databind two way in v-html?

I have an element div with atribute contenteditable="true". This div behaves like an element textarea.
<div v-on:keyup.enter="SendMensage" v-html="msg" contenteditable="true"></div>
my code:
data() {
return {
msg: '',
}
},
methods: {
enviaMensagem() {
console.log(this.msg);
}
}
My problem is that the databind does not work. What is typed in the div does not reflect on the variable. Does anyone know what can it be?
You need to listen to changes of the element, because v-model only works on <textarea> or <input>. You can do this by using an #input listener.
The markup you get like this will be escaped. If you want to unescape it, you can use e.g. this approach. Then, you actually have the markup right next to the pseudo-textarea field. So, why not using a <textarea> from begin with?
new Vue({
el: '#editor',
data: {
msg: ''
},
methods: {
typing: function(el) {
this.msg = el.target.innerHTML;
},
submit: function() {
console.log("Submitting: ", this.msg);
}
}
});
.input {
display: inline-block;
vertical-align: middle;
border: 1px solid black;
width: 200px;
height: 100px;
}
.output {
display: inline-block;
vertical-align: middle;
width: 200px;
height: 100px;
background: #eee;
}
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.min.js"></script>
<div id="editor">
<div class="input" #input="typing" #keyup.enter="submit" contenteditable="true"></div>
<div class="output" v-html="msg"></div>
</div>