I am using the javascript lib Tom-Select. I would like to limit the display of how many items have been selected. I do not want to limit the number of actual choices - only how many are displayed. Standard functionality shows all selections in a growing box. I would like to set a limit of 3. Then if a user selects more than 3 the box will no longer grow but simply say 4 items selected (or 5, 6, etc). Bonus points if I could limit selections by the element width instead of a count (forcing the element to always remain on one line of the form).
You could trick using the render method and the items.length array, but how will you then let your users delete their own choices as you don't display the selected items?
render: {
option: function (data, escape) {
return '<div class="d-flex"><span>' + escape(data.text) + '</span></div>';
},
item: function (data, escape) {
//return '<span class="tag is-info mb-1 mr-1">' + escape(data.text) + '</span>';
if (this.items.length >= 3){
return '<span class="tag is-info mb-1 mr-1" style="display:none">' + escape(data.text) + '</span>';
}else{
return '<span class="tag is-info mb-1 mr-1">' + escape(data.text) + '</span>';
}
}
}
Related
I'm having trouble inserting child elements to a list in HTML using jQuery. I'm a beginner when it comes to anything JavaScript related so my code is probably very clunky and unoptimized. Even if you can't help me with the original issue I always appriciate pointers to improve my code and learn.
I have a function that runs every second (it's called from my CefSharp application, not sure if it's relevant). This is my function:
function updateOrderQueue(index, reference, pickedup, deliveredto, robotId, statuscode, statustext) {
var list = $('#orderQueueList');
var count = $("#orderQueueList li").length;
if (!document.getElementById('order' + reference) && statuscode < 4 && count < 10) {
var data = $(`<li class="list-group-item d-flex justify-content-between align-items-center border border-dark" id="order` + reference + `">
<h3 class="mt-1" id="text`+ reference + `">
[`+index+`] `+ pickedup + ` ➔ ` + deliveredto + ` (` + statustext + `)
</h3>
<small class="badge badge-dark rounded" style="font-size:24px;" id="robotQueue`+ reference + `">` + robotId + `</small>
</li>`);
list.append(data).hide().fadeIn(500);
} else {
var locations = jQuery("#text" + jq(reference));
var assignedrobot = jQuery("#robotQueue" + jq(reference));
locations.text('['+index+'] '+ pickedup + ' ➔ ' + deliveredto + ' (' + statustext + ')');
assignedrobot.text(robotId);
}
var elems = $('#orderQueueList li').detach().sort(function (a, b) {
if (isNaN($(a).text()) || isNaN($(b).text())) {
return $(a).text() > $(b).text() ? 1 : -1;
}
return $(a).text() - $(b).text();
});
list.append(elems);
}
And this is the HMTL code
<div id="orderQueue" class="">
<ul class="list-group" id="orderQueueList">
</ul>
</div>
I'm going to try to quickly explain what I intend on it doing and what it actually does.
So basically, this code is supposed to add, sort and add relevant classes to items in a list. To be added, the item needs to pass a set of requirements (not already added, statuscode under 4 and not more than 10 items total). I then append the item into the list, which I assumed would put it at the end of the list (compared to prepend, which should do the opposite).
The problem is, whenever a new item is added, it is added to the top of the list. My guess is that is has something to do with the fact that I'm appending it to the ul. I tried doing something like $('#orderQueueList li:last') but I didn't get it to work. I've also tried to do $('#orderQueueList).children() and $('#orderQueueList).last() before appending without success.
What am I missing? Have I completely misunderstood what append does? Have I written the code wrong?
Appriciate any pointers,
Thank you in advance.
The issue here is not the .append() but the .sort() which comes after.
Sorting text is affected by whitespace, so the solution is to .trim() the text when doing a text comparison:
$("ul").append("<li>new</li>").hide().fadeIn(500);
var elems = $('ul li').detach().sort(function(a, b) {
if (isNaN($(a).text()) || isNaN($(b).text())) {
return $(a).text().trim() > $(b).text().trim() ? 1 : -1;
}
return $(a).text() - $(b).text();
});
$("ul").append(elems);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul>
<li> 2</li>
<li>1555</li>
<li>xyz</li>
<li>3</li>
<li>
abc
</li>
</ul>
I'm using jQuery 3 and Laravel 5.8.
I've tried so many times for looping data and the result is wrong.
I've done in show.blade.php, and there's nothing wrong. But, when I loop using jQuery it makes the result different.
// Get Project by po id
$('#product_owner').on('change', function() {
let product_owner_val = $(this).val()
if(product_owner_val) {
$.ajax({
url: 'project/get/project-po/' + product_owner_val,
type: 'GET',
dataType: 'json',
beforeSend: function() {
$('#loading-project').show()
},
success: function(projects) {
$('#loading-project').hide()
$('#project-list').empty()
if(projects.length) {
$.each(projects, function(key, project) {
// Project Not found Message
$('#project-not-found').hide()
$('#project-list').append('<div class="box box-solid box-primary"><div class="box-header"><div class="box-title pull-left">' + project.name + ' - ' + project.project_code + '</div><div class="pull-right"><span class="fa fa-eye"></span><span class="fa fa-edit"></span><span class="fa fa-plus"></span><span class="fa fa-circle"></span>Collapsed Button</div></div><div class="box-body">' + $.each(project.project_sprint, function(key, sprint) { }) + '</div></div>')
})
// Append Sprint in each project
} else {
$('#project-not-found').fadeIn()
}
}
})
} else {
$('#project-list').empty()
$('#project-not-found').hide()
}
})
So, in this case. I want to fetch data from the projects table based on 'Product Owner [ID]'. but when I fetch the data to view, its return projects list based on Product Owner. But, here, I have sprints table that attached to projects, it's many to many relations. and Target Table that has project_id and sprint_id. The point is, I want to fetch projects based on sprints that attached to the project.
assuming you're fetching the right data from the model you can create a loop in the loop to list the project_sprint and append the inner HTML with the sprint names. You can do something like below:
$.each(projects, function(key, project) {
$('#project-list').append('<div class="box box-solid box-primary">
<div class="box-header">
<div class="box-title pull-left">' + project.name + ' - ' + project.project_code + '</div>
<div class="pull-right"><a href="/dev/projects/' + project.id + '" id="btn-show" class="btn btn-success"
title="Detail Project ' + project.name + '"><span class="fa fa-eye"></span></a><a
href="/dev/logbook/edit-project/' + project.id + '" class="btn btn-warning" id="btn-modal-show"
title="Edit Project ' + project.name + '"><span class="fa fa-edit"></span></a><a
href="logbook/add-new-sprint-to-project/' + project.id + '" class="btn btn-primary" id="btn-modal-show"
title="Add Sprint to project ' + project.name + '"><span class="fa fa-plus"></span></a><a
href="/dev/logbook/add-target/' + project.id + '" class="btn btn-danger" id="btn-modal-show"
title="Add new target to ' + project.name + '"><span class="fa fa-circle"></span></a>Collapsed Button
</div>
</div>
<div id="srpint_div_'+project.id+'" class="box-body"></div>
</div>')
$.each(project.project_sprint, function(key2, value) {
$('#sprin_div_'+project.id).append(value.name)
})
})
I'm wondering if it is possible to show specific JSON data, based on a choice made with a checkbox.
At this moment I have specific JSON data with peoples names, department, sex and internet usage.
I've managed to show this data by name, department and I drew a bar for the usage.
I've made two checkboxes that show this JSON data when checked. However at this particular moment they still show the same data. I need to find a way to sort it out, after trying certain things, it's kinda getting lost on me.
This is the code for getting the JSON data, showing it and the checkboxes + the code behind it:
JS :
$(document).ready(function() {
$('input[type="checkbox"]').click(function(){
if($(this).attr("value")=="female") {
$(".female").toggle();
}
if($(this).attr("value")=="male") {
$(".male").toggle();
}
});
});
ajax();
function ajax() {
$.ajax({
url:"wifi_data.json",
dataType:"json",
success: function(data) {
//if(data.geslacht == "man") {
$.each(data, function(index, item) {
$('#females').append('<br>Name: ' + data[index].voornaam + ' ' + data[index].achternaam +
'<br>Department: ' + data[index].afdeling +
'<br>Usage: ' + '<div style="width:'+
data[index].verbruik*0.001
+'px;height:10;border:2px solid ; background-color:red;"></div>' +
'<div style="'+ '<br>Sex: ' +
data[index].geslacht +'"></div>' );
});
//}
//if(data.gelsacht =="vrouw") {
$.each(data, function(index, item)
{
$('#males').append('<br>Name: ' + data[index].voornaam + ' ' + data[index].achternaam +
'<br>Department: ' + data[index].afdeling +
"<br>Usage: " + '<div style="width:'+
data[index].verbruik*0.001
+'px;height:10;border:2px solid ; background-color:red"></div>' );
});
//}
}
})
}
HTML :
<div>
<label><input type="checkbox" name="colorCheckbox" value="female"> Show Female Members</label>
<br>
<label><input type="checkbox" name="colorCheckbox" value="male"> Show Male Members</label>
</div>
<div class="female box">
<div id="females"></div>
</div>
<div class="male box">
<div id="males"></div>
</div>
What I've tried to do here with the commented parts of the code (and some code that is removed) is to only append males when "geslacht"(sex) is man(male) or vrouw(female).
I've also tried to append the sex and to then put it in a div and hide it. In order to maybe check if male or female is checked, then show the hidden div's with all the men or females in it.
My issue is that I'm having a huge brain fart on the part how to check what is checked, and then only gather the males or females from the JSON file based on choice.
The JSON looks like this:
{
"46": {
"voornaam": "Sergio",
"achternaam": "Bloemenouw",
"verbruik": "100000",
"afdeling": "FHACI",
"geslacht": "man",
"verbruikPercentage": "18.2%"
},
"25": {
"voornaam": "Chayenne",
"achternaam": "Aalberink",
"verbruik": "200000",
"afdeling": "FHEHT",
"geslacht": "vrouw",
"verbruikPercentage": "36.4%"
},
and so on...
Hopefully someone can steer me into the correct direction.
I think the most important thing I need to figure out is how to only show either the females or the males including their name, usage("verbruikPercentage") and department("afdeling")
Your question contains two problems. One is to separate data into male and female parts, and the other one is about showing/hiding the elements.
For the data separation, you need to do the if statement for each data item, since it is the item that contains the geslacht property.
$.each(data, function(index, item) {
var target = item.geslacht === 'man' ? $('#males') : $('#females');
target.append('<br>Name: ' + data[index].voornaam + ' ' + data[index].achternaam +
'<br>Department: ' + data[index].afdeling +
'<br>Usage: ' + '<div style="width:'+ data[index].verbruik*0.001 +
'px;height:10;border:2px solid ; background-color:red;"></div>' +
'<div style="'+ '<br>Sex: ' + data[index].geslacht +'"></div>');
});
For the toggling, I didn't see anything wrong for now. You can focus on the data processing part for now, and do more debugging on it later.
Your if condition should be inside $.each() loop as follows:
$.each(data, function(index, item) {
if(item.geslacht == "man") {
}
else if(item.geslacht == "vrouw")
{
}
})
I learned about how to customizing ng-grid's aggregation when grouping a grid here.
Now - what If I would want a summary per column (value1, value2, value3) shown in the aggregateTemplate when grouping on groupName if my table looks like the below?
col1;value1;value2;value3;groupName
"first";10;20;30;"group 1"
"second";10;20;30;"group 1"
"third";10;20;30;"group 2"
"fourth";10;20;30;"group 3"
"fifth";10;20;30;"group 3"
Of course I could do this one after the other in the aggregateTemplate calculateChildren function, but its important that the columns and its summaries are aligned. So basically what I'm looking for is for the template to generate the same number of "cells" and add a summary per column in that cell.
Here is a sample plunker: http://plnkr.co/edit/f8patmHudM5PSRyEy6gW?p=preview
I managed to find a solution by writing my own aggregationTemplate. The template now looks like the following:
aggregateTemplate:
"<div onmouseover='mouseOver()'; style='display: inline-block;' ng-click=\"row.toggleExpand()\" ng-style=\"rowStyle(row)\" class=\"ngAggregate\">" +
" <span style='margin-left: -22px' class=\"ngAggregateText\">" +
" <div ng-class=col.colIndex() style='display: inline-block' ng-repeat='col in renderedColumns'><span>{{col.index > 1 ? (aggFunc(row,col) | SwedishCurrency) : (col.index == 0 ? ' ' : row.label)}}</span></div>" +
" </span>" +
" <div class=\"{{row.aggClass()}}\"></div>" +
"</div>" +
""
Here is the aggregation function
$scope.aggFunc = function(row, col) {
var total = 0;
angular.forEach(row.children, function(cropEntry) {
total += parseFloat(cropEntry.entity[col.field]);
});
return total;
};
I got the cells aligned using the following ng-repeat (extract from template definition above):
ng-repeat='col in renderedColumns'
sometimes my javascript code is mixing with html and css. in this stuation, my code is beind unreadable. How do you separate the javascript and html side in javascript?
For example: (using javascript dojo toolkit)
addLayer: function (layer, index) {
var layerClass = layer.visible === true ? 'layer checked' : 'layer';
var html = '';
html += '<li class="' + layersClass + '">';
html += '<div class="cover"></div>';
html += '<span tabindex="0" class="info" title="MyTitle"></span>';
html += '<span tabindex="0" class="toggle box"></span>';
html += '<div class="clear"></div>';
html += '</li>';
var node = dom.byId('layersList');
if (node) {
domConstruct.place(html, node, "first");
HorizontalSlider({
name: "slider",
value: parseFloat(layer.opacity),
minimum: 0,
maximum: 1,
showButtons: false,
discreteValues: 20,
intermediateChanges: true,
style: "width:100px; display:inline-block; *display:inline; vertical-align:middle;",
onChange: function (value) {
layer.setOpacity(value);
}
}, "layerSlider" + index);
if (!this.layerInfoShowClickHandler) {
this.layerInfoShowClickHandler = on(query(".listMenu"), ".cBinfo:click, .cBinfo:keyup", this._onLayerInfoShowIconClicked);
}
}
}
In this stuation, my code is adding html to view side dynamically. Adding event handlers to created html code. Adding additional tools(HorizantalSlider) same time.
This workflow is binded one to another. This code is unreadable. Is there a way to solve this with clean code?
This answer uses Dojo to split your HTML + CSS from JavaScript.
HTML template
The recommended approach is by defining your HTML template in a seperate HTML file. For example:
<li class="{layersClass}">
<div class="cover"></div>
<span tabindex="0" class="info" title="MyTitle"></span>
<span tabindex="0" class="toggle box"></span>
<div class="clear"></div>
</li>
Also notice the replacement of layersClass by a placeholder.
Load the HTML template
Now, to load the template you use the dojo/text plugin. With this plugin you can load external templates, for example by using:
require(["dojo/text!./myTemplate.html"], function(template) {
// The "template" variable contains your HTML template
});
Converting the placeholders
To replace {layersClass}, you can use the replace() function of the dojo/_base/lang module. Your code would eventually look like:
require(["dojo/text!./myTemplate.html", "dojo/_base/lang"], function(myTemplate, lang) {
var html = lang.replace(myTemplate, {
layersClass: layersClass
});
});
This would return exactly the same as your html variable, but seperated the HTML from your JavaScript code.
Seperate CSS
To seperate the CSS style from your HorizontalSlider you could define an id property and just put your CSS in a seperate CSS file. Your HorizontalSlider would become:
HorizontalSlider({
name: "slider",
value: parseFloat(layer.opacity),
minimum: 0,
maximum: 1,
showButtons: false,
discreteValues: 20,
intermediateChanges: true,
id: "mySlider",
onChange: function (value) {
layer.setOpacity(value);
}
}, "layerSlider" + index);
Now you can use the following CSS:
#mySlider {
width:100px;
display:inline-block;
*display:inline;
vertical-align:middle;
}
You could store the html variable in a different place, let's say in a file called template.js. However, doing so you can't concatenate the HTML string immediately since you need to inject this layersClass variable. Here is a possible workaround :
// template.js
var template = function () {
return [
'<li class="', this.layersClass, '">',
'<div class="cover"></div>',
'<span tabindex="0" class="info" title="MyTitle"></span>',
'<span tabindex="0" class="toggle box"></span>',
'<div class="clear"></div>',
'</li>'
].join('');
};
// view.js
html = template.call({
layersClass: layerClass
});
Effective and easy to use. However, if you want to use a template in the form of a string rather than a function, you'll need a template parser. The following one will give the same kind of result as above (notice that regex capturing is not supported by IE7 split()) :
function compile(tpl) {
tpl = Array.prototype.join.call(tpl, '').split(/{{(.*?)}}/);
return Function('return [' + tpl.map(function (v, i) {
if (i % 2) return 'this["' + v + '"]';
return v && '"' + v.replace(/"/g, '\\"') + '"';
}).join(',') + '].join("");');
}
Usage example :
var template = '<b>{{text}}</b>';
var compiled = compile(template);
// compiled -> function () {
// return ["<b>",this["text"],"</b>"].join("");
// }
var html1 = compiled.call({ text: 'Some text.' });
var html2 = compiled.call({ text: 'Bold this.' });
// html1 -> "<b>Some text.</b>"
// html2 -> "<b>Bold this.</b>"
Now, let's see how you could use this tool to organize your files in a clean way.
// product.data.js
product.data = [{
name: 'Apple iPad mini',
preview: 'ipadmini.jpeg',
link: 'ipadmini.php',
price: 280
}, {
name: 'Google Nexus 7',
preview: 'nexus7.jpeg',
link: 'nexus7.php',
price: 160
}, {
name: 'Amazon Kindle Fire',
base64: 'kindlefire.jpeg',
link: 'kindlefire.php',
price: 230
}];
// product.tpl.js
product.tpl = [
'<div class="product">',
'<img src="{{preview}}" alt="{{name}}" />',
'<span>{{name}} - ${{price}}</span>',
'details',
'</div>'
];
// product.view.js
var html = [];
var compiled = compile(product.tpl);
for (var i = 0, l = product.data.length; i < l; i++) {
html.push(compiled.call(product.data[i]));
}
document.getElementById('products').innerHTML = html.join('');
Demo : http://jsfiddle.net/wared/EzG3p/.
More details here : https://stackoverflow.com/a/20886377/1636522.
This compile function might not be enough for your needs, I mean, you might quickly need something more powerful that includes conditional structures for example. In this case, you may take a look at Mustache, Handlebars, John Resig or Google "javascript templating engine".
Consider using templates for building dynamic view elements
E.g:
http://handlebarsjs.com/
http://underscorejs.org/#template
One way is to use HTML (not that cool if you don't like to split your logic):
<div style="display:none" id="jQ_addLayer">
<div class="cover"></div>
<span tabindex="0" class="info" title="MyTitle"></span>
<span tabindex="0" class="toggle box"></span>
<div class="clear"></div>
</div>
than in jQuery create the LI with the passed variable and insert your #jQ_addLayer content:
var html = '<li class="'+layersClass+'">'+ $("#jQ_addLayer").html() +'</li>';
Another way is to escape your string newlines:
var html = '\
<li class="' + layersClass + '">\
<div class="cover"></div>\
<span tabindex="0" class="info" title="MyTitle"></span>\
<span tabindex="0" class="toggle box"></span>\
<div class="clear"></div>\
</li>'; //don't forget to escape possible textual single-quotes in your string