Datatables Jquery Cannot set properties of undefined (setting '_DT_CellIndex') - javascript

I know this has been asked a lot but I really cannot find my error on this one. I've been doing a lot of changes but still cannot figure out what's wrong. <th> and <td> matched number, I tried removing colspans too but had no luck. Any advice? By the way, here is my code please have a look.
window.load_tbl = function(){
var val = document.getElementById('assignval').value;
$('#faculty-tbl tbody').html('<tr><td colspan="6" class="text-center">Please Wait...</td></tr>')
$.ajax({
url:'functions/'+val,
success:function(resp){
if(typeof resp != undefined){
resp = JSON.parse(resp)
if(Object.keys(resp).length > 0){
$('#faculty-tbl tbody').html('')
var i = 1;
Object.keys(resp).map(k=>{
var date_updated = moment(resp[k].date_updated).format('MM/DD/YYYY h:mm a');
var tr = $('<tr></tr>')
tr.append('<td class="text-center"><button type="button" class="btn btn-outline-secondary">'+resp[k].id+'</button></td>')
tr.append('<td class="text-center">'+resp[k].firstname+'</td>')
tr.append('<td class="text-center">'+resp[k].middlename+'</td>')
tr.append('<td class="text-center">'+resp[k].lastname+'</td>')
if(resp[k].section_id != 0) {
tr.append('<td class="text-center">'+resp[k].advisory+'</td>')
} else {
tr.append('<td class="text-center">Unassigned</td>')
}
tr.append('<td><center><button class="btn btn-info btn-sm text-white edit_faculty" data-id = "'+resp[k].id+'"><i class="fa fa-edit"></i></button><button class="btn btn-danger btn-sm remove_faculty" data-id = "'+resp[k].id+'" data-firstname="'+resp[k].firstname+'" data-middlename="'+resp[k].middlename+'" data-lastname="'+resp[k].lastname+'" data-uid="'+resp[k].user_id+'"><i class="fa fa-trash"></i></button><button class="btn btn-dark btn-sm text-white show_date" data-date = "'+date_updated+'"><i class="fa-solid fa-database"></i></button></center></td>')
$('#faculty-tbl tbody').append(tr)
})
}else{
$('#faculty-tbl tbody').html('<tr><td colspan="6" class="text-center">No Data...</td></tr>')
}
}
},
complete:function(){
$('#faculty-tbl').dataTable()
}
})
}
$(document).on('change', '.assigned', function() {
if(this.checked) {
document.getElementById('assignval').value = 'load_ajax.php?action=load_faculty';
load_tbl();
} else {
document.getElementById('assignval').value = 'filter_ajax.php?action=filter_unassignedfaculty';
load_tbl();
}
});
$('#new_faculty').click(function(){
uni_modal('Add faculty','functions/manage_faculty.php');
})
$(document).ready(function() {
load_tbl()
$('#faculty-tbl tbody').on('click', '.edit_faculty', function () {
uni_modal("Update Faculty Information",'functions/manage_faculty.php?id='+$(this).attr('data-id'))
});
$('.help').on('click', function () {
help_modal("Need Help?")
});
$('#faculty-tbl tbody').on('click', '.remove_faculty', function () {
window.uid = $(this).attr('data-uid');
window.firstname = $(this).attr('data-firstname');
window.middlename = $(this).attr('data-middlename');
window.lastname = $(this).attr('data-lastname');
_conf("Are you sure to remove this data?",'remove_faculty', [$(this).attr('data-id')])
});
$('#faculty-tbl tbody').on('click', '.show_date', function () {
static_modal("Faculty Information", "Last Date Updated: "+$(this).attr('data-date'))
});
$('#print_faculty').click(function(){
var redirectWindow = window.open('print/report_faculty.php');
redirectWindow.location;
});
$('#reloadBtn').click(function(){
location.reload();
});
var table = $('#users-tbl')
if (!$.fn.DataTable.isDataTable('#users-tbl')) {
return;
}
else {
var table = $('#users-tbl').DataTable();
table.dataTable().fnDestroy();
}
});
If I miss anything also let me know, I'm doing an Enrollment System atm.
I've tried multiple things like matching rows and columns, and also did a few code that is recommended by the others but still no luck. I have no trouble loading the data but I just want this error gone and never thrown in the first place.

Never mind. I've been ignoring the simplest mistake ever, I keep putting a default message when there is no data available in the table which is clearly not supported by the data table, and when I removed it all my problems went away.
Considered this fixed.
Edit: As said by anrdewJames, I'll expand my answer for anyone who also encounters this error.
In my code there are two with colspans which in my case:
$('#faculty-tbl tbody').html('<tr><td colspan="6" class="text-center">Please Wait...</td></tr>')
$('#faculty-tbl tbody').html('<tr><td colspan="6" class="text-center">No Data...</td></tr>')
I completely removed the first line because I run this code when the data is still loading. Here's my solution:
The first error changed into:
window.check_tbl = function() {
var table = $('#faculty-tbl').DataTable();
if ( ! table.data().any() ) {
$('#faculty-tbl').dataTable().fnDestroy();
}
}
The second error, if the table has no data simply clear and draw.
var table = $('#faculty-tbl').DataTable();
table.clear()
.draw();
In any case, don't try to be so fancy as Datatable has default messages.

Related

Button within modal's table not firing [duplicate]

This question already has answers here:
Event binding on dynamically created elements?
(23 answers)
Closed 3 years ago.
Attempted to put a delete button that works in a table into a modal, with a table and it's like the click event is not firing at all. Hit's not back end code, no console.log(s), or js break points. Am I missing something?
Modal's Table
<table class="table table-hover table-md ">
<thead>
<tr>
<td class="text-left TableHead">Role</td>
<td class="text-right TableHead">Delete</td>
</tr>
</thead>
#*--Table Body For Each to pull DB records--*#
<tbody>
#foreach (var role in Model.Roles)
{
<tr>
<td>#role</td>
<td>
<button class="sqButton btnRed float-right zIndex"
title="Delete" data-toggle="ajax-modal" data-target="#deleteRoleUser"
data-url="#Url.Action("Delete", "Administration",
new {Id = Model.Id , Type = "roleUser"})" >
<i class="glyphicon glyphicon-remove"></i>
</button>
</td>
</tr>
}
</tbody>
</table>
Controller that it's supposed to call
[HttpGet]
public async Task<IActionResult> Delete(string id, string type)
{
if (type == "user") {
ViewBag.Type = "user";
var user = await userManager.FindByIdAsync(id);
if (user == null)
{
ViewBag.ErrorMessage = $"User with Id = {id} cannot be found";
return View("NotFound");
}
var model = new EditUserViewModel
{
Id = user.Id,
UserName = user.UserName,
};
ViewBag.UN = user.UserName;
return PartialView("~/Views/Modals/_DeleteModalPartial.cshtml", model);
}
if (type == "roleUser")
{
ViewBag.Type = "roleUser";
var role = await roleManager.FindByIdAsync(id);
if (role == null)
{
ViewBag.ErrorMessage = $"Role with Id = {id} cannot be found";
return View("NotFound");
}
var model = new EditRoleViewModel
{
Id = role.Id,
RoleName = role.Name,
};
ViewBag.Role = role.Name;
return PartialView("~/Views/Modals/_DeleteModalPartial.cshtml", model);
}
else
{
ViewBag.ErrorMessage = $"cannot be found";
return View("NotFound");
}
}
I am not sure why the click event on the button is not working at all. I have tried removing random code and literally nothing is making it go over to the controller at the least.
EDIT added javascript
$(function () {
var placeholderElement = $('#modal-placeholder');
$('[data-toggle="ajax-modal"]').click(function (event) {
var url = $(this).data('url');
$.get(url).done(function (data) {
placeholderElement.html(data);
placeholderElement.find('.modal').modal('show');
});
});
});
$('.sqButton').click( function (event) {
event.stopPropagation();
});
Since the button doesn't exist on page load you will have to create a event delegate to something that does exist on page load that will attach the event to the right element when it finally does appear in the DOM
In this case we will use the document (because it always exists on page load, some people use 'body') to delegate the event to the [data-toggle="ajax-modal"], like this:
$(document).on('click', '[data-toggle="ajax-modal"]', function (event) {
// code here
});
This will attach the event to the [data-toggle="ajax-modal"] elements on page load AND after page load if the element gets added later.
Try replacing your javascript code
$('.sqButton').click( function (event) {
event.stopPropagation();
});
With the following code
$('.sqButton').click(function(event) {
var url = $(this).data('url');
$.get(url).done(function (data) {
placeholderElement.html(data);
placeholderElement.find('.modal').modal('show');
});
});
if you manually force click, does it hit your controller?
document.querySelector('.btnRed').click();
is there any other element(s) "hijacking" click event?

Why does this jQuery not work in one place?

I have abstracted some jQuery code to handle filtering of tables in my application. In general, the user clicks a link and certain table rows are shown or hidden. The code is below:
var filters = $('ul.filters');
var filtersCount = filters.children().length;
if (filtersCount > 2) {
filters.children(':last-child').css({
'right':'1px'
})
}
var target = filters.data('target');
$('a.filter').on('click', function() {
filters.children().removeClass('active');
$('.filterable').hide();
var $this = $(this);
$this.parents('li').addClass('active');
var visibleStates = $this.data('include-filter').split(" ");
$(visibleStates).each(function(index,state) {
if (state == "all") {
$('.filterable').show();
} else {
$('.filterable' + '.' + state).show();
}
})
if ($this.data('exclude-filter') !== undefined) {
var hiddenStates = $this.data('exclude-filter').split(" ");
$(hiddenStates).each(function(index,state) {
$('.filterable' + '.' + state).hide();
})
}
})
This code is used in five places in my application and works in four of them. By stepping through execution, I know the code does work because it filters the elements but eventually all ".filterable" elements on the page are hidden. I have followed the execution of the code which goes into this part of jQuery:
if ( ret !== undefined ) {
if ( (event.result = ret) === false ) {
event.preventDefault();
event.stopPropagation();
}
}
The line which causes the ".filterable" rows to be hidden is this:
ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ).apply( matched.elem, args );
Eventually, execution stops on this line:
return event.result;
event.result is undefined if I look at it in the console. Javascript is not really my area of expertise so if anyone can give me a point in the right direction, I would be grateful.
EDIT
I have added the simplest code possible into my table as follows with the same result. Both rows are hidden and don't reappear irrespective of which filter link I click.
<tbody>
<tr class="filterable paid-sick-leave">
<td>From</td>
<td>To</td>
</tr>
<tr class="filterable paid-annual-leave">
<td>From</td>
<td>To</td>
</tr>
</tbody>
The filters look like this. The Ruby code prints out the description of the absence category, downcasing it and replacing spaces with hyphens.
<ul class="filters">
<li class="active">
All Absences
</li>
<% #absence_categories.each do |ac| %>
<li>
<%= ac.description %>
</li>
<% end %>
</ul>
change this click event like below:
$(document).on('click','a.filter', function() {
filters.children().removeClass('active');
$('.filterable').hide();
var $this = $(this);
$this.parents('li').addClass('active');
var visibleStates = $this.data('include-filter').split(" ");
$(visibleStates).each(function(index,state) {
if (state == "all") {
$('.filterable').show();
} else {
$('.filterable' + '.' + state).show();
}
})
if ($this.data('exclude-filter') !== undefined) {
var hiddenStates = $this.data('exclude-filter').split(" ");
$(hiddenStates).each(function(index,state) {
$('.filterable' + '.' + state).hide();
})
}
})
It mean "event.result" is Null you need to test if your event click still exist after you need to debug in the console script as application step by step may be this error caused by no reference class or html.

how can i compare class to a string in js

I want to add toggle star functionality in my project. For which I'm calling this script on click. This code fails to compare value of starclass to the class defined in the string.
Here i m trying to add star/unstar functionality just like gmail messages to my project.
$(".mailbox-star").click(function (e) {
debugger;
e.preventDefault();
var $this = $(this).find("a > i");
var glyph = $this.hasClass("glyphicon");
var fa = $this.hasClass("fa");
var msgId = $("#MsgId").val();
var StarClass = $(".mailbox-star i").attr('class');
var StarStatus;
if (StarClass === "fa text-yellow fa-star-o") {
StarStatus = true;
} else {
StarStatus = false;
}
//var starstatus = document.getElementById('ReadstatusStarred');
if (glyph) {
$this.toggleClass("glyphicon-star");
$this.toggleClass("glyphicon-star-empty");
}
$.ajax({
url: "/Home/Starred",
type: "GET",
dataType: "json",
data: {
ChangeStarredStatus: StarStatus,
ChangeMessageId: msgId
},
success: function (status) {
if (status) {
alert(status);
if (fa) {
$this.toggleClass("fa-star");
$this.toggleClass("fa-star-o");
}
}
},
error: function () {
alert("starfailed1");
}
})
});
//HTML CODE
here i m fetching values from my controller using model .If I can send value of IsStarred parameter in my js code my problem will be sorted
<table class="table table-hover table-striped">
<tbody>
#{int count = 0;}
#foreach (var item in Model)
{
string[] dt = #item.DateTime.ToString().Split(' ');
<tr title="#item.DateTime" id="ReadMessage" class="#((item.IsRead != true) ? "row row-highlight" : "row")" >
<td><input type="hidden" value="#item.IsRead" id="Readstatus_#count"></td>
<td><input type="hidden" value="#item.IsStarred" id="ReadstatusStarred"></td>
<td><input type="hidden" id="MsgId" value="#item.MessageId" /></td>
<td><input type="checkbox"></td>
<td class="mailbox-star" ><i class="#((item.IsStarred==true)? "fa fa-star text-yellow":"fa text-yelow fa-star-o")"></i></td>
<td class="mailbox-name" id="Text1" onclick="location.href='#Url.Action("Read", "Home", new
{
NameRead = item.FullName,
SubjectRead = item.Subject,
BodyRead = item.Body,
DateRead = item.DateTime,
MessageIdRead= item.MessageId,
})'">
<a href="#" id="Name">
#item.FullName
</a>
</td>
<td class="mailbox-subject" id="Text1">
<b>#item.Subject</b>-
#if (item.Body == null || item.Body.Length == 0)
{
}
else
{
if (item.Body.Length >= 100)
{
#item.Body.Substring(0, 100)
}
else
{
#item.Body
}
}
</td>
<td class="mailbox-attachment"></td>
<td class="mailbox-date">
#dt[0]
</td>
</tr>
count++;
}
</tbody>
</table>
</div>
Try using jQuery's is() to check for classes instead
var StarStatus = $(".mailbox-star i").is('.fa, .text-yellow, .fa-star-o')
If I got your description right you wanna have something like gmail has, click to star a mail, click again to destar it?
It's hard to say what is broken without the HTML you are using but I would do this in the following manner:
When loading mails from back you have to set up class "starMarked" to starred mails depending on how you mark the starred mail in the data comming from back you would check if something is true or equal to some value and then .addClass("starMarked") to that element.
bind the .click(Function that does the logic below) to all elements that represent mail (list member, square, icon, depends on what you have in the UI)
A functionality of that click then checks if that mail is stared or not. Since the status is already represented with class no need to check through data pulled from the server or make an additional request to the server to get that email's status. This saves time and load on the server.
NOTE: You must be certain the request to change status on the server went through or your logic of toggling on front and status on backend might become inconsistent!
Toggle on front could be done numerous ways but simplest would be using the CSS class "starMarked" to represent it's starred and lack of it to signal it's not. It gives 2 birds with one stone (looks and logic). If you need to check the status you could use .hasClass("starMarked").
When toggling a class use .removeClass() to remove the class from the element

How might I avoid opening a new window in due to watch results?

Please, look into my code. I would like to avoid opening a new window in due to watching results. I would like to avoid showing results instead of current content in the same window.
I have thought about using AJAX to show results in Dialog window (JQueryUI)
https://jqueryui.com/dialog/
but I have no idea how? Thank You in advance :)
HTML: result_browser.html
<form class="form" method="post" id="resultbrowserform">
<!--Something-->
<button type="submit" class="btn btn-default"> Apply </button>
</form>
<div id="dialog" title="Details">
<p></p>
</div>
JS:
$(function () { function serializeForm(form){...}
...
$('#resultbrowserform').submit(function () {
$.ajax({
type: 'POST',
url: '/result_browser/data/',
data: serializeForm($('#resultbrowserform')),
success: function (data, status, jqXHR) {
$('#resultbrowsertablecontainer').remove();
data = data.data;
if (!data.length) {
window.alert('No data for selected time period');
return;
}
div = $('<div id="resultbrowsertablecontainer">');
table = $('<table class="table table-striped">');
header = $('<thead>');
header.append($('<tr>')
.append( $('<th>').html('Die Id'))
.append( $('<th>').html('Application'))
.append( $('<th>').html('Result'))
table.append(header);
body = $('<tbody>');
for (i = 0; i < data.length; i++) {
row = $('<tr>');
row.append( $('<td>').html(data[i].die_id));
row.append( $('<td>').html(data[i].application));
row.append( $('<td>').html(data[i].result));
<!--I would like to avoid opening a new window-->
link = $('<a>').attr('target', '_blank').attr('href', '/result_details/' + data[i].result_id + '/').html('Details');
row.append($('<td>').append(link));
body.append(row);
}
table.append(body);
div.append(table);
$('#resultbrowsercontainer').append(div);
}
});
return false;
});
});
Simply remove the target attribute and then it will open inside itself:
<!--I would like to avoid opening a new window-->
link = $('<a>').attr('href', '/result_details/' + data[i].result_id + '/').html('Details');

Infinite scrolling fills completely rather than incrementally

I am attempting to create infinite scrolling on my web page using an example I found. However, the page fills up completely with all the items instead of just showing several items at a time. In other words it is not doing infinite scrolling. I noticed in some of the examples they parsed out data in chunks but in the real world how are you supposed to do that?
Below is my html code:
<table class="table table-striped table-bordered"><tr>
<th style="text-align:center;">User ID</th> <th>Username</th><th>Rank</th>
<th>Posts</th><th>Likes</th> <th>Comments</th> <th>Flags</th><th>Status</th><th>Action</th></tr>
<tr><td class="center">
<div ng-app='scroll' ng-controller='Scroller'>
<div when-scrolled="loadMore("")">
<div ng-repeat='item in items'>
<span>{{item.id}}
<span style="position:absolute;left:140px;">{{item.username}}</span>
<span style="position:absolute;left:290px;">{{item.rank}}</span>
<span style="position:absolute;left:360px;">{{item.posts}}</span>
<span style="position:absolute;left:440px;">{{item.likes}}</span>
<span style="position:absolute;left:530px;">{{item.comments}}</span>
<span style="position:absolute;left:640px;">{{item.flags}}</span>
<span class="label label-success" style="position:absolute;left:710px;">Active</span>
<a style="position:absolute;left:790px;" class="btn btn-info" style="width:30px" ng-href='/admin/userDetail?userid={{item.id}}'>
View Detail</a>
<hr>
</div>
</div>
</div>
</td></tr>
</table>
Below is my angularjs code:
function Scroller($scope, $http, $q, $timeout) {
$scope.items = [];
var lastuser = '999999';
$scope.loadMore = function(type) {
todate = document.getElementById("charttype").value;
var url = "/admin/getusers?type=" + todate + "&lastuser=" + lastuser;
var d = $q.defer();
$http({
'url': url,
'method': 'GET'
})
.success(function (data) {
var items = data.response;
for (var i = $scope.items.length; i < items.length; i++) {
$scope.items.push(items[i]);
count++;
if (count > 100)
{
lastuser = $scope.items[i].id;
break;
}
d.resolve($scope.items);
d.promise.then(function(data) {
});
}
)
.error(function (data) {
console.log(data);
});
return d.promise;
};
$scope.loadMore();
}
angular.module('scroll', []).directive('whenScrolled', function() {
return function(scope, elm, attr) {
var raw = elm[0];
alert("scroll");
elm.bind('scroll', function() {
if (raw.scrollTop + raw.offsetHeight >= raw.scrollHeight) {
scope.$apply(attr.whenScrolled);
}
});
};
});
My question is why does my web page show all 3200 lines initially rather than allowing me to do infinite scrolling. You will notice I put an alert in the scroll module and it is never displayed. Do I have to incrementally read my database? Any help is appreciated.
You are adding all of the items returned from your API call into $scope.items.
for (var i = 0; i < items.length; i++) {
$scope.items.push(items[i]);
}
Don't you want to add only a subset of those items?
P.S. Might help if you create a Plunkr to show the specific problem.
EDIT:
Based on your comment about the directive not working, I put together this Plunkr, which is a copy of your code but with the $http get code ripped out. The "scroll" alert fires here. I think you're just missing a closing bracket on your for loop (since I don't have your API endpoint to test against, I can't actually run your code live).
EDIT 2:
I'm not sure why you aren't seeing the function fire correctly on scroll. I've set up another plunker where I've changed the result of the scroll event firing to show an alert and load more items from a data variable, so you can see that the scroll event is firing correctly and it will load more items.

Categories