Multiple select in jQuery - javascript

I'm trying to write a jQuery code that works in this way:
When the page is loaded, it is presents only one field.
Selecting an option from that field, a new field is opened and the option have to be all the option of the first , except the option selected. And so on, also for the other created.
The jsfiddle is shown here
var globalObj = {};
var selectedObj = {};
var unselectedObj = {};
var currentSelection = "";
function deleteByVal(obj,val) {
for (var key in obj) {
if (obj[key] == val) delete obj[key];
}
}
$(document).ready(function () {
$(document).on('click', 'target', function(){
var curSelected = $(this).find(":selected").text();
});
$("#select1").one('click',function(){
$(this).children().each(function () {
globalObj[this.value] = this.innerHTML;
});
unselectedObj = globalObj;
});
$(document).on('change', '.prova > .target', function () {
$('div').removeClass('prova');
var $mySelect = $('<select>', {
class: 'target'
});
var selectedValue = $(this).find(":selected").text();
var found = 0;
$.each(selectedObj, function (val, text) {
if (val === selectedValue) {
found++;
}
});
if (found===0) {
selectedObj[this.value] = selectedValue;
deleteByVal(unselectedObj,selectedValue);
}
console.log(selectedValue);
console.log(selectedObj);
console.log(unselectedObj);
var obj = {}; //create an object
$(this).children().each(function () {
if (this.innerHTML!=selectedValue) {
//code
obj[this.value] = this.innerHTML;
}
});
console.log("Questo rappresenta obj:");
console.log(obj);
console.log("stampa obj conclusa");
$( "select" ).not(this).each(function( index ) {
var temp = {}; //create an object
$(this).children().each(function () {
if (this.innerHTML === selectedValue) {
console.log("eliminazione");
$(this).remove();
}
});
$this = $(this);
console.log("stampa temp");
console.log(temp);
console.log("fine stampa temp");
});
$.each(unselectedObj, function (val, text) {
$mySelect.append($('<option />', {
value: val,
text: text
}));
});
var $input = $('<input>', {
type: 'number',
style: 'width: 35px',
min: '1',
max: '99',
value: '1'
});
var $button = $('<button>', {
type: 'button',
class: 'remove_item',
text: 'delete'
});
$(this).parent().after($('<br>', {
class: 'acapo'
}), $('<div>', {
class: 'prova'
}).append($mySelect, $input, $button));
$(this).unbind('change');
$(this).unbind('click');
$(this).on('click', function(){
currentSelection = $(this).find(":selected").text();
});
$(this).on('change',function(){
console.log("nuovo bind");
var selectedValue = $(this).find(":selected").text();
if (currentSelection !== selectedValue) {
console.log(currentSelection + " to "+ selectedValue);
$( "select" ).not(this).each(function( index ) {
$(this).append($('<option />', {
value: currentSelection,
text: currentSelection
}));
$(this).children().each(function () {
if (this.innerHTML === selectedValue) {
console.log("eliminazione");
$(this).remove();
}
});
});
}
});
});
});
The code has some problems and I was thinking to use an existing plugin instead of that code. Is there anyone that knows a plug-in which makes that work?

Here's an option using javascript templating. I'm declaring my available options at the top in an array, thus separating my data model from the UI. I'm using the handlebars library to then compile a template and append it into the DOM. Fiddle: http://jsfiddle.net/LNP8d/1/
HTML/Handlebars
<script id="select-template" type="text/x-handlebars-template">
<div>
<select class="target" id ="select-{{id}}">
<option value="">Select an option…</option>
{{#each options}}
{{#unless selected}}
<option data-option-key="{{#key}}" value="{{value}}">{{label}}</option>
{{/unless}}
{{/each}}
</select>
<input type="number" style="width: 35px" min="1" max="99" value="1">
</div>
</script>
<div id="container">
</div>
Javascript
//Declare the available options
var options = {
option1: {
value: 1,
label: "Option 1"
},
option2: {
value: 2,
label: "Option 2"
},
option3: {
value: 3,
label: "Option 3"
},
option4: {
value: 4,
label: "Option 4"
},
}
source = $("#select-template").html(),
template = Handlebars.compile(source),
onScreen = 0;
//Adds another select menu
var showSelect = function(){
onScreen++;
$("#container").append(template({id:onScreen, options:options}))
};
//Listens to change events
$("body").on("change", ".target", function(){
options[$(this).find(":selected").data("option-key")].selected = true;
showSelect();
});
//Initialises the UI
showSelect();
Please note: this is not a complete example. If you decide to use this method, you'll need to add some checks for what happens when the options run out and if someone changes an option. I hope it's successful in showing you an alternative a potentially more flexible method though.

I think you are looking for something like this:
http://codeassembly.com/Simple-chained-combobox-plugin-for-jQuery/

Related

How to get unchecked value in multiselect jquery plugins

I am using the multiselect jQuery plugin in my ci app
<div class="controls"><select name="delivery_method_' + cloneCntr + '[]" multiple="multiple" class="dm_list_data">' + delivery_method_options + '</select></div>';
$(function () {
$('.dm_list_data').multiselect({
includeSelectAllOption: true,
buttonWidth: '170px',
nonSelectedText: 'Select Delivery Method',
});
});
I have created a function on change. In the function I am not getting unchecked values, it's showing only checked values:
$('.dm_list_data').on('change', function() {
var ischecked= $(this).is(':checked');
if(!ischecked){
alert('uncheckd ' + $(this).val());
} else {
alert("checked");
}
});
Hope this will help you :
see the working example : https://jsfiddle.net/3b4dqhfc/5/
Use jquery's not to get the desired result like this
$(document).ready(function(){
$('.dm_list_data').on('change', function() {
/* use this for unselected value*/
$('.dm_list_data option').not(':selected').each(function(k,v){
console.log(k,v.text, v.value);
});
/* use this for selected value*/
$('.dm_list_data :selected').each(function(k,v){
console.log(k,v.text, v.value);
});
});
});
On every change setup some attribute to unselected and add them in the array.
$('.dm_list_data').on('change', function() {
var $sel = $(this);
val = $(this).val();
$opts = $sel.children();
prevUnselected = $sel.data('unselected');
var currUnselected = $opts.not(':selected').map(function() {
if($("#new-input-"+this.value).length > 0){
$("#new-input-"+this.value).remove();
}
return this.value
}).get();
var currSelected = $('.dm_list_data').val();
$.each(currSelected, function(index, item) {
if($("#new-input-"+item).length == 0){
$("#new-options").append("<input type='text' id='new-input-"+item+"' value='"+item+"'>");
}
});
});
Updated as needed
Add one new div <div id="new-options"></div>

Alternative to HTML in String

I'm using this code right now, but I'm not really liking the way it looks.
function createPageSettingsPopup(page) {
return '<div class="form-group">' +
'<label for="page-title">Title</label>' +
'<input type="text" class="form-control" id="page-title" value="' + page.title + '">' +
'</div>'
}
Is there any alternative to write the HTML in strings to make it look better?
You could use template engines. This is at the expense of elements in the page, but the code will look much cleaner and the template easier to understand as HTML. Put the template in a script tag with type set to text/template
<script type="text/template" id="tmpl">
<div class="form-group">
<label for="page-title">Title</label>
<input type="text" class="form-control" id="page-title" value="{{pageTitle}}">
</div>
</script>
And modify your function as below. Remember to cache the template.
var template = document.getElementById('tmpl').innerHTML; // cache the HTML
function createPageSettingsPopup(page) {
// Find an replace the {{pageTitle}} with page.title, and then return the HTML string.
return template.replace(new RegExp('{{pageTitle}}', 'gi'), page.title)
}
var template = document.getElementById('tmpl').innerHTML; // cache the HTML
function createPageSettingsPopup(page) {
// Find an replace the {{pageTitle}} with page.title, and then return the HTML string.
return template.replace(new RegExp('{{pageTitle}}', 'gi'), page.title)
}
console.log(
createPageSettingsPopup({title:'Hello World'})
);
<script type="text/template" id="tmpl">
<div class="form-group">
<label for="page-title">Title</label>
<input type="text" class="form-control" id="page-title" value="{{pageTitle}}">
</div>
</script>
The above is a minimal example of a template engine, but there are great ones like mustache, handlebars.js, and pug.js
Assuming ES6 you can use backticks:
return `<div>
...
</div>`;
Or have a look at react, to manipulate your DOM, they use jsx which is really nice:
const element = <h1>Hello, world!</h1>;
In case you are using jQuery, sometimes you can do things like these:
var div = $('div').addClass('form-group');
div.append($('label').attr('for', 'page-title').text('Title');
...
Depending on your problem at hand it might also make sense to have the full html structure written up-front and then just manipulate some content and styling using js. In your example:
$('div#title').show();
$('div#title').find('label.page-title').text('Title');
You can try creating a HTML utility that creates elements, add necessary properties and the returns element.
I have created a small implementation of this utility in sample. Benefit of this is you can modify this utility to work with JSON based structure to create dynamic HTML.
Sample
function createPageSettingsPopup(page) {
var divParams = {
class: 'form-group'
}
var labelParams = {
for: 'page-title'
}
var inputParams = {
type: 'text',
class: "form-control",
id: 'page-title',
value: page.title
}
var div = utils.createMyElement('div', '', divParams);
var label = utils.createMyElement('label', 'Title', labelParams)
var input = utils.createMyElement('input', '', inputParams)
div.appendChild(label);
div.appendChild(input);
document.body.appendChild(div)
}
window.addEventListener('load', function() {
createPageSettingsPopup({
title: "foo"
})
})
// This code can be exported to another file
var utils = (function() {
function createMyElement(type, htmlString, params) {
var el = document.createElement(type);
if (htmlString)
el.innerHTML = htmlString;
addProps(el, params)
return el;
}
function addProps(el, props, key) {
if (Object.keys(props).length) {
for (var k in props) {
if (typeof(props[k]) === "object") {
addProps(el, props[k], k);
} else {
if (key) {
el[key][k] = props[k]
} else {
el[k] = props[k]
}
}
}
}
}
return {
createMyElement: createMyElement
}
})()
You can also try JSON based form.
Sample
JSFiddle
window.addEventListener('load', function() {
createPageSettingsPopup({
title: "foo"
})
})
function createPageSettingsPopup(page) {
var form = utils.createForm(getFormData(page))
document.body.appendChild(form)
}
// This can be stored in JSON file or in db and then can be fetched
function getFormData(page) {
var json = {
type: "div",
params: {
class: 'form-group',
innerHTML: "",
},
children: [{
type: 'label',
params: {
for: 'page-title',
innerHTML: "Title"
},
}, {
type: 'input',
params: {
type: 'text',
class: "form-control",
id: 'page-title',
value: page.title
}
}]
}
return json
}
// This is a generic utility and can be exported to a utility file
var utils = (function() {
function JSONBasedForm(form_json) {
var el = "";
if (form_json) {
el = createMyElement(form_json.type, form_json.params);
if (form_json.children && form_json.children.length > 0) {
form_json.children.forEach(function(child) {
var c_el = JSONBasedForm(child)
c_el && el.appendChild(c_el)
})
}
}
return el;
}
function createMyElement(type, params) {
var el = document.createElement(type);
addProps(el, params)
return el;
}
function addProps(el, props, key) {
if (Object.keys(props).length) {
for (var k in props) {
if (typeof(props[k]) === "object") {
addProps(el, props[k], k);
} else {
if (key) {
el[key][k] = props[k]
} else {
el[k] = props[k]
}
}
}
}
}
return {
createForm: JSONBasedForm
}
})()
This does not look better but is another way to create elements in JavaScript
Using the document.createElement you have more programmatic control over which attributes to set
function createPageSettingsPopup(page) {
var div = document.createElement("div");
div.className="form-group";
var label = document.createElement("label");
label.htmlFor="page-title";
label.textContent="Title";
var input = document.createElement("input");
input.type="text";
input.className="form-control";
input.id="page-title";
input.value=page.title;
label.appendChild(input);
div.appendChild(label);
return div;
}
Same in jQuery:
function createPageSettingsPopup(page) {
var $div = $("<div />",{"class":"form-group"});
$div.append(
$("<label />", {"htmlFor":"page-title").text("Title").append(
$("<input/>", { "type":"text","class":"form-control","id":"page-title"}).val(page.title)
)
);
return $div;
}

createElement function not working with MAC Safari

I have below code to create Drop-down:
Below code for HTML to render Drop-down as:
<input class="form-control selectedTextBox mobile-space" list="rider" type="text" id="ridername" placeholder="FirstName LastName"
data-bind="trimedValue: item().Name, datalist: {
options: app.viewModel.riderProfiles(),
optionsValue: 'Id',
optionsText: 'Name',
value: app.viewModel.selectedRiderId
}" />
Below is Knockout.js handler:
ko.bindingHandlers.datalist = (function () {
function getVal(rawItem, prop) {
var item = ko.unwrap(rawItem);
return item && prop ? ko.unwrap(item[prop]) : item;
}
function findItem(options, prop, ref) {
return ko.utils.arrayFirst(options, function (item) {
return ref === getVal(item, prop);
});
}
return {
init: function (element, valueAccessor, allBindingsAccessor) {
var setup = valueAccessor(),
textProperty = ko.unwrap(setup.optionsText),
valueProperty = ko.unwrap(setup.optionsValue),
dataItems = ko.unwrap(setup.options),
myValue = setup.value,
koValue = allBindingsAccessor().value,
datalist = document.createElement("DATALIST");
// create an associated <datalist> element
datalist.id = element.getAttribute("list");
document.body.appendChild(datalist);
// when the value is changed, write to the associated myValue observable
function onNewValue(newVal) {
var dataItems = ko.unwrap(setup.options),
selectedItem = findItem(dataItems, textProperty, newVal),
newValue = selectedItem ? getVal(selectedItem, valueProperty) : void 0;
if (ko.isWriteableObservable(myValue)) {
myValue(newValue);
}
}
// listen for value changes
// - either via KO's value binding (preferred) or the change event
if (ko.isSubscribable(koValue)) {
koValue.subscribe(onNewValue);
} else {
ko.utils.registerEventHandler(element, "change", function () {
onNewValue(this.value);
});
}
// init the element's value
// - either via the myValue observable (preferred) or KO's value binding
if (ko.isObservable(myValue) && myValue()) {
element.value = getVal(findItem(dataItems, valueProperty, myValue()), textProperty);
} else if (ko.isObservable(koValue) && koValue()) {
onNewValue(koValue());
}
},
update: function (element, valueAccessor) {
var setup = valueAccessor(),
datalist = element.list,
dataItems = ko.unwrap(setup.options),
textProperty = ko.unwrap(setup.optionsText);
// rebuild list of options when an underlying observable changes
datalist.innerHTML = "";
ko.utils.arrayForEach(dataItems, function (item) {
var option = document.createElement("OPTION");
option.value = getVal(item, textProperty);
datalist.appendChild(option);
});
ko.utils.triggerEvent(element, "change");
}
};})();
And with only MAC(OS) & Safari (Web-Browser) below error message is shown as:
Message: Unable to process binding "datalist: function () {return { options:app.viewModel.riderProfiles(),optionsValue:'Id',optionsText:'Name',value:app.viewModel.selectedRiderId} }"
Message: undefined is not an object (evaluating 'u.innerHTML=""')
The HTML element contains a set of elements that represent the values available for other controls.
But "Safari" browser does not support it.

knockout binding handler - undefined bindingContext.$data on IE9

I am trying to create a custom binding handler to apply a role based access to fields on page.
In custom handler it will check values of other observables from viewModel and based on condition it will enable or disable input control.
But I am not able to get current ViewModel through bindingContext.$parent , $root, $data or $rawData.
when I debug on IE it only displays {...}
The issue occurs only on IE, It works fine on google crome.
Can someone help me on how do I get current ViewModel through bindingContext.
var divForm = document.getElementById('divform');
function AuditFormViewModel() {
self = this;
self.Name = ko.observable("Kiran");
self.Email = ko.observable("kiranparab0#gmail.com");
self.Complete = ko.observable(true);
self.Send = ko.observable(false);
self.conditionArray = [
{ Field: "Name", condition: [{ Property: "Complete", Value: true }, { Property: "Send", Value: true }] },
{ Field: "Email", condition: [{ Property: "Complete", Value: false }] }
];
}
ko.bindingHandlers.hasAccess = {
update: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
var field = valueAccessor();
var accessConditions = [];
accessConditions = bindingContext.$data.conditionArray;
var condition = accessConditions.filter(function (cnd) {
return cnd.Field === field
})[0].condition;
var value = true;
for (var i = 0; i < condition.length; i++) {
var cndProp = condition[i].Property;
var cndVal = bindingContext.$data[cndProp]();
value = value && (condition[i].Value === cndVal);
}
ko.bindingHandlers.enable.update(element, function () { return value });
}
};
auditFormViewModel = new AuditFormViewModel();
ko.applyBindings(auditFormViewModel, divForm);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.1.0/knockout-min.js"></script>
<div id="divform">
Name : <input type="text" data-bind="value:Name, hasAccess:'Name'" />
<br />
Email : <input type="text" data-bind="value:Email, hasAccess:'Email'" />
</div>
Here is the fiddle
In my code I have not declared self as var,
its just change of line. 'var self = this' instead 'self = this'

Trying to appending list items to dynamic id's

Hi I'm trying to add "children" list items to "family" lists. I have a first form to create the families and a second form which is a select box to create the children and add them to a selected family from the select box. The Ol's ids are dynamically created and the drop down's options dynamically created.
When an option is created, the values are incremented every time a family and option is made.
My plan is to reference the OL families by using the options, basically corresponding to the family by the time it is created.
For example when I make a family1, there will be an option created with that family1 with a value 1, and when family2 is created, an option 2 is created with a value of 2.
I'm trying to append children to the family but I have no idea how to reference the OL's ids
this is what I have so far
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="mystyle.css">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js">
</script>
<script>
$(document).ready(function () {
var i = 1;
$(document).on('click', "#submit", function () {
var str = ' '
var family = document.getElementById('famname').value;
$('#family input[type = text],input[type = text],input[type = text],input[type = text]').each(function () {
str = str + $(this).val() + ' ';
$(this).val('');
});
$("<ol>", {
text: str,
id: "family+" + i
}).appendTo("#container").append($('<button />', {
'class': 'btn2',
text: 'Remove'
}));
$('#select').append($('<option />', {
text: family,
value: i
}));
i++;
});
// Append items functions
$(document).on('click', "#submit2", function () {
var child = prompt("Please enter the child you want to add");
var e = document.getElementById("select");
var str = e.options[e.selectedIndex].value;
$('<li>', {
text: child
}).appendTo( ? ? ? ).append($('<button />', {
'class': 'btn',
text: 'Remove'
}))
});
//delete items functions
$(document).on('click', '.btn', function () {
$(this).closest('li').remove();
});
// delete the list
$(document).on('click', '.btn2', function () {
$(this).parent().next().remove();
$(this).closest('ol').remove();
});
});
</script>
</head>
<body>
<form id ="family" method = "post" target="_parent">
Enter Family Name <input type = "text" name = "famname" id = "famname" > <br>
Enter Address <input type = "text" name = "address"> <br>
Enter Father's name <input type = "text" name = "dad"> <br>
Enter Mother's name<input type = "text" name = "mom"> <br>
<input id="submit" type="button" value="Submit" name="submit">
</form>
<p>Select Which family you want to add a child to</p>
<form id = "child">
<select id ="select">
</select>
<input id="submit2" type="button" value="Submit" name="submit">
</form>
<div id = "container">
</div>
</body>
</html>
Tip and help are appreciated
http://jsfiddle.net/Jnewguy/4LVTz/
Try
$(document).ready(function () {
var i = 1;
var $famname = $('#famname');
var $finputs = $('#family input[type = text]').not('#famname');
$(document).on('click', "#submit", function () {
var family = $famname.val();
var $ol = $("<ol>", {
id: "family-" + i
}).appendTo("#container");
$('<li />', {
text: family
}).append($('<button />', {
'class': 'btn2',
text: 'Remove'
})).appendTo($ol);
$finputs.each(function () {
$('<li />', {
text: this.value
}).appendTo($ol);
$(this).val('');
});
$('#select').append($('<option />', {
text: family,
value: i
}));
i++;
});
// Append items functions
var $select = $('#select')
$(document).on('click', "#submit2", function () {
var child = prompt("Please enter the child you want to add");
$('<li>', {
text: child
}).appendTo('#family-' + $select.val()).append($('<button />', {
'class': 'btn',
text: 'Remove'
}))
});
//delete items functions
$(document).on('click', '.btn', function () {
$(this).closest('li').remove();
});
// delete the list
$(document).on('click', '.btn2', function () {
$(this).parent().next().remove();
$(this).closest('ol').remove();
});
});
Demo: Fiddle

Categories

Resources