Enabling History API with datatables - javascript

I have a dataTable object on a page representing a list of releases I need to keep track of with the url /releases I want to add the following functionality
if /releases?query=<query>, the dataTable will initialized with the provided query
The query parameter is updated if the user changes the search term
The back and forward buttons in the browser go the appropriate query
So far I am able to do the first 2, but when I listen for the popstate event, redrawing the table triggers a pushState which I can't figure out how to prevent. Here's my code so far:
$(document).ready(function(){
var prevSearch;
var table = $('#releases').dataTable({
"bJQueryUI" : true,
"sPaginationType" : "full_numbers",
"iDisplayLength" : 50,
"oSearch": {"sSearch": '#{params[:query]}'},
"fnDrawCallback": function(oSettings) {
var curSearch = oSettings.oPreviousSearch.sSearch;
if (!prevSearch) {
prevSearch = curSearch;
} else if (curSearch != prevSearch) {
console.log("changed to: " + curSearch);
history.pushState({query: curSearch}, "title", "releases?query=" + curSearch);
prevSearch = curSearch;
}
}
});
window.addEventListener("popstate", function(e) {
if (e.state) {
table.fnFilter(e.state.query);
}
});
});
Note, I am using a rails backend and this is inlined javascript being served in the page.

you have only 2 options here:
move pushState code out of drawCallback. There must be some other code that causes the datatables to draw when user enters something. put your pushState code there. This is the ideal solution
add a hack like this
$(document).ready(function () {
var prevSearch;
var saveState = true;
var table = $('#releases').dataTable({
"bJQueryUI":true,
"sPaginationType":"full_numbers",
"iDisplayLength":50,
"oSearch":{"sSearch":'#{params[:query]}'},
"fnDrawCallback":function (oSettings) {
var curSearch = oSettings.oPreviousSearch.sSearch;
if (!prevSearch) {
prevSearch = curSearch;
} else if (curSearch != prevSearch) {
console.log("changed to: " + curSearch);
if (saveState) {
history.pushState({query:curSearch}, "title", "releases?query=" + curSearch);
}
prevSearch = curSearch;
}
}
});
window.addEventListener("popstate", function (e) {
if (e.state) {
saveState = false;
table.fnFilter(e.state.query);
saveState = true;
}
});
});

Related

Show the attribute inspector without saving a new feature

The requirement is to add a new feature from template picker but without applying it, can i show the attribute inspector than save the feature.
selectedTemplate = templatePicker.getSelected();
This selectedTemplate is then selected to put the points on the map than opens the attribute inspector by selecting it.
selectedTemplate.featureLayer.applyEdits([newGraphic], null, null);
Sample Code Block :
dojo.connect(drawToolbar, "onDrawEnd", function(geometry) {
//display the editable info window for newly created features
if (map.infoWindow.isShowing) {
map.infoWindow.hide();
}
drawToolbar.deactivate();
var fieldAttributes = layerFieldToAttributes(selectedTemplate.featureLayer.fields);
var newAttributes = dojo.mixin(fieldAttributes, selectedTemplate.template.prototype.attributes);
var newGraphic = new esri.Graphic(geometry, null, newAttributes);
var layerInfos = [{
'featureLayer': selectedTemplate.featureLayer,
'isEditable': true
}];
var attInspector = new esri.dijit.AttributeInspector({
layerInfos: layerInfos
}, dojo.create("div"));
selectedTemplate.featureLayer.applyEdits([newGraphic], null, null, function() {
var screenPoint = map.toScreen(getInfoWindowPositionPoint(newGraphic));
map.infoWindow.setContent(attInspector.domNode);
map.infoWindow.resize(325, 185);
map.infoWindow.show(screenPoint, map.getInfoWindowAnchor(screenPoint));
templatePicker.clearSelection();
});
dojo.connect(attInspector, "onAttributeChange", function(feature, fieldName, newFieldValue) {
feature.attributes[fieldName] = newFieldValue;
feature.getLayer().applyEdits(null, [feature], null);
});
dojo.connect(attInspector, "onDelete", function(feature) {
feature.getLayer().applyEdits(null, null, [feature]);
map.infoWindow.hide();
});
});
}
I would like my client first add the attribute to feature and (save & apply) it.
Any help would be appreciated.
Here is the sample project : https://www.dropbox.com/s/fh71g1k9nsa70nq/index-2.html.zip?dl=0
I dont think you can do that with the AttributeInspector, try creating a custom popup that will have options to save and delete/cancel, when saving fire the applyEdits, when clicking delete, remove, ect.
Content:
var content = "<input id='text1'></input> </br>" +
"<input id='text1'></input> </br>" + "<button id='submit'>Submit</button>" + "<button id='delete'>Delete</button>"
/*
var attInspector = new AttributeInspector({
layerInfos: layerInfos
}, dojo.create("div"));
*/
map.infoWindow.setTitle(selectedTemplate.featureLayer.name);
map.infoWindow.setContent(content);
map.infoWindow.resize(350, 240);
map.infoWindow.show(evt.geometry, map.getInfoWindowAnchor(evt.geometry));
Listener:
on(map.infoWindow, "show", function () {
on(dom.byId("submit"), "click", function () {
alert("I should be saving");
});
on(dom.byId("delete"), "click", function () {
alert("I should be deleting");
});
})
Check out this fiddler: https://jsfiddle.net/kreza/jpLj5y4h/2/

How to show confirmation pop up when changing page in DataTable

I am landing on the first page of DataTable, make some changes.
Then I move to the second page.
Actually, confirmation popup is shown but it navigate to the second page.
Expected: confirm pop is shown but it still landing on the first.
Here is my code:
$('#dataTable-id').on( 'page.dt', function (event) {
if( change ){
bootbox.dialog({
title: "Confirmation",
message : "Discard changes?",
buttons :{
main: {
label : "Leave",
className : "btn-primary",
callback: function (){
// To avoid broking page/length controller
// move to other pages
return true; // cancel draw
}
},
cancel: {
label : "Stay",
className : "btn-default",
callback : function() {
// stay at current page.
return true;
}
}
},onEscape: function () {return true;}
});
}
});
How to show confirmation popup before page change?
The page.dt event is only informational, it can not be canceled.
You can workaround that restriction by writing a custom preDrawCallback like discussed here: https://datatables.net/forums/discussion/25507
EDIT: You have to cancel the redraw generally and do the paging manually in the bootbox callback (as it does not work as a real modal dialog like the native javascript confirm()). I modified the above example to incorporate a bootbox confirm dialog on paging: https://jsfiddle.net/bk4nvds5/
$(document).ready(function () {
var pageLen = 10;
var originalPage = 0;
var originalPageLen = pageLen;
var gotoPage = 0;
var gotoPageLen = originalPageLen;
var fromBootbox = false;
var table = $('#example').DataTable({
"pageLength": pageLen,
"preDrawCallback": function (settings) {
if(table){ // ignore first draw
if (!fromBootbox) {
// To avoid broking page/length controller, we have to reset the paging information
gotoPage = settings._iDisplayStart;
gotoPageLen = settings._iDisplayLength;
bootbox.confirm("Are you sure?", function(result) {
console.log("goto page" + gotoPage + " (result: " + result + ")");
if (result) {
fromBootbox = true;
table.page.len(gotoPageLen);
table.page(gotoPage / gotoPageLen).draw(false);
fromBootbox = false;
}
});
settings._iDisplayStart = originalPage;
settings._iDisplayLength = originalPageLen;
$('[name="example_length"]').val(originalPageLen);
return false; // cancel draw
}else{
originalPage = settings._iDisplayStart;
originalPageLen = settings._iDisplayLength;
}
}
}
});
});

How to achieve recursive call - jquery

I have a custom created dialog module.
I am passing a mvc view called Cart to this module.
The cart view has a link called 'Create New Contat' clicking on which the view(Cart) will be replaced with another view(Contact) using an ajax call. The contact view has a button called cancel. When the user clicks on Cancel the old view(Cart) will replace the existing view(contact).
The problem I am facing is after replacing the view none of the links or buttons work on the view.
Can some body pls directed me towards a better way of doing this.
Pasted below is the code.
$(document).on('click', '.ddlCart li', function(e) {
var ddlselectedVal = $(this).attr('id');
var selectedListinsCount = selected_Listings.length;
var SelectedMlsnums = selected_Listings.join();
var agentId = $("#AgentId").val();
var Action;
var EnvironmentURL = $("#EnvironmentURL").val();
var postData = { AgentId: agentId, Mlsnums: SelectedMlsnums, ActionTypeValue: “PreAddToCart” };
var close = function (event, ui) {
$('#dvModalDialog').dialog("close");
}
var open = function (event, ui) {
var url = EnvironmentURL + "MLSReports/Stats/SearchContacts";
$("#btncart_cancel").on("click", function () {
$('#dvModalDialog').dialog("close");
});
$("#btncart_submit").on("click", function () {
var url = EnvironmentURL + "MLSReports/Stats/Cart";
//Send the data using post and put the results in a div
$.post(url, {
AgentId: agentId, Mlsnums: SelectedMlsnums, ActionTypeValue: "AddToCart"
},
function (data) {
// Replace current data with data from the ajax call to the div.
$("#dvModalDialog").empty().append(data);
});
});
$("#lnkCreateNewcart").on("click", function () {
var url = EnvironmentURL + "MLSReports/Stats/Cart";
//Send the data using post and put the results in a div
$.post(url, {
ActionTypeValue: "preAddorEditContact"
},
function (data) {
//debugger;
// Replace current data with data from the ajax call to the div.
$("#dvModalDialog").empty().append(data);
$("#btnCancelContact").on("click", function () {
////********** replace the view (Contact) with the view (Cart).
// In the cancel event I am loading the previous page.I am having problem here. after post none of the controls work.
$.post(url, {
ActionTypeValue: "PreAddToCart"
},
function (data) {
//debugger;
// Replace current data with data from the ajax call to the div.
$("#dvModalDialog").empty().append(data);
})
});
});
});
};
if (ddlselectedVal == "AddtoCart") {
var rd = Mod.ReportsDialog({ title: 'Add To Cart', close: close, open: open });
rd.url = EnvironmentURL + "/MLSReports/Stats/Cart";
rd.targetElement = '#dvModalDialog'// '#dvSendEmail'
rd.formName = '#frmCart'
rd.postData = postData
rd.open();
}
});

Running a form handled by ajax in a loaded ajax page?

Using tutorials found i'm currently loading new pages with this:
$("a.nav-link").click(function (e) {
// cancel the default behaviour
e.preventDefault();
// get the address of the link
var href = $(this).attr('href');
// getting the desired element for working with it later
var $wrap = $('#userright');
$wrap
// removing old data
.html('')
// slide it up
.hide()
// load the remote page
.load(href + ' #userright', function () {
// now slide it down
$wrap.fadeIn();
});
});
This loads the selected pages perfectly, however the pages have forms that themselves use ajax to send the following:
var frm = $('#profileform');
frm.submit(function (ev) {
$.ajax({
type: frm.attr('method'),
url: frm.attr('action'),
data: frm.serialize(),
success: function (data) {
alert(data)
}
});
However this is not sending the form as it did before the page itself was called to the parent page via ajax. Am I missing something? Can you not use an ajax call in a page already called by ajax?
I also have other issues, for example I disable the submit button unless there are any changes to the form, using:
var button = $('#profile-submit');
var orig = [];
$.fn.getType = function () {
return this[0].tagName == "INPUT" ? $(this[0]).attr("type").toLowerCase() : this[0].tagName.toLowerCase();
}
$("#profileform :input").each(function () {
var type = $(this).getType();
var tmp = {
'type': type,
'value': $(this).val()
};
if (type == 'radio') {
tmp.checked = $(this).is(':checked');
}
orig[$(this).attr('id')] = tmp;
});
$('#profileform').bind('change keyup', function () {
var disable = true;
$("#profileform :input").each(function () {
var type = $(this).getType();
var id = $(this).attr('id');
if (type == 'text' || type == 'select') {
disable = (orig[id].value == $(this).val());
} else if (type == 'radio') {
disable = (orig[id].checked == $(this).is(':checked'));
}
if (!disable) {
return false; // break out of loop
}
});
button.prop('disabled', disable);});
However this also doesn't work when pulled to the parent page. Any help much appreciated! I'm really new to ajax so please point out any obvious mistakes! Many thanks in advance.
UPDATE
Just an update to what i've found. I've got one form working by using:
$(document).on('mouseenter', '#profile', function() {
However the following:
$(document).on('mouseenter', '#cancelimage', function() {
$('#cancelimage').onclick=function() {
function closePreview() {
ias.cancelSelection();
ias.update();
popup('popUpDiv');
$('#imgForm')[0].reset();
} }; });
Is not working. I understand now that I need to make it realise code was there, so I wrapped all of my code in a mouseover for the new div, but certain parts still don't work, so I gave a mouseover to the cancel button on my image form, but when clicked it doesn't do any of the things it's supposed to.
For anyone else who comes across it, if you've got a function name assigned to it, it should pass fine regardless. I was trying to update it, and there was no need. Doh!
function closePreview() {
ias.cancelSelection();
ias.update();
popup('popUpDiv');
$('#imgForm')[0].reset();
};
Works just fine.

Modify collapse.js to get addtional data from xml when expanding fieldset in Drupal 7?

In drupal i have generated a list where each item is a fieldset with collapsible, that can contain extra information.
Because of the rather large list i want to avoid loading the extra information until a user clicks on the fieldset.
Best case scenario:
User clicks on collapsed fieldset.
Fieldset loads extra information.
Fieldset uncollapses.
I've copied and loaded the copy of collapse.js into my form, but I'm very new to js and jQuery, so I'm a little lost. If someone can show me how to call a function the first time the fieldset is expanded, I'm sure i can figure out the rest.
I've included the code from collapse.js:
(function ($) {
//Toggle the visibility of a fieldset using smooth animations.
Drupal.toggleFieldset = function (fieldset) {
var $fieldset = $(fieldset);
if ($fieldset.is('.collapsed')) {
var $content = $('> .fieldset-wrapper', fieldset).hide();
$fieldset
.removeClass('collapsed')
.trigger({ type: 'collapsed', value: false })
.find('> legend span.fieldset-legend-prefix').html(Drupal.t('Hide'));
$content.slideDown({
duration: 'fast',
easing: 'linear',
complete: function () {
Drupal.collapseScrollIntoView(fieldset);
fieldset.animating = false;
},
step: function () {
// Scroll the fieldset into view.
Drupal.collapseScrollIntoView(fieldset);
}
});
}
else {
$fieldset.trigger({ type: 'collapsed', value: true });
$('> .fieldset-wrapper', fieldset).slideUp('fast', function () {
$fieldset
.addClass('collapsed')
.find('> legend span.fieldset-legend-prefix').html(Drupal.t('Show'));
fieldset.animating = false;
});
}
};
//Scroll a given fieldset into view as much as possible.
Drupal.collapseScrollIntoView = function (node) {
var h = document.documentElement.clientHeight || document.body.clientHeight || 0;
var offset = document.documentElement.scrollTop || document.body.scrollTop || 0;
var posY = $(node).offset().top;
var fudge = 55;
if (posY + node.offsetHeight + fudge > h + offset) {
if (node.offsetHeight > h) {
window.scrollTo(0, posY);
}
else {
window.scrollTo(0, posY + node.offsetHeight - h + fudge);
}
}
};
Drupal.behaviors.collapse = {
attach: function (context, settings) {
$('fieldset.collapsible', context).once('collapse', function () {
var $fieldset = $(this);
// Expand fieldset if there are errors inside, or if it contains an
// element that is targeted by the uri fragment identifier.
var anchor = location.hash && location.hash != '#' ? ', ' + location.hash : '';
if ($('.error' + anchor, $fieldset).length) {
$fieldset.removeClass('collapsed');
}
var summary = $('<span class="summary"></span>');
$fieldset.
bind('summaryUpdated', function () {
var text = $.trim($fieldset.drupalGetSummary());
summary.html(text ? ' (' + text + ')' : '');
})
.trigger('summaryUpdated');
// Turn the legend into a clickable link, but retain span.fieldset-legend
// for CSS positioning.
var $legend = $('> legend .fieldset-legend', this);
$('<span class="fieldset-legend-prefix element-invisible"></span>')
.append($fieldset.hasClass('collapsed') ? Drupal.t('Show') : Drupal.t('Hide'))
.prependTo($legend)
.after(' ');
// .wrapInner() does not retain bound events.
var $link = $('<a class="fieldset-title" href="#"></a>')
.prepend($legend.contents())
.appendTo($legend)
.click(function () {
var fieldset = $fieldset.get(0);
// Don't animate multiple times.
if (!fieldset.animating) {
fieldset.animating = true;
Drupal.toggleFieldset(fieldset);
}
return false;
});
$legend.append(summary);
});
}
};
})(jQuery);
It looks to me like you'd have to override the whole Drupal.toggleFieldset function (just like when you are overriding a Drupal theme function.
You could perhaps add a class to the fieldset in FormAPI then catch it in the complete function of the $content.slideDown params and fire a custom function of yours, to add a 'loading' graphic and make your ajax request.
I'm assuming from your question that you are familiar enough with FormAPI/jQuery.ajax() to have a go. But let me know if not and i'll include some snippets
EDIT
Here is some example code, it'd take a quite a while to setup a test environment for this, so it'just a pointer (cant create a JS fiddle for this ;))
You might add your fieldset like this in PHP
$form['my_fieldset'] = array(
'#type' = 'fieldset',
'#title' = t('My fieldset'),
'#collapsible' = true,
'#collapsed' = true,
'#attributes' => array(
'class' => array('ajax-fieldset'),
'rel' => 'callback/url/path' // random attribute to store the link to a menu path that will return your HTML
)
);
$form['my_fieldset'] = array(
'#markup' => '<div class="response">loading...</div>'
);
You'll also obviously have setup a menu hook returning your themed data # callback/url/path. IMO it's better to return JSON data and theme them in with JS templating, but the Drupal way (for the moment at least) seems to be to render HTML in the menu hook callback function.
Then here is the JS. I've only included the altered complete function, rather than reproduce what you pasted. Add the complete function in to a copy of the code the re-specify the core Drupal function in your own JS file
$content.slideDown({
complete: function () {
Drupal.collapseScrollIntoView(fieldset);
fieldset.animating = false;
if($fieldset.hasClass('ajax-fieldset')) {
$.get(
Drupal.settings.basePath + $fielset.attr('rel'),
function(data) {
$fieldset.find('.response').html(data);
}
)
}
}
});
Or, rather than messing around with the collapsible function. just create your own fieldset without the collapsible/collapsed classes and implement from scratch yourself
....so.. something like that :)

Categories

Resources