tablesorter select box with a custom match function - html

I have an HTML table with a column, in which an unordered list of strings is present. I am using tablesorter with the filter plugin to interact with the list. I want each string in all cells of the column to be present in a select filter in the header of the table. If the user selects an option, those rows should be displayed, where this string appears in the unordered list of the particular column cell.
Let me give you an example. I have the following list:
<table>
<thead>
<tr>
<th>name</th>
<th>profession</th>
</tr>
</thead>
<tbody>
<tr>
<td>Carl</td>
<td>
<ul>
<li>Builder</li>
<li>Professor</li>
</ul>
</td>
</tr>
<tr>
<td>Lisa</td>
<td>
<ul>
<li>Programmer</li>
<li>Professor</li>
</ul>
</td>
</tr>
</tbody>
</table>
In the table header, I want tablesorter to display a select box with the three professions. If, say, professor is selected, both rows should be shown. If programmer is selected, only Lisa would appear. How would I reach that?

Use the filter_functions widget option as follows (demo):
$(function () {
$('table').tablesorter({
theme: 'blue',
widgets: ['filter'],
widgetOptions: {
filter_functions: {
1: {
"Programmer": function (e, n) { return /programmer/.test(n); },
"Professor" : function (e, n) { return /professor/.test(n); },
"Builder" : function (e, n) { return /builder/.test(n); }
}
}
}
});
});
Update: If you don't want to hardcode because the professions vary, then you can use the filter_selectSource option to grab all of the column text, extract out each item from the list then return it as an array (demo):
HTML (make sure to include these class names)
<th class="filter-select filter-match">profession</th>
Script
$(function () {
$('table').tablesorter({
theme: 'blue',
widgets: ['filter'],
widgetOptions: {
filter_selectSource: function (table, column, onlyAvail) {
// get an array of all table cell contents for a table column
var arry = [],
array = $.tablesorter.filter.getOptions(table, column, onlyAvail);
// split content if multiple professions are encountered
$.each( array, function(i, n){
// look for carriage returns; split into two separate professions
if (/\n/.test(n)) {
n = n.split(/\n/);
}
arry.push(n);
});
// flatten array
return $.map(arry, function(n){ return n; });
}
}
});
});

Related

Add or subtract quantity as you click on the row of a table

I need that when I click on the row of a table I add the amount and that when I click on that same row again I subtract the amount that I had added. I have managed to add it but I don't know how to make it subtract the amount when clicking again.
I have managed to make the selected row change color depending on whether I select it or not, but now I need what has been added (this if I have succeeded) to be subtracted if I click on the row again.
This is my html:
<tbody>
<tr *ngFor="let item of articulos; index as i" (click)="total(item.cantidad)"
(click)="cambiarFlag(item)"
[ngClass]="{'seleccionada': item.selected, 'noSeleccionada': !item.selected}">
<td>{{item.articulo}}</td>
<td>{{item.cantidad}}</td>
<td>{{item.recogida}}</td>
</tr>
<br>
</tbody>
<div type="button" class="col border border-white border-4" id="other" type="button"
routerLink="/entry-order-lines-quantity" style="background-color:rgb(3, 71, 150);">
Cantidad {{totalCantidad}}
</div>
This is my ts:
export class EntryOrderLinesComponent implements OnInit {
totalCantidad: number = 0;
articulos = [
{
articulo: '385/65X22.5 HANKOOK AH51 160K (3003836)',
cantidad: 94,
recogida: '0',
selected: false,
},
{
articulo: '385/65X22.5 HANKOOK TH31 164K (3003309)',
cantidad: 60,
recogida: '0',
selected: false,
},
];
total(cantidad: number) {
this.totalCantidad += cantidad;
}
cambiarFlag(item: any) {
item.selected = !item.selected;
}
Thank you very much.
When we need execute two functions we should use an unique "event" and separate by ";" the functions. Some like:
<tr *ngFor="let item of articulos; index as i"
(click)="total(item.cantidad);cambiarFlag(item)">
Well, if always make the same, we can use an unique function
<tr *ngFor="let item of articulos; index as i"
(click)="selectAndCalculateTotal(item)">
And use
selectAndCalculateTotal(item:any)
{
item.selected=!item.selected;
this.totalCantidad+=(item.selected)?-item.cantidad:item.cantidad;
}
Really, if you have a few elements (less than 50 or 100) it's better calculate the total using the array item instead use an auxiliar variable. It's worst perfomance but more "robust". So you can use a getter
get total()
{
return this.items.reduce((a:number,b:any)=>{
return b.selected?a+b.cantidad:a
},0))
}
since you have a filed holding the selected state of each item, you need to first check the state before deciding the action to perform. you can do that like this
total(item: any) {
if (item.selected) {
this.totalCantidad += cantidad;
item.selected = !item.selected;
}else {
this.totalCantidad -= cantidad;
item.selected = !item.selected;
}
}
this way you don't need to call two functions anymore. you can delete the other function to changing the state of item.selected = !item.selected;
don't forget to pass the selected item into total(item) in your html click action

Angular search in a table

I want to make a search on the column SN in a table.
there many information in my table, I want to be able to search based on SN but when I add the filter it does not even load my table
This is what I did:
in My controler my List is filled :
$scope.List = {};
MyServices.getList()
.success(function (data) {
angular.forEach(data, function (value, index) {
$scope.List[value.SN] = {
Description: value.Description,
SN: value.SN
}
});
})
.error(function (error) {
$scope.status = 'Unable to load customer data: ' + error.message;
});
and this is my HTML:
<label>Search: <input ng-model="search.SN"></label>
<tr ng-repeat="V in List| filter:search">
<td>{{V.SN}}</td>
<td>{{V.Description}}</td>
</tr>
You must write as follow:
<label>Search: <input ng-model="search.SN"></label>
<tr ng-repeat="V in List| filter: {SN: search.SN}">
<td>{{V.SN}}</td>
<td>{{V.Description}}</td>
</tr>
Remove the object declaration on the input field. It will match the whole object for your specified value on the input field:
<label>Search: <input ng-model="search"></label>
<tr ng-repeat="V in List| filter: search">
<td>{{V.SN}}</td>
<td>{{V.Description}}</td>
</tr>

Angular ng-repeat with nested json objects?

I have a JSON object, represented as such:
{
"orders" : [
{
"ordernum" : "PRAAA000000177800601",
"buyer" : "Donna Heywood"
"parcels" : [
{
"upid" : "UPID567890123456",
"tpid" : "TPID789456789485"
},
{
"upid" : "UPID586905486090",
"tpid" : "TPID343454645455"
}
]
},
{
"ordernum" : "ORAAA000000367567345",
"buyer" : "Melanie Daniels"
"parcels" : [
{
"upid" : "UPID456547347776",
"tpid" : "TPID645896579688"
},
{
"upid" : "UPID768577673366",
"tpid" : "TPID784574333345"
}
]
}
]
}
I need to do a repeater on the second level of this, a list of the "upid" numbers.
I know already how to get the top level
<li ng-repeat="o in orders">{{o.ordernum}}</li>
But I am unclear on the sequence to loop a level down. For example, this is wrong:
<li ng-repeat="p in orders.parcels">{{p.upid}}</li>
I also know how to nest repeaters to get this, but in this case i don't need to display the top level at all.
CLARIFICATION
The goal here is to have one list with the 4 "upid" numbers (there are 2 for each parcel, and there are 2 parcels in the order).
Actually its same answer of #sylwester. The better way to put it in filter. And you can reuse it by passing propertyName parameter.
In your case we passed parcels
JS
myApp.filter('createarray', function () {
return function (value, propertyName) {
var arrayList = [];
angular.forEach(value, function (val) {
angular.forEach(val[propertyName], function (v) {
arrayList.push(v)
});
});
return arrayList;
}
});
HTML
<ul>
<li ng-repeat="o in ordersList.orders | createarray: 'parcels'">{{o.upid}}</li>
</ul>
Here is working Fiddle
You can just create new array 'parcels' like in demo below:
var app = angular.module('app', []);
app.controller('homeCtrl', function($scope) {
$scope.data = {
"orders": [{
"ordernum": "PRAAA000000177800601",
"buyer": "Donna Heywood",
"parcels": [{
"upid": "UPID567890123456",
"tpid": "TPID789456789485"
}, {
"upid": "UPID586905486090",
"tpid": "TPID343454645455"
}]
}, {
"ordernum": "ORAAA000000367567345",
"buyer": "Melanie Daniels",
"parcels": [{
"upid": "UPID456547347776",
"tpid": "TPID645896579688"
}, {
"upid": "UPID768577673366",
"tpid": "TPID784574333345"
}]
}]
};
$scope.parcels = [];
angular.forEach($scope.data.orders, function(order) {
angular.forEach(order.parcels, function(parcel) {
$scope.parcels.push(parcel)
})
})
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="homeCtrl">
<ul>
<li ng-repeat="o in parcels">{{o.upid}}</li>
</ul>
</div>
</div>
Seems like you just need a double-nested for loop -
<ul>
<div ng-repeat="o in orders">
<li ng-repeat="p in o.parcels">{{p.upid}}</li>
</div>
</ul>
The HTML might be a little ugly here, but I'm not sure what exactly you are going for. Alternatively you could just create a new array of the parcels via mapping.
Searching a lot for nice and simple solution for iterating dynamically. I came up with this
JAVASCRIPT (angular): a person is an example of nested object. the is_object function will be use in the HTML view.
$scope.person = {
"name": "john",
"properties": {
"age": 25,
"sex": "m"
},
"salary": 1000
}
// helper method to check if a field is a nested object
$scope.is_object = function (something) {
return typeof (something) == 'object' ? true : false;
};
HTML: define a template for simple table. the 1st TD is the key which is displayed. another TD (2 or 3, but never both) will be show the value if its not an object (number / string), OR loop again if its an object.
<table border="1">
<tr ng-repeat="(k,v) in person">
<td> {{ k }} </td>
<td ng-if="is_object(v) == false"> {{ v }} </td>
<td ng-if="is_object(v)">
<table border="1">
<tr ng-repeat="(k2,v2) in v">
<td> {{ k2 }} </td>
<td> {{ v2 }} </td>
</tr>
</table>
</td>
</tr>
</table>
The reason that <li ng-repeat="p in orders.parcels">{{p.upid}}</li> does not work the way you expect is because the parcels array is an object inside each individual order in your order array, i.e. it is not an object of the orders array itself.
If your orders array is defined on the $scope of a controller, then you create the array on the $scope variable:
$scope.allParcels = $scope.orders
.map(function (elem) {
return elem.parcels;
}) // get an array where each element is an array of parcels.
.reduce(function (previousValue, currentValue) {
return previousValue.concat(currentValue);
}); // concat each array of parcels into a single array of parcels
then on the template, you can use <li ng-repeat='p in allParcels'>{{p.upid}}</li>
If, however, you do not want to place the array on the $scope, I believe you can do something similar to this:
<li ng-repeat="p in orders
.map(function (elem) {
return elem.parcels;
})
.reduce(function (previousValue, currentValue) {
return previousValue.concat(currentValue);
})">{{p.upid}}</li>
although I'm not 100% sure that Angular will evaluate the .map/.reduce in the ng-repeat expression (also having an array generated this way in an ng-repeat is ill-advised since angular would have to constantly generate a new array via map/reduce on each $digest cycle).

Multiple call to onselect from select element with Angular ng-repeat

Im using an HTML select element to display an entity's field that has enumerated values in it.
Within the page, I have multiple fields for the entity, therefore multiple select elements.
Im using an Angular ng-repeat to display each field, by creating a select within the ng-repeat on my table row. I want to capture the "onselect" event when an item is selected from any of the drop downs, the trouble is, whenever the user selects a value in one drop down, the event fires for all drop downs on the page.
My html:
<div ng-app>
<div ng:controller="TodoCtrl">
<table>
<tr ng-repeat="prop in customForm">
<td>{{prop.legend}}</td>
<td>
<select ng-name="prop.name" ng-model="entity[prop.name]"
ng-options="val as val for val in prop.enumeratedValues"
onselect="{{fireSelectEvent(prop.name)}}"></select>
</td>
</tr>
<tr><td>Calls Made</td></tr>
<tr ng-repeat="call in callLog">
<td>{{call}}</td>
</tr>
</table>
</div>
</div>
My Controller:
//'use strict';
function TodoCtrl($scope) {
$scope.customForm =
[
{name:"aval", legend: "A Value", enumeratedValues: ["1","2","3"], editable: true},
{name:"bval", legend: "B Value", enumeratedValues: ["4","5","6"], editable: true},
{name:"cval", legend: "C Value", enumeratedValues: ["7","8","9"], editable: true}
];
$scope.entity = {};
$scope.callLog = [];
$scope.fireSelectEvent = function( propName )
{
console.log("Prop=" + propName + " value=" + $scope.entity[propName]);
$scope.callLog.push( propName );
}
}
My fiddle:
http://jsfiddle.net/utgwG/
Good luck. We're all counting on you.
What is the onselect attribute? You're seeing fireSelectEvent fire that many times, because you've bound the onselect attribute using Angular interpolation.
What I'm guessing you want to do is: ng-change="fireSelectEvent(prop.name)"
Your updated fiddle: http://jsfiddle.net/utgwG/2/

How to add button or images to dojo grid

I have a dojo grid with a json datastore (mysql resultset converted into json format). Currently my grid show 5 columns as shown below in the figure:
I have column named 'Action'. The rows under this 'Action' column should contain buttons or images(edit icon, delete icon) with hyperlinks such as edit.php?id=1 for edit, or delete.php?id=1 for delete.
Here is my dojo grid code:
<span dojoType="dojo.data.ItemFileReadStore" data-dojo-id="studentsStore" url="http://localhost/newsmis/public/studentManagement/student/fetchdata/data/1"></span>
<table dojoType="dojox.grid.DataGrid" id="studentsGrid" data-dojo-id="studentsGrid" columnReordering="true" sortFields="['idstudents','first_name','middle_name','last_name']" store="studentsStore" clientSort="true" selectionMode="single" rowHeight="25" noDataMessage="<span class='dojoxGridNoData'>No students found</span>">
<thead>
<tr>
<th field="idstudents" width="20%">Student ID</th>
<th field="first_name" width="20%">First Name</th>
<th field="middle_name" width="20%">Middle Name</th>
<th field="last_name" width="20%">Last Name</th>
<th field="action" width="20%">Action</th>
</tr>
</thead>
</table>
My json data format is
{"identifier":"idstudents","items":[{"idstudents":"11","first_name":"Pradip","middle_name":"Maan","last_name":"Chitrakar"}]}
How can i do it? Please suggest me some ideas
The one way I know is, that defining formatting method for that column in grid structure. So instead of defining the structure of the grid declaratively, define in JavaScript object like below
var structure = [
{
name: "First Name",
field: "first_name"
},
{
name: "Action",
field: "_item",
formatter: function(item){
var btn = new dijit.form.Button({
label: "Edit"
});
return btn;
}
}
]
and set this structure to the grid
<table dojoType="dojox.grid.DataGrid" id="studentsGrid" data-dojo-id="studentsGrid" columnReordering="true" sortFields="['idstudents','first_name','middle_name','last_name']" store="studentsStore" clientSort="true" selectionMode="single" rowHeight="25" noDataMessage="<span class='dojoxGridNoData'>No students found</span>" structure=structure >
Here is the working example,
Image in dojo Grid
As documentation of dojox.grid.DataGrid stays:
Beginning with Dojo 1.7, you should use dgrid or gridx, next-generation grid components that take full advantage of modern browsers and object stores.
piece of code example:
columns: [
{
field: "id",
label: "ID"
},
{
field: "name",
label: "Name"
},
{
field: "options",
label: "Options",
renderCell: function (obj) {
var cellContent = domConstruct.create("div",{});
var btn = new Button({
label: "Cell " + obj.id,
name: "idBtn"
})
btn.placeAt(cellContent);
on(btn, "click", function (evt) {
console.log(obj);
});
return cellContent;
}
}
]
This is JSfiddle example how to do it in dgrid by using function renderCell in column properties of dgrid.
renderCell(object, value, node, options) - An optional function that will be called to render the value into the target cell. object refers to the record from the grid’s store for the row, and value refers to the specific value for the current cell (which may have been modified by the column definition’s get function). node refers to the table cell that will be placed in the grid if nothing is returned by renderCell; if renderCell returns a node, that returned node will be placed in the grid instead. (Note: if formatter is specified, renderCell is ignored.)
If you don't want a button, but only an image icon, you can return a span-element from the formatter function like this:
formatter: function(value) {
var myValueClass = "dijitNoValue";
...
myValueClass = "ValueClass1";
...
return "<span class='dijitReset dijitInline dijitIcon " + myValueClass + "'></span>";
}
An css class has to be defined like
.ValueClass1 {
background-image: url('val_image1.png');
background-repeat: no-repeat;
width: 18px;
height: 18px;
text-align: center;
background-position: 1px;
}