I am attempting to create a datatable where the user can filter the table using dropdown menus. The first dropdown menu "project_select" fills with all the unique values from a column of the data table. The second dropdown menu "hr_select" fills with values based on the user's selection from the "project_select" dropdown menu.
Currently, the dropdown menus are mapped to span elements in the html. I am looking to convert these span elements to select2.
This is my desired html code:
<label for="project_select"></label><select id="project_select" class="js-example-basic-single" style="width: 10%">
<option></option>
</select>
<label for="hr_select"></label><select id="hr_select" class="js-example-basic-multiple" multiple="multiple" style="width:15%">
<option></option>
</select>
However, when I try and replace the span elements with that desired HTML code.. it doesn't work.
This is my code: https://jsfiddle.net/dfahsjdahfsudaf/nL6q21g9/16/
Thanks in advance for any help!
I converted my comments to an answer for future visitors to this question:
The steps needed to convert your standard selects to "Select2" selects are:
Add the required libraries to the page:
<link href="https://cdn.jsdelivr.net/npm/select2#4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/select2#4.1.0-rc.0/dist/js/select2.min.js"></script>
Give IDs to each of the current <select> elements:
select = $('<select id="project_s2"><option value=""></option></select>');
and:
select = $('<select id="hr_s2" multiple><option value=""></option></select>');
Use those IDs to initialize Select2:
$('#project_s2').select2();
$('#hr_s2').select2();
The end result is:
var dataSet = [{
"Project_Name": "A",
"ID": "65",
"HR": "0",
},
{
"Project_Name": "A",
"ID": "65",
"HR": "10",
},
{
"Project_Name": "B",
"ID": "65",
"HR": "0",
},
{
"Project_Name": "B",
"ID": "65",
"HR": "20",
},
{
"Project_Name": "C",
"ID": "65",
"HR": "20",
},
{
"Project_Name": "C",
"ID": "65",
"HR": "100",
},
];
$(document).ready(function() {
var table = $('#example').DataTable({
data: dataSet,
orderCellsTop: true,
columns: [{
data: "Project_Name"
},
{
data: "ID"
},
{
data: "HR"
}
],
initComplete: function() {
this.api().columns([0, 2]).every(function() {
var column = this;
var colIdx = column.index();
var node;
var select;
if (colIdx === 0) {
node = $('#project_select');
select = $('<select id="project_s2" style="width: 40%"><option value=""></option></select>');
} else {
node = $('#hr_select');
select = $('<select id="hr_s2" style="width: 20%" multiple><option value=""></option></select>');
}
select.appendTo($(node).empty())
.on('change', function() {
var val = $(this).val();
if (colIdx === 0) {
val = $.fn.dataTable.util.escapeRegex(val);
column.search(val ? '^' + val + '$' : '', true, false).draw();
rebuildPositionSelect();
} else {
var vals = val.map(x => $.fn.dataTable.util.escapeRegex(x)).join('|');
column.search(vals ? '^' + vals + '$' : '', true, false).draw();
}
});
column.data().unique().sort().each(function(val) {
select.append('<option value="' + val + '">' + val + '</option>')
});
});
$('#project_s2').select2();
$('#hr_s2').select2();
}
});
function rebuildPositionSelect() {
var select = $('#hr_select select').empty().append('<option value=""></option>');
var column = table.column(2, {
search: 'applied'
});
column.search('').draw();
column.data().unique().sort().each(function(val) {
select.append('<option value="' + val + '">' + val + '</option>');
});
}
});
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Demo</title>
<script src="https://code.jquery.com/jquery-3.5.1.js"></script>
<script src="https://cdn.datatables.net/1.10.22/js/jquery.dataTables.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.22/css/jquery.dataTables.css">
<link rel="stylesheet" type="text/css" href="https://datatables.net/media/css/site-examples.css">
<link href="https://cdn.jsdelivr.net/npm/select2#4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/select2#4.1.0-rc.0/dist/js/select2.min.js"></script>
</head>
<body>
<div style="margin: 20px;">
<div>
<span>Project: </span>
<span id="project_select"></span>
<span> HR: </span>
<span id="hr_select"></span>
</div>
<br><br>
<table id="example" class="display dataTable cell-border" style="width:100%">
<thead>
<tr>
<th>Project_Name</th>
<th>ID</th>
<th>HR</th>
</tr>
</thead>
</table>
</div>
</body>
</html>
As mentioned in the comments, this is a very basic set-up for each Select2 drop-down.
To customize them further you can provide options - for example:
$('#project_s2').select2( {
theme: "classic",
placeholder: 'Select an option'
} );
Final Note:
The use of basic <span> elements in the above example, only minimal layout/styling may be insufficient for your needs. So, you will probably want to provide CSS customizations which provide a better overall layout. But those changes should not affect your Select2 elements.
For example, adding in style="width: 40%" (as shown above) makes a difference - but that may not be what you want, in your specific case.
Related
I try to create the individual search field with this code.
<html>
<head>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
</head>
<body>
<h1>DataTables Server Side</h1>
Seacrch specific column : <input id="column3_search" type="text"/>
<table id="example" class="display nowrap" style="width:100%; background-color: black; color: white;">
<thead>
<tr>
<th>Column A</th>
<th>Column B</th>
<th>Column C</th>
<th>Column D</th>
</tr>
</thead>
</table>
</body>
<!-- DataTable Pagination-->
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/v/bs4/dt-1.10.21/r-2.2.5/datatables.min.css"/>
<script type="text/javascript" src="https://cdn.datatables.net/v/bs4/dt-1.10.21/r-2.2.5/datatables.min.js"></script>
</html>
<script>
$(document).ready(function() {
var table = $('#example').DataTable({
bProcessing: true,
bServerSide: true,
deferRender: true,
sPaginationType: "full_numbers",
lengthMenu: [[10, 25, 50, 100], [10, 25, 50, 100]],
bjQueryUI: true,
sAjaxSource: 'load_table_data',
columns: [
{"data": "Column A"},
{"data": "Column B"},
{"data": "Column C"},
{"data": "Column D"}
]
});
$('#column3_search').on( 'keyup', function () {
table.columns(1)
.search( this.value )
.draw();
} );
} );
</script>
Which i specify the <input id="column3_search" type="text"/> to search field of column index 1, but when i do searching it not do any search just showing the processing for a second and done.
And when i remove the .column() out, and try to searching with this code.
$('#column3_search').on( 'keyup', function () {
table.columns(1)
.search( this.value )
.draw();
} );
It working normally by search all column in the table. So what a problem with this code and how can i fix it.
This is my table_schemas.
SERVERSIDE_TABLE_COLUMNS = [
{
"data_name": "A",
"column_name": "Column A",
"default": "",
"order": 1,
"searchable": True
},
{
"data_name": "B",
"column_name": "Column B",
"default": "",
"order": 2,
"searchable": True
},
{
"data_name": "C",
"column_name": "Column C",
"default": 0,
"order": 3,
"searchable": True
},
{
"data_name": "D",
"column_name": "Column D",
"default": 0,
"order": 4,
"searchable": True
}
]
You need to define the target and searchable property in column property.
columns: [
{"data": "Column A","targets":0,"searchable":true},
{"data": "Column B","targets":1,"searchable":true},
{"data": "Column C","targets":2,"searchable":true},
{"data": "Column D","targets":3,"searchable":true}
]
$("#column3_search").on("input", function (e) {
table.columns(1).search($(this).val()).draw();
});
If you want to display only it on your datatable you should add a filter and then draw :
$('#column3_search').on('keyup', function() {
let string = $(this).val();
table.columns(1).data().filter(function(value, index) {
return value === string ? true : false;
}).draw();
});
I need to access the values of "city" and "nation" inside the array the following json file using AngularJS with ng-repeat.
This is my Json file:
[
{
"name": "first",
"location": [
{
"city" : "milan",
"nation" : "italy"
}
],
"visibility": 1
},
{
"name": "second",
"location": [
{
"city" : "new york",
"nation" : "usa"
},
{
"city" : "london",
"nation" : "uk"
}
],
"visibility": 1
}
]
My problem is that I need to get City and Nation as text string values, because I need to add them as css class name inside a tag, basically like this:
<div class="milan italy"></div>
<div class="new york usa london uk></div>
I'm not able to figure it out how to do this.
I tried with this code but nothing is shown:
<div ng-repeat-start="tile in tiles"></div>
<div class="mix {{ tile.location.city }} {{ tile.location.nation }}"></div>
<div ng-repeat-end></div>
Thank you in advance for your suggestions.
As #MattDionis said, you would need to specify ng-class, not just class. What you can try is using a function for ng-class. So you can do
<div ng-repeat="tile in tiles">
<div ng-class="getLocation(tile)"></div>
</div>
$scope.getLocation = function(tile) {
var resp = '';
for (var i = tile.location.length; i-- > 0;) {
resp = resp + tile.location[i].city + ' ' + tile.location[i].nation;
}
return resp;
}
I'm sure there's a better way to combine them than that, but that's what I came up with off-hand
First, you want to use ng-class rather than simply class to properly evaluate those bindings.
Also, tile.location appears to be an array of objects rather than simply an object. Therefore, {{ tile.location.city }} would not work, but {{ tile.location[0].city }} should.
The remaining issue would be how to loop through mutliple city/nation values within ng-class. I'll get back to you on that.
Please see demo below
You can create function to transform your array of object to string ie:
$scope.tostring = function (array) {
var res = "";
angular.forEach(array, function (obj) {
console.log(obj);
for (var k in obj) {
if (obj.hasOwnProperty(k)) {
res += " " +obj[k];
}
}
});
return res;
};
var app = angular.module('app', []);
angular.module('app').controller('homeCtrl', homeCtrl);
homeCtrl.inject = ['$scope'];
function homeCtrl($scope) {
$scope.titles = [{
"name": "first",
"location": [{
"city": "milan",
"nation": "italy"
}],
"visibility": 1
}, {
"name": "second",
"location": [{
"city": "new york",
"nation": "usa"
}, {
"city": "london",
"nation": "uk"
}
],
"visibility": 1
}];
$scope.tostring = function(array) {
var res = "";
angular.forEach(array, function(obj) {
console.log(obj);
for (var k in obj) {
if (obj.hasOwnProperty(k)) {
res += " " + obj[k];
}
}
});
return res;
};
}
<!DOCTYPE html>
<html ng-app="app">
<head>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.2/angular.min.js"></script>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body ng-controller="homeCtrl">
<div ng-repeat="title in titles">
<h3 ng-class="tostring(title.location)">{{title.name}} My class is:*{{tostring(title.location)}}* </h3>
</div>
</body>
</html>
I have this simple XML View:
<core:View xmlns:core="sap.ui.core" xmlns:mvc="sap.ui.core.mvc" xmlns="sap.m"
controllerName="listicons.list" xmlns:html="http://www.w3.org/1999/xhtml">
<Page title="Title">
<content>
<List id="test-list"></List>
</content>
</Page>
</core:View>
In my controller, I call a method to build list items onInit. First of all set some data:
var data = {
"products": [
{
"prodName": "Apple",
"prodCountry": "Netherlands",
"price": "normal"
},
{
"prodName": "Orange",
"prodCountry": "Spain",
"price": "extra"
},
{
"prodName": "Strawberry",
"prodCountry": "Poland",
"price": "normal"
}
]
};
// create a Model with this data and attach it to the view
var model = new sap.ui.model.json.JSONModel();
model.setData(data);
this.getView().setModel(model);
var list = this.getView().byId("test-list");
Then I build the list and bind the items to it:
// bind the List items to the data collection
list.bindItems({
path : "/products",
sorter : new sap.ui.model.Sorter("prodName"),
//template : listTmpl
template : new sap.m.StandardListItem({
title: "{prodName}",
description: "{prodCountry}"
})
});
After I built the list and is alread rendered, I look after which items have an extra price and set an icon for them:
jQuery.each(list.getItems(), function(i, obj) {
if(obj.mProperties.price == "extra") {
obj.setIcon("sap-icon://flag");
}
});
So. Everything works fine. But I am not happy with my solution, because I'd rather like to manipulate the data BEFORE rendering the list. I tried to build a list template directly before binding the items to the list and then use this template like:
var listTmpl = jQuery.each(data.products, function(i, a) {
var lI = new sap.m.StandardListItem({
title: "{prodName}",
description: "{prodCountry}"
});
if(a.price == "extra") {
lI.setIcon("sap-icon://flag");
}
return lI;
});
But then my list is not shown and I got an error in the console, saying
Missing template or factory function for aggregation items of Element sap.m.List ...
Does anyone have an idea how to improve my sol.?
THX a lot..
IMHO, I think you can have the controller as clean as possible, and define most of the needed functionality (binding, template, sorter, and icon) in the XMLView:
<List id="test-list" items="{
path : '/products',
sorter : [{
path : 'prodName',
descending : true
}]
}">
<StandardListItem title="{prodName}"
description="{prodCountry}"
icon="{path:'price', formatter:'.getIconFlag'}" />
</List>
You then can rid of all the template binding and manipulation stuff you have in your controller, and you only need to specify the formatter function getIconFlag:
getIconFlag : function (sPrice) {
return sPrice === "extra" ? "sap-icon://flag" : null;
}
See the following working example:
sap.ui.controller("view1.initial", {
onInit : function(oEvent) {
var oModel = new sap.ui.model.json.JSONModel();
oModel.setData({
"products": [
{
"prodName": "Apple",
"prodCountry": "Netherlands",
"price": "normal"
},
{
"prodName": "Orange",
"prodCountry": "Spain",
"price": "extra"
},
{
"prodName": "Strawberry",
"prodCountry": "Poland",
"price": "normal"
}
]
});
this.getView().setModel(oModel);
},
getIconFlag : function (sPrice) {
return sPrice === "extra" ? "sap-icon://flag" : null;
}
});
sap.ui.xmlview("main", {
viewContent: jQuery("#view1").html()
})
.placeAt("uiArea");
<script id="sap-ui-bootstrap"
src="https://sapui5.hana.ondemand.com/resources/sap-ui-core.js"
data-sap-ui-theme="sap_bluecrystal"
data-sap-ui-xx-bindingSyntax="complex"
data-sap-ui-libs="sap.m"></script>
<div id="uiArea"></div>
<script id="view1" type="ui5/xmlview">
<mvc:View
controllerName="view1.initial"
xmlns="sap.m"
xmlns:core="sap.ui.core"
xmlns:mvc="sap.ui.core.mvc" >
<List id="test-list" items="{
path: '/products',
sorter: [{
path: 'prodName',
descending: true
}]
}">
<StandardListItem title="{prodName}" description="{prodCountry}" icon="{path:'price', formatter:'.getIconFlag'}" />
</List>
</mvc:View>
</script>
bind the value for price to a formatter-function. so you can create the icons dynamically from the value
list.bindItems({
path : "/products",
sorter : new sap.ui.model.Sorter("prodName"),
template : new sap.m.StandardListItem({
title: "{prodName}",
description: "{prodCountry}",
/* bind items to factory-function */
icon: {
path: "price",
formatter: function(price) {
if (price == "extra") {
return "sap-icon://flag";
}
}
}
})
});
ps: i did not test this, but it should work like this. if you receive errors just comment.
I have been stuck on displaying data with ng-repeat. the only thing I have been able to do is display the one of the two objects. Every Customer can have multiple Users. I am trying to display the Users in a table with there CustomerId.
Working plunkr
app.controller('MainCtrl', function ($scope) {
var json = [
{
"CustomerId": "12xsd-344-fv23", "CompanyName": "ConocoPhillips",
"Address": "1234 Main St", "City": "Redmond", "State": "WA", "Zip": "10999",
"Email": "debra#example.com",
"Users": [
{
"FirstName": "Rudy", "LastName": "Sanchez", "CustomerId": "12xsd-344-fv23", "Customer": null,
"Email": "admin#energy.com", "EmailConfirmed": true, "PasswordHash": "AGtuCXr",
"SecurityStamp": "b0fca140", "PhoneNumber": null, "PhoneNumberConfirmed": false, "TwoFactorEnabled": false,
"LockoutEndDateUtc": null, "LockoutEnabled": false, "AccessFailedCount": 0, "Roles": [], "Claims": [], "Logins": [],
"Id": "49b5", "UserName": "admin"
},
{
"FirstName": "Troy", "LastName": "Benbow", "CustomerId": "12xsd-344-fv23", "Customer": null,
"Email": "tbenbow#yahoo.com", "EmailConfirmed": true, "PasswordHash": "AM8wL+iHaSG",
"SecurityStamp": "14f1483a-2e6f-41da-8307-a6c5945984a9", "PhoneNumber": null, "PhoneNumberConfirmed": false, "TwoFactorEnabled": false,
"LockoutEndDateUtc": null, "LockoutEnabled": true, "AccessFailedCount": 0, "Roles": [], "Claims": [], "Logins": [],
"Id": "9985b820-a45", "UserName": "tbenbow"
}
]
},
];
$scope.customers = json;
});
Since, CustomerId is also a property of User, you could make a list of Users in the controller and then loop them in the table:
$scope.users = [];
for(var i = 0; i < $scope.customers.length; i++) {
for(var j = 0; j < $scope.customers[i].Users.length; j++) {
//now you have access to customer properties with $scope.customers[i]
var user = $scope.customers[i].Users[j];
//example of adding CompanyName property
user.CompanyName = $scope.customers[i].CompanyName;
//add user to $scope.users
$scope.users.push(user);
}
}
And then just ng-repeat the users:
<tr ng-repeat="user in users">
<td>{{user.FirstName}} {{user.LastName}}</td>
<td>{{user.UserName}}</td>
<td>{{user.Email}}</td>
<td>{{user.CustomerId}}</td>
<td>{{user.CustomerName}}</td>
</tr>
Here is an updated plunker.
In fact, even if you need a property on the parent Customer part of json, you can add the property to the users array being repeated.
Preparing the data for view will often simplify template tricks (like having to build the table with extra ng-repeated elements. IMO, this is preferable.
There are two possible solutions for this problem, the first one ( rearranging your data in your controller) has already been mentioned in the other answer.
Another way would be a nested loop which I implemented like this:
<!doctype html>
<html ng-app="plunker" >
<head>
<meta charset="utf-8">
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css">
<script src="http://code.angularjs.org/1.1.4/angular.js"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<table class="table table-bordered">
<thead>
<tr>
<th>Name</th>
<th>UserName</th>
<th>Email</th>
<th>CustomerId</th>
</tr>
</thead>
<tbody ng-repeat="customer in customers">
<tr ng-repeat="user in customer.Users">
<td>{{user.FirstName}} {{user.LastName}} {{customer.CustomerId}}</td>
<td>{{user.UserName}}</td>
<td>{{user.Email}}</td>
<td>{{user.CustomerId}}</td>
</tr>
</tbody>
</table>
</body>
</html>
This solution is fast and easy to implement and gives you access to both the user and customer. I would still suggest to rebuild your data in your controller most of the time as it keeps your views clean and keeps any real logic in your controller (Check here for an example of that).
But this example is so simple that you can easily handle it in a nested ng-repeat.
I am trying to populate a chained State City select dropdowns. I am using an external json file to do so, which consists of State and their corresponding city name, id and pincode. Can somebody gimme some idea on how to achieve this. I need to populate the city and pincode value based on the state selection. Thanks in advance...
JSON
{
"Alaska" : [
{"id": "1", "name": "Adak", "pincode": "xxx1"},
{"id": "2", "name": "Akhiok", "pincode": "xxx2" },
{"id": "3", "name": "Akiak", "pincode": "xxx3" },
],
"Arizona" : [
{"id": "4", "name": "Apache Junction", "pincode": "xxx4"},
{"id": "5", "name": "Avondale", "pincode": "xxx5"},
{"id": "6", "name": "Benson", "pincode": "xxx6"},
],
"Alabama" : [
{"id" : "5", "name": "Abbeville", "pincode": "xxx7" },
{"id" : "7", "name": "Adamsville", "pincode": "xxx8" },
{"id" : "8", "name": "Akron", "pincode": "xxx9" },
]
}
Final rendered html
<select id="state">
<option value="1">Alaska</option>
<option value="2">Arizona</option>
<option value="3">Alabama</option>
</select>
<select id="city">
<option value="4">Adak</option>
<option value="5">Akhiok</option>
<option value="6">Akiak</option>
</select>
<input id="pincode" type="text" />
I find your question interesting and wrote the corresponding solution which you can see live here.
Here is the code:
jQuery(document).ready(function () {
var myData;
var onCityChange = function (e) {
var cityData = $("option:selected", e.target).data('pincode');
$("#pincode").val(cityData.pincode);
};
var onStateChange = function (e) {
var state = $("option:selected", e.target).text();
if (state && myData) {
var stateData = myData[state];
if (stateData && stateData.length > 0) {
$("#city").empty();
$("#city").unbind('change');
for (var i = 0; i < stateData.length; i++) {
var cityData = stateData[i];
var option = $('<option value="' + cityData.id + '">' + cityData.name + '</option>')
.data('pincode', cityData);
$("#city").append(option);
}
$("#city").bind('change', onCityChange)
.trigger('change');
}
}
};
$.ajax({
url: 'DependendSelectsFromJson.json',
dataType: 'json',
success: function (data) {
var stateOptions = "", stateId = 1;
for (var prop in data) {
if (data.hasOwnProperty(prop)) {
stateOptions += '<option value="' + stateId + '">' + prop + '</option>';
stateId++;
}
}
myData = data;
$("#state").html(stateOptions)
.bind('change', onStateChange)
.trigger('change');
}
});
});
to get access about the pincode of the second (city) select box I used jQuery.data function which I find very practical if one need to associate some additional information with an DOM element. One could save only pincode value in the way, but I used the function to save full information about the city.
UPDATED: I forgot to mention that the JSON data which you posted has errors. You should remove commas before ']'. Probably it is just a problem with cut&paste of the data to the text of the question.
there are many ways.
global Varible option.
var json_object = {
"Alaska" : {}
}; //and the rest of other text too lazy to type :)
$('#state').change(function(){
var state = $(this).val();
cities = json_object[state];
$('#city').html('');//remove the prev elements
for(item in cities){//same as foreach in php
$('<option value=""></option>').appendTo($('#city'));
}
});
i must admit is not complete but giving you the rough idea.
Other wise use ajax like so
$('#state').change(function(){
var url = 'ajax.php';//should return json with a list of cities only
var state = $('#state').val();
var data = {'state':state};
$.ajax({
url:url,
data:data,
dataType:'json',
success: function(items){
//do something with it
}
})
});
The only thing you need to know is that both have their advantages and disadvantages.
first option : can only fire one request but loads sh*t loads of data that are not needed.
second option: makes more request but less data