Bootstrap Modal and Datatables.net - datatables

I am displaying a bootstrap modal dialog from within a datatables event handler that is defined like this:
dt_table.on('click', 'tr', function(e) {
//console.log('row clicked');
if ( $(this).hasClass('selected') ) {
$(this).removeClass('selected');
}
else {
//check to see if user has any pending changes
var saveButton = document.getElementById('saveBtn');
if (saveButton && !saveButton.disabled) {
//for now if save button is enabled ask user if they're sure they don't want to save pending changes first
var options = {
'backdrop' : 'static', //prevents clicking outside the model to dismiss dialog
}
$('#pendingChanges').modal(options);
var save_pending_changes = $('#save_pending_changes').val();
console.log(save_pending_changes);
if (save_pending_changes) {
// console.log(e);
//e.preventDefault();
return;
}
}
console.log('about to execute default behavior');
//first unselect any other selected row
dt_table.$('tr.selected').removeClass('selected');
//then select row of interest
$(this).addClass('selected');
var rowIndices = dt_table.row('.selected')[0];
selectedRowIndex = rowIndices[0];
//set hidden param
$('#selectedRowIndex').val(selectedRowIndex);
var row = dt_table.rows(rowIndices);
if (row.data().length == 0) {
//e.preventDefault();
return;
}
var id = row.data()[0]['id'];
var url = '/report/' + id + '/';
//window.location = url;
$('#notes_display').load(url + ' #notes_display');
}
});
The issue I'm running into is that the entire event handler executes before the modal is dismissed. It's behaving as if the modal is running in it's own thread. How can I fix this?
My modal is defined as
<div class="modal fade" id="pendingChanges" tabindex="-1" role="dialog" aria-labelledby="pendingChangesLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title" id="pendingChangesLabel">You have unsaved changes</h3>
</div>
<div class="modal-body">
<p>
<strong>Are you sure you want to leave?</strong>
</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal" onclick="saveChanges(true)">No</button>
<button type="button" class="btn btn-primary" data-dismiss="modal" onclick="saveChanges(false)">Yes</button>
</div>
</div>
</div>
</div>

A sample of the logic would have been helpful :) But guess I understand the problem. I will recommend you split the workflow up in promises :
//determine if modal should displayed
function determine(tr) {
return new Promise(function(resolve) {
//dont know the logic here
resolve(true / false)
})
}
//show modal and return caption of the clicked button
function showModal() {
return new Promise(function(resolve) {
$('#pendingChanges button').one('click', function(e) {
resolve(e.currentTarget.innerText)
})
$('#pendingChanges').modal({
backdrop: 'static',
keyboard: false
})
})
}
dt_table.on('click', 'tr', function(e) {
determine(this).then(function(result) {
if (result) {
showModal().then(function(button) {
console.log(button) //Yes or No
})
}
})
})
Not tested but should be OK.
determine() if the modal should be shown based on the <tr>
Call showModal() which is only resolved when a button is clicked
You get either Yes or No

Thanks to davidkonrad I solved my issue. Here's the working code.
<input type="hidden" name="save_pending_changes" id="save_pending_changes" value="false"/>
<!-- Modal -->
<div class="modal fade" id="pendingChanges" tabindex="-1" role="dialog" aria-labelledby="pendingChangesLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title" id="pendingChangesLabel">You have unsaved changes</h3>
</div>
<div class="modal-body">
<p>
<strong>Are you sure you want to leave?</strong>
</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal" onclick="saveChanges(true)">No</button>
<button type="button" class="btn btn-primary" data-dismiss="modal" onclick="saveChanges(false)">Yes</button>
</div>
</div>
</div>
</div>
function saveChanges(save) {
$('#save_pending_changes').val(save);
}
dt_table.on('click', 'tr', function(e) {
var oldRow = getCurrentRow();
determine(this).then(function(result) {
var enabled = result['enabled'];
var tr = result['tr'];
if (enabled) {
showModal().then(function(save) {
if (save == 'true') {
setSelection(oldRow);
return;
}
else {
select(tr);
}
})
}
else {
select(tr);
}
})
})
//determine if modal should displayed
function determine(tr) {
return new Promise(function(resolve) {
//check to see if user has any pending changes
var saveButton = document.getElementById('saveBtn');
var enabled = false;
if (saveButton) {
enabled = !saveButton.disabled;
}
var result = {};
result['enabled'] = enabled;
result['tr'] = tr;
resolve(result);
})
}
//show modal and return caption of the clicked button
function showModal() {
return new Promise(function(resolve) {
$('#pendingChanges button').one('click', function(e) {
var save_pending_changes = $('#save_pending_changes').val();
resolve(save_pending_changes);
})
$('#pendingChanges').modal({
backdrop: 'static',
keyboard: false
})
})
}
function getCurrentRow() {
return (dt_table.$('tr.selected'));
}
function setSelection(row) {
//first unselect any other selected row
dt_table.$('tr.selected').removeClass('selected');
//then select row
row.addClass('selected');
}
function select(tr) {
//first unselect any other selected row
dt_table.$('tr.selected').removeClass('selected');
//then select row of interest
$(tr).addClass('selected');
var rowIndices = dt_table.row('.selected')[0];
selectedRowIndex = rowIndices[0];
//set hidden param
$('#selectedRowIndex').val(selectedRowIndex);
var row = dt_table.rows(rowIndices);
if (row.data().length == 0) {
//e.preventDefault();
return;
}
var id = row.data()[0]['id'];
var url = '/report/' + id + '/';
//window.location = url;
$('#notes_display').load(url + ' #notes_display');
}

Related

vuejs event modifier search list process

I'm a vue js newbie, I perform a get operation with the value entered in the search input and if there is a result, I show it in "listShow", if there is no result, I return "listShow" false. no problem so far. only if the user chooses any of the incoming data, I send the "name" searchtext of the incoming data to the input. but if there is no result "listShow false" and click somewhere outside the input
I want to make "newDiv" true. so "inputOutClick" does the job, but when I click on any of the "search" data, "inputOutClick" does not allow this "selecteds()" function to fire.
And also, is my coding style correct, I'm getting too repetitive.
Is it ok to use search #keyup?
Does it make sense to use v-on:focusout?
const app = new Vue({
el: '#app',
data: {
searchText: '',
listShow: true,
newDiv:false,
searcList:[],
},
methods: {
inputOutClick() {
this.listShow = false
},
selecteds(list) {
this.listShow = false;
this.searchText = list.name;
},
async search() {
if (this.searchText !== '') {
const res = await this.callApi('get', 'search' + '?filter=' + this.searchText)
if (res.status === 200) {
this.searcList = this.getList;
if (res.data.length > 0) {
this.listShow = true;
} else {
this.listShow = false;
}
}
} else {
this.listShow = false;
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.js"></script>
<div id="app">
<div>
<input
type="text"
v-model="searchText"
#keyup="search"
v-on:focusout="inputOutClick"
/>
<div v-if="listShow" style="background:red">
<ul>
<li v-for="(list, index) in searcList">
<a #click="selecteds(list)">{{ list.name }}</a>
</li>
</ul>
</div>
<div v-if="newDiv">
<p>hello</p>
</div>
</div>
</div>
You can use #mousedown.prevent on the searchList entries (where the click handler is attached). This prevents the v-on:focusout event being fired, if a searchList entry is clicked.
<input
type="text"
v-model="searchText"
#keyup="search"
v-on:focusout="inputOutClick"
/>
<a
#click="selectEntry(entry)"
#mousedown.prevent
>
xxx
</a>
Use #mousedown instead of #click.
=> #click runs after #focusout.
=> #mousedown runs before #focusout.
If you do not want to run the focusout function on the input field when the list is clicked at all then you can use #mousedown.prevent="selecteds(list)".
See example below (click on "Full page" so the console.log doesn't block the list):
const app = new Vue({
el: '#app',
data: {
searchText: '',
listShow: true,
newDiv:false,
searcList:[],
list: {}
},
methods: {
inputOutClick() {
console.log("inputOutClick");
if (this.listShow == false) {
console.log("mousedown was fired first");
}
this.listShow = false
},
selecteds(list) {
console.log("selecteds");
this.listShow = false;
this.searchText = list.name;
},
async search() {
console.log("search");
this.listShow = true;
this.searcList = ['aeaeg', 'tdthtdht', 'srgsr'];
this.list.name = "TEST"
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.js"></script>
<div id="app">
<div>
<input
type="text"
v-model="searchText"
#keyup="search"
v-on:focusout="inputOutClick"
/>
<div v-if="listShow" style="background:red">
<ul>
<li v-for="(list, index) in searcList">
<a #mousedown="selecteds(list)">LIST TEXT</a>
</li>
</ul>
</div>
<div v-if="newDiv">
<p>hello</p>
</div>
</div>
</div>

How to show a hidden div by clicking on another div (button) in Vue?

I am trying to show the loading div that's normally hidden after clicking a confirm button, even after changing the hidden value on click.
here is my code:
<div class="form-btns">
<button type="button" class="btns full-width" v-on:click="submitPhoneNumber(); isHidden = false">
{{t('confirmNumber')}}
</button>
<loader title="" v-if="!isHidden"></loader>
<button type="button" class="btns hyperlink">
{{t('accountRecovery')}}
</button>
</div>
data() {
return {
isHidden: true,
};
Your code is a little funky. Get rid of how you're trying to set the value directly in the template HTML and set it in your function in the methods. So your new code would be:
<div class="form-btns">
<button type="button" class="btns full-width" v-on:click="submitPhoneNumber()">
v {{t('confirmNumber')}}
</button>
<loader title="" v-if="!isHidden"></loader>
<button type="button" class="btns hyperlink">
{{t('accountRecovery')}}
</button>
</div>
Then in your methods just toggle the value like this:
data() {
return {
isHidden: true,
}
},
methods: {
submitPhoneNumber() {
this.isHidden = false; // I would probably rename this to isLoading and invert the logic
// Other stuff...
}
}
You should change the isHidden value inside the submitPhoneNumber, either as below, or with a dedicated function that you call in the first one.
let app = new Vue({
el: '#app',
data: {
isHidden: true
},
methods: {
submitPhoneNumber() {
this.isHidden = false;
// Simulate an API response after 3s to hide the loader
setTimeout(() => { this.isHidden = true; }, 3000);
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button type="button" #click="submitPhoneNumber()">
Submit button
</button>
<p v-if="!isHidden">Loading...</p>
</div>

How to make disabled button after click in Vuejs

I have a button on my website that gives bonuses to the user. Button have several conditions in 1 button:
<button class="btn btn--small btn--purple" :disabled="isDisabled" #click="takeBonus">Take</button>
<script>
......
computed: {
isDisabled() {
return this.heal_used === 1 || this.diff < 10;
this.$forceUpdate();
},
},
.......
</script
But when user click Take button, and if all success, button is still active this.$forceUpdate(); not working. And i need make when user click Take button, and if all success, make this button disabled.
My full Bonus.vue:
<template>
<div class="inner-page">
<div class="account" v-if="loaded && !$root.isMobile">
<div class="page-header">
</div>
<div class="form-panels hide-below-m">
<div class="col-7" style="margin-top: 5rem;margin-right: 3rem;">
<div class="faucet-component mx-5" rv-class-boost="data.boostIsOpen">
<img src="https://dota2.skins1.games/src/img/components/shine.png?v=8ce59643e70cb2f8550deb6a249b5f29" class="faucet-component__shine-bg">
<div class="faucet-component__content d-flex justify-content-between align-items-center flex-column w-100" style="
height: 15rem;">
<div class="faucet-component__available-amount-block round-circle p-2">
<div class="faucet-component__availabe-amount-coins d-flex justify-content-center align-items-center round-circle h-100" rv-currency="model:amount">Спасение</div>
</div>
<!-- rivets: unless model:cnt | eq 0 --><div class="faucet-component__remaining">
<span rv-t="">Left</span>:
<span>{{ bonus_num }}</span><br>
<span rv-t=""></span>
<span>{{ diff }}</span>
</div>
<!-- rivets: if model:cnt | eq 0 -->
<div class="faucet-component__buttons-container d-flex align-items-center w-75 justify-content-around">
<button class="btn btn--small btn--purple" :disabled="isDisabled" #click="takeBonus">Take</button>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
loaded: false,
bonus: {},
diff: {},
user: {},
bonus_num: 0,
heal_used: {}
}
},
mounted() {
this.$root.isLoading = true;
if (!this.$cookie.get('token')) {
this.$root.isLoading = false;
this.$router.go(-1);
}
this.domain = window.location.protocol + '//' + window.location.hostname;
setTimeout(() => {
this.getUser();
}, 100);
},
computed: {
isDisabled() {
return this.heal_used === 1 || this.diff < 10;
this.$forceUpdate();
},
},
methods: {
getUser() {
this.$root.axios.post('/user/getProfile')
.then(res => {
const data = res.data;
console.log(data.heal_used);
console.log(data.diff);
this.loaded = true;
this.user = data.user;
this.bets = data.bets;
this.bonus = data.bonus;
this.diff = data.diff;
this.heal_used = data.heal_used;
this.new_day = data.new_day;
this.bonus_num = data.bonus_num;
this.$root.isLoading = false;
})
.catch(err => {
this.$root.isLoading = false;
this.$router.go(-1);
})
},
takeBonus() {
this.$root.axios.post('/user/takeBonus', {
value: this.user.cashback
})
.then(res => {
const data = res.data;
if (data.type === 'success') {
console.log(data.heal_used);
this.bonus_num = data.bonus_num;
this.$root.user.balance = data.newBalance;
this.heal_used = data.heal_used;
this.$forceUpdate();
}
this.$root.showNotify(data.type, this.$t(`index.${data.message}`));
})
},
}
}
How i can make it, when user click Take button, and if all success, so that the Take button becomes disabled?
I'm sorry but your code has no indentation, so I just did that on jsfiddler so you know "How to make disabled button after click in Vuejs". You can have a look on : https://jsfiddle.net/o81yvn05/1/
<div id="app">
<button :disabled="isDisabled" #click="disableButton()">Please click here</button>
</div>
<script>
new Vue({
el: "#app",
data: {
isDisabled: false,
},
methods: {
disableButton() {
this.isDisabled = true
}
}
})
</script>

Error mesage quickly displaying then hiding as expected in VUE

I have a page which I validate the email add input #blur. This works perfectly and displays the error message if it fails validation rules set but the issue I have is that due to the #blur, when I click my reset button the error quickly displays then hides and this is poor UI and I want to stop it but can't figure out how to.
HTML
<div class="card" v-on:click="select($event)">
<div class="card-body">
<div class="form-group row">
<label class="col-sm-3 col-form-label pr-0" for="emailAddField">Email <span class="text-danger">*</span></label>
<div class="col-sm-9">
<div class="input-group">
<input id="emailAddField" ref="pdEmailAdd" class="form-control" type="search" :value="pdEmailAdd" #input="pdEmailAddInput" #blur="emailInputValChecker($event)" placeholder="Enter an email address">
<div class="input-group-append" :class="emailButtonValidation">
<a class="btn input-group-text primaryBtn" :class="emailButtonValidation" type="button" :href="'mailto:' + pdEmailAdd">
<i class="far fa-envelope"></i>
</a>
</div>
</div>
<div v-if="emailFormatErrorMsg" class="text-danger">Incorrect email address format</div>
</div>
<div class="card-footer">
<button id="resetButton" ref="resetButton" class="btn btn-warning col-4" #click="pdInitPageStates($event)" :disabled="resetDetails">
Reset details
</button>
</div>
</div>
I have 'hacked' at the card trying to use #click on the card to get the id but this didn't work so I set the id in my `data but not happy about it and sure there is a lot better way but I just can't figure it out
Code
data() {
return {
pdEmailAdd: '',
reg: /^(([^<>()\[\]\\.,;:\s#"]+(\.[^<>()\[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,24}))$/,
detailsChanged: false,
emailIncorrectFormat: false,
targetId: 'resetButton', // HACK
targetId2: '', // HACK
}
},
computed: {
emailButtonValidation() {
if (!this.pdEmailAdd || !this.reg.test(this.pdEmailAdd)) {
if (this.pdEmailAdd === '') {
this.emailIncorrectFormat = false;
} else {
this.emailIncorrectFormat = true;
}
return 'disabled'
} else {
this.emailIncorrectFormat = false;
return ''
}
},
resetDetails() {
this.detailsChanged = false;
if (this.pdName != this.$store.state.account.firstname + ' ' + this.$store.state.account.lastname) {
this.detailsChanged = true;
}
if (this.telNoType === 'ddi' && this.pdTelNo != this.$store.state.account.ddi) {
this.detailsChanged = true;
} else if (this.telNoType === 'mobile' && this.pdTelNo != this.$store.state.account.mobile) {
this.detailsChanged = true;
} else if (this.telNoType === 'na' && this.pdTelNo != '') {
this.detailsChanged = true;
}
if (this.pdExtNo != this.$store.state.account.extension) {
this.detailsChanged = true;
}
if (this.pdEmailAdd != this.$store.state.user.adminemail) {
this.detailsChanged = true;
}
return !this.detailsChanged;
}
}
// Another hack to try set it soon as page loads
mounted() {
this.$refs.resetButton.click();
},
methods: {
emailInputValChecker(event) {
this.emailFormatErrorMsg = false;
if (!this.pdEmailAdd || !this.reg.test(this.pdEmailAdd)) {
if (this.pdEmailAdd === '') {
this.emailFormatErrorMsg = false;
} else {
this.select(event)
// Uses the 'dirty hacks'
if (this.targetId !== '' && this.targetId !== 'resetButton' && this.targetId2 !== 'resetButton') {
this.emailFormatErrorMsg = true;
};
}
}
},
select(event) {
this.targetId = event.target.id;
if (this.targetId === 'resetButton') {
this.targetId2 === 'resetButton';
} else if (this.targetId === '') {
this.targetId === 'resetButton';
}
}
}
Basically all I want is the input to check it passes validation when input is left unless the reset button is clicked then ignore it but think I've gone code blind and can't think of a way to do this.
The mousedown event on your reset button is what causes blur on the input to fire. Adding #mousedown.prevent to the reset button will stop that from happening specifically when the reset button is clicked.
This snippet ought to illustrate the solution. Remove #mousedown.prevent from the reset button and you'll see similar behavior to your issue, with the error briefly flashing.
new Vue({
el: '#app',
data: {
email: '',
error: null
},
methods: {
onBlur () {
if (this.email === 'bad') {
this.error = 'bad email!'
} else {
this.error = null
}
},
onReset () {
this.error = null
this.email = ''
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button #click="onReset" #mousedown.prevent>Reset</button>
<p>
Type in "bad" and leave input to trigger validation error<br>
<input type="text" v-model="email" #blur="onBlur"/>
</p>
<p>{{ email }}</p>
<p v-if="error">error!</p>
</div>

VueJS Computed Data Search Not Functioning

I am attempting to create a search function in my Vue.js 2 application. However, even though my algorithm is giving me the right results, I am not getting the proper filter. Right now, whenever I run a search, I get nothing on the page. Here is my code:
computed:{
filteredSmoothies: function(){
return this.smoothies.filter(smoothie => {
var stuff = smoothie.title.split(" ").concat(smoothie.description.split(" ")).concat(smoothie.category)
var sorted = [];
for (var i = 0; i < stuff.length; i++) {
sorted.push(stuff[i].toLowerCase());
}
console.log(sorted)
if(this.search != null){
var indivthings = this.search.split(" ")
indivthings.forEach(item => {
if(sorted.includes(item.toLowerCase())){console.log("true")} else {console.log("false")}
if(sorted.includes(item.toLowerCase())){return true}
})
} else {
return true
}
})
}
}
Here is my relevant HTML:
<div class="container">
<label for="search">Search: </label>
<input type="text" name="search" v-model="search">
</div>
<div class="index container">
<div class="card" v-for="smoothie in filteredSmoothies" :key="smoothie.id">
<div class="card-content">
<i class="material-icons delete" #click="deleteSmoothie(smoothie.id)">delete</i>
<h2 class="indigo-text">{{ smoothie.title }}</h2>
<p class="indigo-text">{{ smoothie.description }}</p>
<ul class="ingredients">
<li v-for="(ing, index) in smoothie.category" :key="index">
<span class="chip">{{ ing }}</span>
</li>
</ul>
</div>
<span class="btn-floating btn-large halfway-fab pink">
<router-link :to="{ name: 'EditSmoothie', params: {smoothie_slug: smoothie.slug}}">
<i class="material-icons edit">edit</i>
</router-link>
</span>
</div>
</div>
As the discussions in the comments, the root cause should be:
you didn't return true/false apparently inside if(this.search != null){}, it causes return undefined defaultly.
So my opinion is use Javascript Array.every or Array.some. Also you can use for loop + break to implement the goal.
Below is one simple demo for Array.every.
computed:{
filteredSmoothies: function(){
return this.smoothies.filter(smoothie => {
var stuff = smoothie.title.split(" ").concat(smoothie.description.split(" ")).concat(smoothie.category)
var sorted = [];
for (var i = 0; i < stuff.length; i++) {
sorted.push(stuff[i].toLowerCase());
}
console.log(sorted)
if(this.search != null){
var indivthings = this.search.split(" ")
return !indivthings.every(item => { // if false, means item exists in sorted
if(sorted.includes(item.toLowerCase())){return false} else {return true}
})
} else {
return true
}
})
}
or you can still use forEach, the codes will be like:
let result = false
var indivthings = this.search.split(" ")
indivthings.forEach(item => {
if(sorted.includes(item.toLowerCase())){result = true}
})
return result
filteredSmoothies is not returning a list. You iterate over to see if indivthings is contained within sorted, but you don't do anything with that information. I think you need to modify your sorted list based on the results of indivthings.