I need to hide/show a certain option group (<optgroup>) conditionally and I've tried the top solution from this question - Select2: Hide certain options dynamically
It hides every option (as required), but group title stays visible, although it has no child items. How to hide the whole option group in select2?
I think you can disable <optgroup> in Select2 only using data array source, not using options in HTML.
See issues on Select2 github repo:
https://github.com/select2/select2/issues/4876
https://github.com/select2/select2/pull/5035
Here is a snippet which exemplifies the two approach:
var data = [{
text: 'group1',
children: [{
id: '1',
text: 'option 1'
}, {
id: '2',
text: 'option 2',
disabled: false,
}, {
id: '3',
text: 'option 3',
disabled: true,
}]
},
{
text: 'group2',
disabled: true,
children: [{
id: '4',
text: 'option 4',
disabled: true,
}, {
id: '5',
text: 'option 5'
}]
}];
$(document).ready(function() {
$('#test').select2({ data: data, });
$('#test2').select2();
});
var group2Disabled = true;
toggleGroup2 = function() {
group2Disabled = !group2Disabled;
console.log("toggleGroup2", group2Disabled);
var gr2 = data.find(findGroup2);
gr2.disabled = !gr2.disabled;
$('#test').empty().select2({data:data}).trigger("change");
}
function findGroup2(el) {
return el.text === 'group2';
}
select {
width: 40%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.5/js/select2.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.5/css/select2.min.css" rel="stylesheet"/>
<label>Select List #1 (data)</label>
<select id="test"></select>
<button onclick="toggleGroup2()">toggle disable Group2</button>
<br><br><br>
<label>Select List #2 (html)</label>
<select id="test2">
<optgroup label="group1">
<option>option 1</option>
<option>option 2</option>
<option disabled="true">option 3</option>
</optgroup>
<optgroup label="group2" disabled="true">
<option disabled="true">option 4</option>
<option>option 5</option>
</optgroup>
</select>
Or you can check this jsfiddle: https://jsfiddle.net/beaver71/u0jekg44/
You can use this .css to hide the options and the optgoup
.select2-results__option[aria-disabled=true]
{
display: none;
}
Don't forget to put the disabled="true" attribute in both elements. You can also set this attribute dynamically using JQuery ($(theOption).prop('disabled', true)).
for whoever stumbles upon this, no, you do nοt need to alter the data source in order to disable an opt group
like Mateus wrote, css to hide disabled options and optgroups:
.select2-results__option[aria-disabled=true]
{
display: none;
}
and javascript logic to disable optgroups, if they have no options enabled:
mySelect.find("optgroup").each(function(){
let totalOptions = $(this).find("option").length
let disabledOptions = $(this).find("option:disabled").length
if( totalOptions == disabledOptions ) $(this).prop('disabled', true)
else $(this).prop('disabled', false)
})
Related
I am using react-bootstrap-table2 pagination in my react application.I am using a state to update the sizePerPage dynamically.But it is not working. When I console the state, I am able to see the change, but it is not updating the pagination.
Here is my code:
const [sizePerPage,setSizePerPage] = useState(10);
const pagination = paginationFactory({
page:1,
sizePerPage:sizePerPage,
.......
onPageChange:function(page,sizePerPage),
onSizePerPageChange:function(page,sizePerPage)
});
Here I am changing the state:
<select onChange={e=>setSizePerPage(e.target.value)}>
<option value={10}>10</option>
<option value={25}>25</option>
<option value={50}>50</option>
</select>
Rest of the things are working fine.
I think you have a typo in your code.
onSizePerPageChange:function(page,sizePerpage)
Should be:
onSizePerPageChange:function(page,sizePerPage)
If there is no reason you need a custom dropdown list you can use the built-in feature by doing this:
const pagination = paginationFactory({
sizePerPageList: [{
text: '10', value: 10
}, {
text: '25', value: 25
}, {
text: '50', value: 50
}],
});
I'm trying two way binding (knockout observables) with sharepoint modal dialog
var ViewModel = function () {
var self = this;
self.firstName = "Irfanullah";
self.lastName = ko.observable('M.');
self.fullName = ko.computed(function () {
return self.firstName + ' ' + self.lastName();
});
};
ko.applyBindings(new ViewModel());
<button type=button onclick="openDialog2()">click me</button>
<div id="wrap" style="display:none">
<div id="d10" class="dialogHolder">
<div id="kobasic">
<h4>Editable data</h4>
<p><input type="text" data-bind="value: firstName" /></p>
<p><input type="text" data-bind="value: lastName" /></p>
<p>Full Name: <span data-bind="text: fullName"></span></p>
</div>
</div>
When i test this code on sharepoint wiki page its working good, but when i use same code on sharepoint dialog it shows values (one way binding)but two way binding/ko.observable() does not work (when i type something in lastname text box it does not update fullname)
function openDialog2() {
var e = document.getElementById('d10');
var options = {
title: "Test Knockout",
width: 700,
height: 800,
html: e.cloneNode(true)
};
mydialog = SP.UI.ModalDialog.showModalDialog(options);
}
I believe that is alll becase e.cloneNode(true) but i could not figureout alternat solution
For SharePoint dialogs I am using this approach:
(note: jQuery needed)
// create dom element
var element = document.createElement('div');
// apply my custom view
$(element).append('<!--my HTML -->');
// apply knockout bindings
ko.applyBindings(myViewModel, element);
// show sharepoint modal dialog
var options = {
allowMaximize: false,
html: element,
title: "My title",
autoSize: true,
showClose: true,
dialogReturnValueCallback: myCallback
};
SP.UI.ModalDialog.showModalDialog(options);
So in your case:
var element = document.createElement('div');
$(element).append('<div id="d10" class="dialogHolder"><div id="kobasic"><h4>Editable data</h4><p><input type="text" data-bind="value: firstName" /></p><p><input type="text" data-bind="value: lastName" /></p><p>Full Name: <span data-bind="text: fullName"></span></p></div></div>');
ko.applyBindings(new ViewModel(), element);
var options = {
allowMaximize: false,
html: element,
title: "My title",
autoSize: true,
showClose: true,
dialogReturnValueCallback: myCallback
};
SP.UI.ModalDialog.showModalDialog(options);
I need to add an additional field to my custom form, I want to add the name of the credit card.
I tried in the following way:
var cardNameElement = elements.create('cardName', {
style: style
//, placeholder: 'Custom card number placeholder',
});
cardNameElement.mount('#card-name-element');
<div id="card-name-element" class="field"></div>
But this does not work, in its documentation only allows to perform these procedures validating only four elements or data: cardNumber, cardExpiry, cardCvc, postalCode.
How can I add the name of the credit card and validate it using stripe.js
My code:
var stripe = Stripe('pk_test_6pRNASCoBOKtIshFeQd4XMUh');
var elements = stripe.elements();
var style = {
base: {
iconColor: '#666EE8',
color: '#31325F',
lineHeight: '40px',
fontWeight: 300,
fontFamily: 'Helvetica Neue',
fontSize: '15px',
'::placeholder': {
color: '#CFD7E0',
},
},
};
var cardNumberElement = elements.create('cardNumber', {
style: style
//, placeholder: 'Custom card number placeholder',
});
cardNumberElement.mount('#card-number-element');
var cardExpiryElement = elements.create('cardExpiry', {
style: style
});
cardExpiryElement.mount('#card-expiry-element');
var cardCvcElement = elements.create('cardCvc', {
style: style
});
cardCvcElement.mount('#card-cvc-element');
/*var postalCodeElement = elements.create('postalCode', {
style: style
});
postalCodeElement.mount('#postal-code-element');*/
function setOutcome(result) {
var successElement = document.querySelector('.success');
var errorElement = document.querySelector('.error');
successElement.classList.remove('visible');
errorElement.classList.remove('visible');
if (result.token) {
// In this example, we're simply displaying the token
successElement.querySelector('.token').textContent = result.token.id;
successElement.classList.add('visible');
// In a real integration, you'd submit the form with the token to your backend server
//var form = document.querySelector('form');
//form.querySelector('input[name="token"]').setAttribute('value', result.token.id);
//form.submit();
} else if (result.error) {
errorElement.textContent = result.error.message;
errorElement.classList.add('visible');
}
}
document.querySelector('form').addEventListener('submit', function(e) {
e.preventDefault();
stripe.createToken(cardNumberElement).then(setOutcome);
});
<script src="https://code.jquery.com/jquery-2.0.2.min.js"></script>
<script src="https://js.stripe.com/v3/"></script>
<form action="" method="POST">
<input type="hidden" name="token" />
<div class="group">
<div class="card-container1">
<label>
<span class="title-card">Card number</span>
<div id="card-number-element" class="field"></div>
<span class="brand"><i class="pf pf-credit-card" id="brand-icon"></i></span>
</label>
</div>
<div class="card-details">
<div class="expiration">
<label>
<span class="title-card">Expiry date</span>
<div id="card-expiry-element" class="field"></div>
</label>
</div>
<div class="cvv">
<label>
<span class="title-card">CVC</span>
<div id="card-cvc-element" class="field"></div>
</label>
</div>
</div>
</div>
<button type="submit">Pay $25</button>
<div class="outcome">
<div class="error"></div>
<div class="success">Success! Your Stripe token is <span class="token"></span></div>
</div>
</form>
What I want to do:
Elements does not support collecting the cardholder's name at the moment. It focuses on collecting:
Card number
Expiration date
CVC
ZIP code (in some countries)
If you want to collect the cardholder's name you have to build your own field for the name and submit it to the API during token creation:
var card_name = document.getElementById('card_name').value;
stripe.createToken(card, {name: card_name}).then(setOutcome);
You can see a live example on jsfiddle here: https://jsfiddle.net/7w2vnyb5/
As I struggled like an idoit on this for a while. As of Feb 2019 you can add tokenData object with information on the details of the card. For Example:
let custData = {
name: 'Firstname Lastname',
address_line1: '21 Great Street',
address_line2: 'Shilloong',
address_city: 'Chicago',
address_state: 'Illinois',
address_zip: '12345',
address_country: 'US'
};
stripe.createToken(card, custData).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.
stripeTokenHandler(result.token);
}
});
});
If you're using "PaymentIntents", which you probably should be if you're EU based / SCA compliant, then the format for this has changed again slightly...
stripe.confirmCardPayment(
'{PAYMENT_INTENT_CLIENT_SECRET}',
{
payment_method: {
card: cardElement,
billing_details: {
name: 'Jenny Rosen'
}
}
}
).then(function(result) {
// Handle result.error or result.paymentIntent
});
stripe.confirmCardPayment docs:
https://stripe.com/docs/stripe-js/reference#stripe-confirm-card-payment
billing_details object docs:
https://stripe.com/docs/api/payment_methods/create#create_payment_method-billing_details
I use Meta-Data for custom fields such as cardholder name:
... create({
amount: myAmount,
currency: 'USD,
description: "Put your full discription here",
source: tokenid,
metedata: {any: "set of", key: "values", that: "you want", cardholder: "name"}
},
idempotency_key "my_idempotency_key"
)}
resource: https://stripe.com/docs/payments/charges-api#storing-information-in-metadata
I have a data grid component in Vue.js, which looks a bit like the one in the official sample: http://vuejs.org/examples/grid-component.html
Based on the input data instead of pure strings sometimes I'd like to display entries "decorated" as a checkbox or a v-link component (not exclusively, I may need to render other components too, like unescaped HTML or an img).
Obviously I don't want to prepare the Grid component for all the use cases, so this is not what I'd like to do:
A sample data model to be displayed:
model = [
{
field1: 'some string',
field2: 'another string',
field3: { // this should be a checkbox
state: true
},
field4: { // this should be an <a v-link>
url: 'http://whatever',
label: 'go somewhere'
}
}
]
A relevant excerpt from the Grid component:
<template>
...
<tr v-for="entry in model">
<td>
<div v-if="typeof entry === 'object' && entry.hasOwnPropery('url')">
<a v-link="entry.url">{{ entry.label }}</a>
</div>
<div v-if="typeof entry === 'object' && entry.hasOwnProperty('state')">
<input type="checkbox" v-model="entry.state">
</div>
<div v-else>
{{ entry }}
</div>
</td>
</tr>
...
</template>
What's the Vue.js philosophy for injecting custom components as decorators? I want my Grid to be completely agnostic regarding these decorator components.
This would be a good place for a mutable component piece. You define a few different decorator components and then use your data to decide which one should be used for rendering.
Template:
<div id="app">
<ul>
<li
v-for="entry in entries"
>
<component :is="entry.type">
{{ entry.content }}
</component>
</li>
</ul>
</div>
Component:
new Vue({
el: '#app',
components: {
'blank': {
template: '<div><slot></slot></div>'
},
'green': {
template: '<div style="color: #0f0;"><slot></slot></div>'
},
'red': {
template: '<div style="background-color: #f00;"><slot></slot></div>'
}
},
computed: {
entries: function() {
return this.raw_entries.map(
function(entry) {
if (typeof entry !== "object") {
return { type: 'blank', content: entry }
}
if (!entry.hasOwnProperty('type')) {
entry.type = 'blank'
}
return entry
}
)
}
},
data: {
raw_entries: [
'Base Text',
{
type: 'green',
content: 'Green Text'
},
{
type: 'red',
content: 'Red Background'
}
]
}
})
JsFiddle Working example using lists
Still trying to work with Dgrid (0.4) and dojo (1.10), I have now another issue with the selection.
My web page contain a Dialog opened when you click on a button.
Inside this dialog, we have the following code which display a grid with data coming from a database through a Json HTTP page. This is working fine, even sorting and query filtering.
What I want to do know is to allow the user to double click on a row, get the selected row Id contains in the first column to update the form in the main page. I use the dgrid/selection for this. However, it always return the last row of the grid instead of the one the user selected.
The selection code is based on this :
http://dgrid.io/tutorials/0.4/hello_dgrid/
Any idea?
Thanks
<script language="javascript">
require
(
[
"dojo/_base/declare",
"dojo/_base/array",
"dgrid/OnDemandList",
"dgrid/OnDemandGrid",
"dgrid/Keyboard",
"dgrid/Selection",
"dgrid/Editor",
"dgrid/extensions/ColumnHider",
"dstore/Memory",
"dstore/RequestMemory",
"dojo/_base/lang",
"dojo/dom-construct",
"dojo/dom",
"dojo/on",
"dojo/when",
"dojo/query",
"dojo/store/Observable",
"dstore/Rest",
"dojo/_base/Deferred",
"dojo/store/Cache",
"dojo/domReady!",
],
function(
declare, arrayUtil, OnDemandList, OnDemandGrid, Keyboard, Selection, Editor, ColumnHider, Memory, RequestMemory, lang, ObjectStore, dom, on, when, query, Observable, Rest, Deferred
){
var fform = dom.byId("filterForm");
var ContactColumns = [
{ label: "", field: "contact_id", hidden: true, unhidable: true},
{ label: "Company Name", field: "company_name", unhidable: true },
{ label: "Contact Name", field: "contact_name", unhidable: true },
{ label: "Email", field: "contact_email", unhidable: true }
];
var ContactGrid=declare([OnDemandGrid, Keyboard, Selection,ColumnHider]);
var contactlist = new Observable(new Rest({ target: './ajax.contactsLoader.php' }));
var selection = [];
window.contactgrid = new ContactGrid(
{
className: "dgrid-selectors",
collection: contactlist,
maxRowsPerPage:10,
selectionMode: 'single',
cellNavigation: false,
columns: ContactColumns
}, "contacttable"
);
on(fform, "submit", function (event) {
var cpy_filter = fform.elements.fcompany_name.value;
var ct_filter = fform.elements.fcontact_name.value;
var email_filter = fform.elements.fcontact_email.value;
contactgrid.set('collection',contactlist.filter({contact_name: ct_filter, company_name: cpy_filter, contact_email: email_filter }));
contactgrid.refresh();
event.preventDefault();
});
contactgrid.on('dgrid-select', function (event) {
// Report the item from the selected row to the console.
console.log('Row selected: ', event.rows[0].data);
});
contactgrid.on('dgrid-deselect', function (event) {
console.log('Row de-selected: ', event.rows[0].data);
});
contactgrid.on('.dgrid-row:click', function (event) {
var row = contactgrid.row(event);
console.log('Row clicked:', row.data);
});
}
);
</script>
<div class="dijitDialogPaneContentArea" style="width:96%;margin-left:5px">
<form id="filterForm">
<div class="dijitDialogPaneActionBar" >
<button data-dojo-type="dijit.form.Button" type="submit">Filter</button>
<button
data-dojo-type="dijit.form.Button"
data-dojo-attach-point="submitButton"
type="submit"
>
Select
</button>
<button
data-dojo-type="dijit.form.Button"
data-dojo-attach-point="cancelButton"
>
Close
</button>
</div>
<div data-dojo-attach-point="contentNode" >
<input type="text" data-dojo-type="dijit.form.TextBox" name="fcompany_name" id="fcompany_name" style="width:33%">
<input type="text" data-dojo-type="dijit.form.TextBox" name="fcontact_name" id="fcontact_name" style="width:32%">
<input type="text" data-dojo-type="dijit.form.TextBox" name="fcontact_email" id="fcontact_email" style="width:33%">
<div id="contacttable">
</div>
</div>
</form>
</div>
Just found the reason.
the columns need to have a 'id' column called ID. I just change the 'contact_id' column to 'id' and it works fine.
thanks