Jquery - Add hyperlink to datatables - javascript

Using the DataTable plugin I am able to generate a table just fine but I want a custom hyperlink on one of the columns that links to another page but taking information from the rest of the row...for example in row 1 I want a hyperlink: http://url/?data['imdata'][i]['faultInst']["attributes"]["code"] or something like that. I've seen a lot of complicated examples from other forms but couldn't get it to work. Looking for the simplest solution as this is a side project and I need it to be completed.
$(document).ready(function(){
$.getJSON('/static/faults.json', function (data) {
var test = $('#table5').DataTable({
});
var tr;
for (var i = 0; i < data["totalCount"]; i++) {
test.row.add([
data['imdata'][i]['faultInst']["attributes"]["code"],
data['imdata'][i]['faultInst']["attributes"]["cause"],
data['imdata'][i]['faultInst']["attributes"]["descr"],
data['imdata'][i]['faultInst']["attributes"]["created"],
data['imdata'][i]['faultInst']["attributes"]["changeSet"],
data['imdata'][i]['faultInst']["attributes"]["childAction"],
data['imdata'][i]['faultInst']["attributes"]["dn"],
data['imdata'][i]['faultInst']["attributes"]["domain"],
data['imdata'][i]['faultInst']["attributes"]["highestSeverity"],
data['imdata'][i]['faultInst']["attributes"]["lastTransition"],
data['imdata'][i]['faultInst']["attributes"]["lc"],
data['imdata'][i]['faultInst']["attributes"]["occur"],
data['imdata'][i]['faultInst']["attributes"]["origSeverity"],
data['imdata'][i]['faultInst']["attributes"]["prevSeverity"],
data['imdata'][i]['faultInst']["attributes"]["rule"],
"test",
//data['imdata'][i]['faultInst']["attributes"]["Severity"],
data['imdata'][i]['faultInst']["attributes"]["subject"],
data['imdata'][i]['faultInst']["attributes"]["type"],
//data['imdata'][i]['faultInst']['attributes']["ack"]
"test",
"test"
])
}
test.draw();
});
});

When you have a setup like this, just avoid to define data, by that you get the proper value you can turn into a link. dataTables know which data it should pass to the render function by the targets value. Example :
var table = $('#example').DataTable({
columnDefs : [
{ targets : [0],
render : function(data) {
return '<a href="'+data+'" target_blank>'+data+'</a>'
}
}
]
})
table.row.add(['https://example.com', 'david', 'programmer']).draw()
demo -> http://jsfiddle.net/47k7nhkb/

Related

Single check box selection with jquery datatable

In jquery datatable, single check box should be selected.
This link is working fine.
But above link is using jquery.dataTables 1.10.16 version. And I am using jquery.dataTables 1.9.4.
Can the same functionality as listed in example given above be possible with jquery.dataTables 1.9.4 instead of jquery.dataTables 1.10.16?
In the same page which you give the link, there are many explanation about to using "single check" oparetion.
At the end of the listed attachment, you can see the referanced .js file is
https://cdn.datatables.net/select/1.2.5/js/dataTables.select.min.js
In your page, you should add this file referance after dataTable.js.
I think, the version of jquery is not important. The important file is "dataTables.select.js"!
Secondly, you must update your dataTable maker codes like the sample below;
$(document).ready(function() {
$('#example').DataTable( {
columnDefs: [ {
orderable: false,
className: 'select-checkbox',
targets: 0
} ],
select: {
style: 'os',
selector: 'td:first-child' // this line is the most importan!
},
order: [[ 1, 'asc' ]]
} );
} );
UPDATES :
Why dont you try to write your own selector function?
for example;
$(document).ready(function() {
$('#example').DataTable( {
/// put your options here...
} );
$('#example').find("tr").click(function(){ CheckTheRow(this); });
} );
function CheckTheRow(tr){
if($(tr).find("td:first").hasClass("selected")) return;
// get the pagination row count
var activePaginationSelected = $("#example_length").find("select").val();
// show all rows
$("#example_length").find("select").val(-1).trigger("change");
// remove the previous selection mark
$("#example").find("tr").each(function(i,a){
$(a).find("td:first").removeClass("selected");
$(a).find("td:first").html("");
});
// mark the picked row
$(tr).find("td:first").addClass("selected");
$(tr).find("td:first").html("<i class='fa fa-check'></i>");
// re turn the pagination to first stuation
$("#example_length").find("select")
.val(activePaginationSelected).trigger("change");
}
Unfortunately, legacy data table does not support or have that select extension.
Workaround:
Create checkbox element inside 'mRender' callback.
Bind action to the checkbox. (This can be done inside the fnRowCallback or outside as in my example in below fiddle
https://jsfiddle.net/Rohith_KP/dwcatt9n/1/
$(document).ready(function() {
var userData = [
["1", "Email", "Full Name", "Member"],
["2", "Email", "Full Name", "Member"]
];
var table = $('#example').DataTable({
'data': userData,
'columnDefs': [{
'targets': 0,
'className': 'dt-body-center',
'mRender': function(data, type, full, meta) {
return '<input type="checkbox" value="' + $('<div/>').text(data).html() + '">';
}
}],
'order': [1, 'asc']
});
$('#example tr').click(function() {
if ($(this).hasClass('row_selected'))
$(this).removeClass('row_selected');
else
$(this).addClass('row_selected');
});
});
Also, I suggest you to upgrade your datatable version. Then you can use that select extension.
Can the same functionality as listed in example given above be possible with jquery.dataTables 1.9.4 instead of jquery.dataTables 1.10.16?
Yes.
But, not using the Select Extension since it requires at least version 1.10.7.
For 1.9.4, a possible solution would be:
$(document).ready(function() {
$('#example').find("td input[type='checkbox']").click(function() {
selectRow(this);
});
var table = $('#example').DataTable();
function selectRow(clickedCheckBox) {
var currentPage = table.fnPagingInfo().iPage;
// Being unchecked
if (!$(clickedCheckBox).is(':checked')) {
$(clickedCheckBox).removeAttr('checked');
getRow(clickedCheckBox).removeClass('selected');
return;
}
var selectEntries = $("#example_length").find("select");
var showEntriesCount = selectEntries.val();
var totalRows = table.fnGetData().length;
// If show entries != totalRows append total rows opiton that can be selected
if (totalRows != showEntriesCount)
selectEntries.append($('<option>', {
value: totalRows,
text: totalRows
}));
// Display all rows
selectEntries.val(totalRows).trigger("change");
// Removes all checked attribute from all the rows
$("#example").find("td input[type='checkbox']").each(function(value, key) {
getRow(key).removeClass('selected');
$(key).removeAttr('checked');
});
// Check the clicked checkBox
$(clickedCheckBox).prop('checked', true);
getRow(clickedCheckBox).addClass('selected');
// Re set the show entries count
selectEntries.val(showEntriesCount).trigger("change");
// If added, Remove the additional option added to Show Entries
if (totalRows != showEntriesCount)
selectEntries.find("[value='" + totalRows + "']").remove();
// Go to the page on which the checkbox was clicked
table.fnPageChange(currentPage);
}
function getRow(element) {
return $(element).parent().parent();
}
});
The above will require fnPagingInfo to take the user back to initial page. I haven't tested the solution on large dataset, tested it on a table with 150 rows, but should work fine on larger datasets too.
JSFiddle
Have you tried below code and I checked and it is working fine, you need to update styles and scripts:
You havr to update the latest styles and scripts to achieve latest functionality.
Single check box selection with jquery datatable

SAPUI5 TreeTable's getRows method returns empty array on the first call

I am trying to build an SAPUI5 application using TreeTable and I'm facing some problems to use its methods.
In my app, I have a button which triggers this method.
onChangeViewContext: function(oEvent) {
.........
.........
var aViewContext = oContext.oModel.getProperty(sPath + "/ViewContext");
var aDataModel = oContext.oModel.getProperty("/ApplicationCollection/" + sAppId + "/DataModel");
var oStructure = this._createParentChildStructure(aDataModel);
var oTreeModel = this.getView().getModel("treeModel");
oTreeModel.setData(oStructure);
this._oViewDetailLine = oSource.getParent().getParent().getParent();
this._oViewDetailLine.setVisible(false);
this.byId("idSelectElementsPanel").setVisible(true);
this._setSelectedItems(aViewContext, oTree);
}
What I'm trying to do here is only bind the rows with my treeModel, get tree table object and send it to my _setSelectedItems method which below.
_setSelectedItems: function(aViewContext, oTree) {
oTree.clearSelection();
var sElementName;
var aSelectedIndices = [];
var aElements = [];
var aRows = oTree.getRows();
aRows.forEach(function(row) {
if (row._oNodeState !== undefined) {
aElements.push(row.getCells()[0].getText());
}
});
I need to get rows array here because I will use it for setting selected items of tree table. The problem is when "onChangeViewContext" triggered, oTable.getRows() returns an empty array. But when I click cancel button (which just hides my tree table, nothing more) and then trigger "onChangeViewContext" function again, I can get the rows array completely.
Even on the first call when I try to get table's model, I can get the treeModel and its data correctly.
I've tried to refresh bindings, aggregations etc. But no luck.
By the way, I'm using row binding in my xml view like this :
<t:TreeTable id="idSelectElementsTree" rows="{path: 'treeModel>/'}" selectionMode="MultiToggle" enableSelectAll="false"
rowSelectionChange="onSelectElement">
I'm really drowning here so any any help would be appreciated.
Edit : rest of the setSelectedIndexes function :
aViewContext.forEach(function(name) {
sElementName = name;
if (aElements.indexOf(sElementName) !== -1) {
aSelectedIndices.push(aElements.indexOf(sElementName));
}
});
aSelectedIndices.forEach(function(idx) {
if (oTree.getRows()[idx]._bHasChildren) {
oTree.expand(idx);
}
oTree.addSelectionInterval(idx, idx);
});
What could help here is to add an event rowsUpdated="onRowsUpdated" to the table in the XML view. This event is triggered after the table has been loaded and will hence provide you with the data via;
this.getView().byId("sTableId").getRows();
The difference to your approach is that the event would not be triggered by the press of a button but automatically, as the table is rendered. You can then also use this function to trigger another one as per your use case.

How to show Total number of pages in YUI Paginator?

I am using YUI Paginator API for pagination and I need to show Total number of pages on screen. I saw that there is a function getTotalPages() in API but I am unsure about how to use it, there isn't enough documentation. Also after looking at some other documentation I tried using {totalPages} but didn't work.
Can somebody help me out in this issue? Thanks in advance!!
Below is the code snippet I am using. Please refer to template object from config:
config = {
rowsPerPage: 100,
template :
'<p class="klass">' +
'<label>Total pages: {totalPages}</label>'+
'<label>Page size: {RowsPerPageDropdown}</label>'+
'</p>',
rowsPerPageDropdownClass : "yui-pg-rpp-options",
rowsPerPageOptions : [
{ value : 100 , text : "100" },
{ value : 250 , text : "250" },
{ value : 500 , text : "500" },
{ value : 1000 , text : "1000" },
{ value : tstMap[tabName].length , text : "All" }
],
};
var myPaginator = new YAHOO.widget.Paginator(config);
The Paginator utility allows you to display an item or a group of items depending on the number of items you wish to display at one time.
Paginator's primary functionality is contained in paginator-core and is mixed into paginator to allow paginator to have extra functionality added to it while leaving the core functionality untouched. This allows paginator-core to remain available for use later on or used in isolation if it is the only piece you need.
Due to the vast number of interfaces a paginator could possibly consist of, Paginator does not contain any ready to use UIs. However, Paginator is ready to be used in any Based-based, module such as a Widget, by extending your desired class and mixing in Paginator. This is displayed in the following example:
YUI().use('paginator-url', 'widget', function (Y){
var MyPaginator = Y.Base.create('my-paginator', Y.Widget, [Y.Paginator], {
renderUI: function () {
var numbers = '',
i, numberOfPages = this.get('totalPages');
for (i = 1; i <= numberOfPages; i++) {
// use paginator-url's formatUrl method
numbers += '' + i + '';
}
this.get('boundingBox').append(numbers);
},
bindUI: function () {
this.get('boundingBox').delegate('click', function (e) {
// let's not go to the page, just update internally
e.preventDefault();
this.set('page', parseInt(e.currentTarget.getContent(), 10));
}, 'a', this);
this.after('pageChange', function (e) {
// mark the link selected when it's the page being displayed
var bb = this.get('boundingBox'),
activeClass = 'selected';
bb.all('a').removeClass(activeClass).item(e.newVal).addClass(activeClass);
});
}
});
var myPg = new MyPaginator({
totalItems: 100,
pageUrl: '?pg={page}'
});
myPg.render();
});

Titanium Alloy ListView Nested Model Array of Tags

I am using Titanium Alloy version 3.2. I have a collection of posts in a listview. My data looks like this:
[
{ username: 'dude', imageUrl: 'url', tags: ['tag1','tag2','tag3'] },
{ username: 'wheres', imageUrl: 'url', tags: ['tag1'] },
{ username: 'my', imageUrl: 'url', tags: ['tag1','tag2','tag3','tag4'] },
{ username: 'car', imageUrl: 'url', tags: ['tag1','tag2'] }
]
And here is the xml. This works only for username and image. I can't figure out how to add the tags to each post.
<ListView id="streamListview">
<Templates>
<ItemTemplate name="template" id="template">
<View class="item-container">
<ImageView bindId="pic" class="pic"/>
<Label bindId="username" class="username"/>
</View>
</ItemTemplate>
</Templates>
<ListSection id="section">
<ListItem template="template" class="list-item"/>
</ListSection>
</ListView>
And my controller code (without the tags)
var posts = [];
for (var i=0; i<data.length; i++){
var post = {
template : "template",
pic : { image : data[i].get("imageUrl") },
username : { text : data[i].get("username") }
};
posts.push(post);
}
$.section.setItems(posts);
How can I add tags (that are clickable) to the post if I am supposed to declare EVERY view in the template before hand? Each tags array in my example would need a different number of views depending on the array length. Each tag would ideally be its own UI.Label element. I believe this can be done using a TableView, but I would prefer using ListView for performance reasons.
I think I know what you need, in this case since you want to generate each item dynamically (for example, a scenario where you open your window with your ListView empty first and make an API call to get remote data and fill the ListView with said data) you will need to use ItemTemplates declared in their own controllers.
You just create a new controller like normal and in the view xml you put your ItemTemplate:
<Alloy>
<ItemTemplate name="template" id="template">
<View class="item-container">
<ImageView bindId="pic" class="pic"/>
<Label bindId="username" class="username"/>
</View>
</ItemTemplate>
</Alloy>
In your tss you put all of the styles referred to each element in your template, since you didn't provide a tss example I can't tell what are your style properties, but in the tss you need to define the style of the template, for example lets say something like:
"#template": // this is the id of your template in your xml
{
width : Ti.UI.FILL,
height : '44dp',
backgroundColor : '#FFFFFF'
}
To fill your ListView with ListItems dynamically, you will need to do something like this in your callback from your API:
function displayListItems(items)
{
var itemCollection = [];
for(var i=0; i < items.length; i++)
{
var tmp = {
pic : {
image : items[i].image
},
username : {
text : items[i].text
},
template : 'template' // here goes the name of the template in your xml, **do not confuse name with id, both are different and using one doesn't replace the other**
};
itemCollection.push(tmp);
}
$.ListView.sections[0].items = itemCollection;
}
And voila, you get your ListView filled dynamically. Now there are some extra steps you can do.
In your template controller you can leave it blank since the ListView can manage the itemclick event, but if you want different actions to take place when a certain element in the Listitem to trigger, you need to specify the functions to be called in your controller for each element.
For example lets say you passed a property called dataInfo to your ImageView and your Label in your template like this:
function displayListItems(items)
{
var itemCollection = [];
for(var i=0; i < items.length; i++)
{
var tmp = {
pic : {
image : items[i].image
dataInfo : items[i].fooA //lets pass to the ImageView the object fooA
},
username : {
text : items[i].text,
dataInfo : items[i].fooB //lets pass to the Label the object fooB
},
template : 'template' // here goes the name of the template in your xml, **do not confuse name with id, both are different and using one doesn't replace the other**
};
itemCollection.push(tmp);
}
$.ListView.sections[0].items = itemCollection;
}
And you want the ImageView and the Label to call different functions, you will need to change your xml like this:
<Alloy>
<ItemTemplate name="template" id="template">
<View class="item-container">
<ImageView bindId="pic" class="pic" onClick="imageFunction"/> <!-- added onClick event -->
<Label bindId="username" class="username" onClick="labelFunction"/> <!-- added onClick event -->
</View>
</ItemTemplate>
</Alloy>
In your controller you will declare each function:
function imageFunction(e)
{
var dataInfo;
if(Ti.Platform.osname === 'android')
{
var item = e.section.items[e.itemIndex];
var bindObject = item[e.bindId];
dataInfo = bindObject.fooA;
}
else
{
dataInfo = e.source.fooA;
}
}
function labelFunction(e)
{
var dataInfo;
if(Ti.Platform.osname === 'android')
{
var item = e.section.items[e.itemIndex];
var bindObject = item[e.bindId];
dataInfo = bindObject.fooB;
}
else
{
dataInfo = e.source.fooB;
}
}
Now you might ask, why do check for the operative system name, well that is because Android and iOS receive different e objects even if you use the same function. In iOS whatever property you pass to the source of the event can be accessed directly with e.source.propertyName while in Android you need to access to the item in e.section using e.itemIndex, after that you retrieve the view inside the item with the e.bindId associated to it.
One of the biggest restrictions on ListItems is updating the views inside a ListItem, to do this you need to update the whole item you want to change visually and assign it a different template, but the speed at which this is done you won't be able to notice any lag, seriously ListView's performance is something else, unlike ScrollView and let's not talk about the horrible and buggy TableView.
A warning, as of Titanium SDK 3.2.0.GA there's a bug in ItemTemplates that causes for views inside child views in the template to change their zIndex in Android with no way to control it, there are two known instances for this:
If you use a don't set the layout in a child view: this could cause for a view that should be displayed beneath another view to come on top of it.
If you use a vertical layout in a child view: this could cause for the positions of each view to be scrambled, this is because zIndex alters the order of display in a vertical layout.
This bug is triggered randomly and the Appcelerator team hasn't put much work on it, check the JIRA ticket here TIMOB-16704.
This can be avoided if you use a template with fixed positioned views and making sure no view comes on top of another, also remember no vertical layouts, haven't tested this with horizontal but personally I try to avoid horizontal layouts since there are other bugs related to it when used in scrollviews, normal views, etc.
EDIT
Another thing you might want to do with this is to assign a different look to the items you render, you have to options:
To apply the styles when you declare the ListItem.
To apply a different layout to each ListItem depending on a series of conditions.
For the first option you need to omit or overwrite the declaration of certain properties in your template:
For example, let's use a different background color where the property fooA exists and another color if it doesn't:
function displayListItems(items)
{
var itemCollection = [];
for(var i=0; i < items.length; i++)
{
var properties = {};
if(typeof items[i].fooA !== 'undefined')
{
properties = {
backgroundColor : 'red'
};
}
else
{
properties = {
backgroundColor : 'blue'
};
}
var tmp = {
properties : properties, // properties for the template
pic : {
image : items[i].image
dataInfo : items[i].fooA //lets pass to the ImageView the object fooA
},
username : {
text : items[i].text,
dataInfo : items[i].fooB //lets pass to the Label the object fooB
},
template : 'template' // here goes the name of the template in your xml, **do not confuse name with id, both are different and using one doesn't replace the other**
};
itemCollection.push(tmp);
}
$.ListView.sections[0].items = itemCollection;
}
You can change width, height, backgroundColor, layout, etc. according to your needs.
Now if you want each item to have a distinct look (meaning different views to display different content) and perhaps a different behavior, you'll need to use different templates.
This might sound bothersome but it is not, templates are fast to create once you get used to them which doesn't take long, another downer might be that if you want 11 different looks, that might mean you'll need 11 templates but that's a extreme case and you might want to rethink your UI if you're dealing with that many templates.
Although restrictive, item templates offer a wide array of options for you to use, a little of imagination is the only ingredient necessary to bring out all of the possibilities.
EDIT 2
I finally understood what was you problem, if you need to create a template whose content changes according to a x variable, then you should try declaring the template on your ListView controller, but this should be done before opening the window were you will be showing the ListView since the templates property can only be set on creation, you should add something like:
function createTemplate(items)
{
var template = {};
for(var i=0; i < items.length; i++)
{
template.childTemplates = [];
for(var j=0; items[i].tags.length; j++)
{
var childTemplate = {
type: 'Ti.UI.Label',
bindId: 'tag' + j,
properties : {
width : Ti.UI.SIZE, // Here you define how your style
height : Ti.UI.SIZE,
font : {
fontSize : '18dp'
},
text : items[i].tags[j].text // you can pass the text here or if you want to on the later for
}
};
template.childTemplates.push(childTemplate);
}
}
// After this you should end up with a template with as many childTemplates as tags each item have, so send the template to the controller with your ListView
Alloy.createController('ListViewWindow', {customTemplate: template});
}
And in your ListView controller you retrieve the template:
var args = arguments[0] || {};
var template = args.customTemplate;
$.ListView.templates = {'customTemplate' : template}; // before any window open call, do this
This should add the template to your ListView, you can also create the ListView in your controller instead of declaring it in your Alloy xml, use the one that fits your needs more.
This should be possible with a ListView if you create the template in the controller dynamically. You would also need to iterate through each "tags" object and generate a Ti.UI.Label "type" for each tag item. However, I'm not certain this method will be more efficient than using a TableView object because essentially every ListItem you create will contain a different template.
To generate a dynamic template it would be similar to this below: Keep in mind you will need to iterate over "tags" and generate x Ti.UI.Label types where x is the length of "tags". Also, the click event should work using Titanium SDK 3.2.1.
var plainTemplate = {
childTemplates: [
{
type: 'Ti.UI.Label',
bindId: 'username'
},
{
type: 'Ti.UI.ImageView',
bindId: 'pic'
},
{
type: 'Ti.UI.Label',
bindId: 'tags',
events: { click : handleTagClickEvent } // Binds a callback to click event
}
]};
function handleTagClickEvent(e) {
Ti.API.info('You clicked a tag label: ' + e.type);
}
var listView = Ti.UI.createListView({
templates: { 'plain': plainTemplate },
defaultItemTemplate: 'plain'
});
Hope this helps you in some way!

How do I ignore HTML when filtering a jQuery data table?

I'm using the jQuery DataTables plugin, and having a slight problem with the filtering feature in it. If I have a table cell with content like <a href='foo6'>Blah</a>, and I filter for "6", that cell will show up, even though there is no "6" in "Blah". What I'd like to do is have the DataTables plug-in ignore HTML when doing it's filtering.
I've tried scouring the DataTables site, and found conflicting, un-helpful leads. One post suggested that I needed an sType:'html' option in the definition of my aaColumns, but I tried that and it didn't help ... plus a later post suggested that the current version of DataTables auto-detects the HTML sType. I also found this code snippet:
// Make filtering ignore HTML (see http://datatables.net/plug-ins/filtering)
$.fn.dataTableExt.ofnSearch['html'] = function ( sData ) {
var n = document.createElement('div');
n.innerHTML = sData;
if (n.textContent) {
return n.textContent.replace(/\n/g," ");
} else {
return n.innerText.replace(/\n/g," ");
}
};
which was supposed to fix things ... but it didn't.
So, my question is: does anyone out there know how to make DataTables ignore non-text (ie. HTML) content when filtering rows?
It turned out I needed a custom mRender function on my column headers. More importantly (since I had tried this at first without checking the "type" argument) you need to use the type argument provided to that function to make it only apply when filtering.
My end result looked something like this:
aaHeaders: [{
mRender: function(data, type, full) {
// If we're filtering, don't look at the HTML; only filter on the text
return type == 'filter' ? $(data).text() : data
}
}], //...
You can try this one :
$.fn.dataTableExt.ofnSearch['html'] = function ( sData ) {
return $("<div/>").html(sData).text();
}
Just upgrade your datatable.js.. I have used 1.9.4 and got the same problem after upgrading to 1.10.9 the problem resolved..
// To override basic search functionality of datatable
$.fn.dataTable.ext.search.push(
function( settings, data, dataIndex ) {
var tableId = settings['sTableId'];
var searchTerm = settings.oPreviousSearch.sSearch;
if ( 'tableId' == tableId){
//I added tableId condition as I have multiple table on same page..
if(data[0].indexOf(searchTerm) > -1 ||data[2].indexOf(searchTerm) > -1||data[3].indexOf(searchTerm) > -1||data[4].indexOf(searchTerm) > -1||data[5].indexOf(searchTerm) > -1 || data[6].indexOf(searchTerm) > -1){
return true;
}else{
return false;
}
}
return true;
}
);

Categories