Jquery Tab control - reloading all tabs that have been clicked - jquery-ui

Folks,
I'm using Jquery UI - Tab.
I've an edit screen where the main form and tabs have been shown below. Now when I navigate from one record to another the Ajax call goes to the server to fetch new main record.
Now I want to refresh the tab below, with new record id, as well so what I've done is the following:
var jsonTabMetaData = [{"HtmlName":"Notes","Text":"Notes","Url":"../Notes.rl?moduleName=glbModuleName&moduleRecordID=glbModuleRecordID&sessionID=glbSessionID&company=glbCompanyName","Selected":false,"ModuleName":null,"ModuleRecordID":0},{"HtmlName":"AddressTel","Text":"Address & Telephone","Url":"../PhysicalAddress.rl/QuickAddress?moduleName=glbModuleName&moduleRecordID=glbModuleRecordID&sessionID=glbSessionID&company=glbCompanyName","Selected":false,"ModuleName":null,"ModuleRecordID":0},{"HtmlName":"Sendout","Text":"Send outs","Url":"../Sendouts.rl/List?moduleName=glbModuleName&moduleRecordID=glbModuleRecordID","Selected":false,"ModuleName":null,"ModuleRecordID":0},
function fnReboundTabs() {
$('#tabs a').each(function (index) {
var newUrl = jsonTabMetaData[$(this).attr("data-index")].Url;
newUrl = newUrl.replace("glbModuleRecordID", glbModuleRecordID);
newUrl = newUrl.replace("glbModuleName", glbModuleName);
newUrl = newUrl.replace("glbSessionID", glbSessionID);
newUrl = newUrl.replace("glbCompanyName", glbCompanyName);
this.href = newUrl;
});
`
if (firstTimeReboundTabs) {
firstTimeReboundTabs = false;
$("#tabs").tabs({
select: function (event, ui) {
},
cache: true,
event: '<%= (UI.Web.Helper.SessionMaster.OpenTabOnMouseOver) ? "mouseover": "click" %>',
async: false,
ajaxOptions: {
cache: false,
success: function () { },
error: function (xhr, status, index, anchor) {
$(anchor.hash).html(
"Couldn't load this tab. Should you see this error again, please notify admin.");
}
}
});
}
`
Now the problem is this:
When I navigate, the value changes in the URL, but the tab click request is opening into the new screen.
I.e. It is not working as Ajax call.
The Main screen goes and the URL open as a new URL in the browser address bar.

If I'm following this correctly, you want to change the URL of a tab's content when recordId is changed, and reload that tab without reloading the entire page.
This is possible using two methods of your tabs object:
To change the url, use the .tabs("url", index, url) where:
index = the index of the tab you are updating
url = the string of the new URL
To reload the tab's content at any time use .tabs("load", index)
index = the index of the tab you are updating.
Using these together should do what you want. I.e. when you have a new recordId do:
mytabs.tabs("url", i, 'mypage?recordId' + newRecordId)
mytabs.tabs("load", i)
The documentation is here, under the 'methods' tab: jqueryui docs

This is what I've done now:
function fnReboundTabs() {
for (var idx = 0; idx < jsonTabMetaData.length; idx++) {
var newUrl = jsonTabMetaData[idx].Url;
newUrl = newUrl.replace("glbModuleRecordID", glbModuleRecordID);
newUrl = newUrl.replace("glbModuleName", glbModuleName);
newUrl = newUrl.replace("glbSessionID", glbSessionID);
newUrl = newUrl.replace("glbCompanyName", glbCompanyName);
$("#tabs").tabs("url", idx, newUrl)
}
if (isNaN($('#tabs').tabs().tabs('option', 'selected')))
{ }
else {
$("#tabs").tabs("load", $('#tabs').tabs().tabs('option', 'selected'))
}
}
This function will then be called by when the main record has been downloaded at client side - JSON/ AJAX based.

Related

photoswipe returning to initial hash of current page instead of last scroll position

On IOS, when I close photoswipe to return to the page, it wont return to the scroll position I was at when I clicked the thumbnail.
Instead the page scrolls back to the # which was specified when I initially called the page.
For example if photoswipe is on www.somepage.html, and I navigate to the page using:
www.somepage.html#footer
and then scroll up and click a thumnail in #middle of page, on closing photoswipe, the page scrolls back down to the footer.
I've tried disabling history in the photswipe options, and i've also tried clearing the hash data from the url using:
//clear hash
//$(document).ready(function (e) {
// window.location.hash = '';
// window.history.pushState("", document.title, window.location.pathname);
//
//});
But none of it seems to work. If I navigate to the page without the # in the page, everthing is fine.
I'm guessing I may have to pass a variable in the url instead of the # and scroll to the div in question via javascript?
I already have the javascript in place to scroll, but I'm not sure how to read the variable from the url and then use it's value in Javascript.
If this is likely to be the best fix for the issue, could anyone give an example of the javascript code needed?
Here's my current scroll code:
$(function () {
$('a[href*=#]:not([href=#],[data-toggle],[data-target],[data-slide])').click(function () {
if (location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '') || location.hostname == this.hostname) {
var target = $(this.hash);
target = target.length ? target : $('[name=' + this.hash.slice(1) + ']');
if (target.length) {
$('html,body').animate({
scrollTop: target.offset().top
}, 1000);
return false;
}
}
});
});
If anyone else has the same issue, I've managed to fix this by passing the div id to the page in the query string rather than using a #.
Here's the code:
$(window).ready(function () {
if (document.location.search.length) {
target = getUrlVars()["id"];
scrollToID('#' + target, 750);
} else {
return;
}
//target = $url().param('id');
//if (target == '') return;
});
function getUrlVars() {
var vars = [], hash;
var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
for (var i = 0; i < hashes.length; i++) {
hash = hashes[i].split('=');
vars.push(hash[0]);
vars[hash[0]] = hash[1];
}
return vars;
}
// scroll function
function scrollToID(id, speed){
var offSet = 100;
var targetOffset = $(id).offset().top - offSet;
var mainNav = $('#main-nav');
$('html,body').animate({scrollTop:targetOffset}, speed);
if (mainNav.hasClass("open")) {
mainNav.css("height", "1px").removeClass("in").addClass("collapse");
mainNav.removeClass("open");
}
}
if (typeof console === "undefined") {
console = {
log: function() { }
};
}

turbolinks change url manually

I use endless scrolling in my rails 4 application.
The code automatically updates the url when I scroll a list:
var loading = false;
$(function() {
function nearBottomOfPage() {
return $(window).scrollTop() > $(document).height() - $(window).height() - 200;
}
$(window).scroll(function(){
if (loading || $(".infinite-scroll").length == 0) {
return;
}
if(nearBottomOfPage()) {
loading=true;
var qstring = parseInt(getParameterByName('page'));
if(isNaN(qstring)) {qstring = 1;}
qstring++
updateQueryString('page',qstring);
var url = $(".infinite-scroll").data("update-url")
var q_string = window.location.search
if(url.indexOf("?") != -1) {
q_string = q_string.replace("?","&")
}
url = url + q_string
$.ajax({
url: url,
type: 'get',
dataType: 'script',
success: function() {
$(window).sausage('draw');
loading=false;
}
});
}
});
$(window).sausage();
});
The updateQuerystring function replaces the state like so:
window.history.replaceState({turbolinks: true, position: Date.now()}, document.title, new_url);
This is a hack to try to get something that worked in rails 3 to work in rails 4...
It seems to work but it is not clean and has some issues.
The main issue is the fact that the scroll position is not reminded. When clicking the back button, I come back to the top of the page.
Is there a way to do this cleaner. Ideally, instead to do window.history.replaceState I'd like to do Turbolinks.replaceState(newUrl) or something like that.
But if I could find a way to remember the scroll position, It would be great already.
Thank you!
Triggering a Turbolinks visit manually
You can use Turbolinks.visit(path) to go to a URL through Turbolinks.
https://github.com/rails/turbolinks#triggering-a-turbolinks-visit-manually

Knockout bindings are not working with jsTree

I'm trying to get jsTree (1.0-rc3) working with Knockout.js (2.2.1).
See example jsFiddle: http://jsfiddle.net/adeconsulting/qfr6A/
Note: I've included several JS resources in the Fiddle to match as close as possible my Visual Studio project, in case there's a conflict between libraries which might be causing this problem.
Run the Fiddle and navigate through the jsTree, it's a list of servers by their physical location and type. It helps to have Firebug's console open so you can see the ajax calls and responses. When you click a leaf node, an ajax call is made to retrieve the server details and display a form whose values use Knockout bindings. I hide the form when a non-leaf node is selected.
It works the first time you click a leaf node. After that, Knockout does not update the form for leaf node clicks. However, if you happen to click the Edit button, then all of a sudden the most recent server details ARE displayed.
I'm thinking that there's a conflict between jsTree and Knockout bindings, but don't know where to start troubleshooting what that might be.
Since stackoverflow apparently requires at least one code block, here's the JavaScript portion of the Fiddle:
// Global vars:
var prevJsTreeNodeId = null;
var serverModelBindingsApplied = false;
var serverLoadInProgress = false;
/*
* The knockout.js view model
*/
var ServerViewModel = function () {
// Data
var self = this;
self.IsReadOnly = ko.observable(true); // the form's input mode
self.btnEditSave = ko.observable("Edit"); // the Edit/Save button text
self.Server = ko.observable({}); // the Server object
// Operations
self.setEditable = function () {
self.IsReadOnly(false);
self.btnEditSave("Save");
};
self.setReadOnly = function () {
self.IsReadOnly(true);
self.btnEditSave("Edit");
};
self.doEditSave = function () {
var flag = self.IsReadOnly();
if (flag) {
// switch to Edit mode
self.setEditable();
}
else {
// switch back to readOnly
self.setReadOnly();
}
};
// use ajax to update the knockout.js view model's Server object for the specified server name
self.load = function (serverName) {
if (!serverLoadInProgress) {
serverLoadInProgress = true;
// use ajax to retrieve the server's details
var data = {
json: JSON.stringify({
ServerName: serverName,
PrimaryIP: "1.2.3.4",
BrandDesc: "Dell",
OSDesc: "Windows 2003 Server",
Location: "xyz"
}),
delay: 1
};
$.ajax({
url:"/echo/json/",
data:data,
type:"POST",
success:function(response)
{
console.log(response);
window.ServerViewModelInstance.Server = ko.mapping.fromJS(response);
// apply bindings the first time we retrieve a Server object
if (!serverModelBindingsApplied) {
ko.applyBindings(window.ServerViewModelInstance,
document.getElementById('servercontent'));
serverModelBindingsApplied = true;
}
else {
// hmmm... updating the view model's .Server property doesn't trigger the
// form to be updated, yet if we click the Edit button, the new values
// suddenly appear, so try emulating that here...
self.setReadOnly();
}
}
});
serverLoadInProgress = false;
}
};
}; // ServerViewModel
/*
* document load
*/
$(function () {
// configure the jsTree
$("#divtree")
.jstree({
"themes": { "theme": "default", "dots": true, "icons": true },
"plugins": ["themes", "html_data", "ui", "types"],
"types": {
"type_attr": "tag", // the attribute which contains the type name
"max_depth": -2, // disable depth check
"max_children": -2, // disable max children check
"valid_children": ["root"],
"types": {
"root": {
"valid_children": ["level1"]
},
"level1": {
"valid_children": ["level2"],
"start_drag": false,
"move_node": false,
"delete_node": false,
"remove": false
},
"level2": {
"valid_children": ["leaf"],
// use the theme icon for the level2 nodes
"start_drag": false,
"move_node": false,
"delete_node": false,
"remove": false
},
"leaf": {
"valid_children": "none"
}
}
}
});
// register to receive notifications from #divtree when a jsTree node is selected
$("#divtree").bind("select_node.jstree", function (event, data) {
// data.rslt.obj is the jquery extended node that was clicked
var key = data.rslt.obj.attr("key");
var id = data.rslt.obj.attr("id");
if (id == prevJsTreeNodeId) {
// user clicked the same node, nothing to do
return;
}
prevJsTreeNodeId = id;
// when a jsTree node is selected, reset the knockout.js view model to read only
window.ServerViewModelInstance.setReadOnly();
var idx = key.indexOf("Server");
if (idx === 0) { // "Server|servername"
// show the "servercontent" div
$("#servercontent").show();
// display the server details
var serverName = key.substr(idx + 7, key.length);
window.ServerViewModelInstance.load(serverName);
}
else {
// hide the "servercontent" div
$("#servercontent").hide();
}
});
// hide the "servercontent" div
$("#servercontent").hide();
// instantiate the knockout.js view model
window.ServerViewModelInstance = new ServerViewModel();
}); // document ready
// initialization timer routine to select the main jsTree node
setTimeout(function () {
// open the root node
$.jstree._reference("#divtree").open_node("#root");
}, 500);
Sorry for my bad formatting below - this editor is not my friend... :-/
If I understand you right, the detail panel for a clicked tree node isn't updated with the correct data - right?
Try to do the following:
change:
window.ServerViewModelInstance.Server = ko.mapping.fromJS(response);
to: window.ServerViewModelInstance.Server(response);
(e.g. not overwriting the initial ko.observable which you only binds once, instead updating its values)
and in view where you bind to the observables..
for instance instead of: ... "value: Server.ServerName, ...
change it to: ... "value: Server().ServerName, ...
(e.g executing the function before accessing the property)
It works and updates the form when clicking on a new server name node in the tree (tried in firefox)
A copy of your example with the modified code can be found at: http://jsfiddle.net/RZ92g/2/

Sending information to the content script for a context-menu

I've seen many questions regarding context-menu and two-way communication and it appears that I know the answer to my question... "you can't", but I'm going to try anyway.
On each page there is a modal div that is created by a page-mod. This modal is designed to show up when a user hovers over words in text nodes to give a translation of the word. This works perfectly and I don't have any problems with the page-mod.
What I want to do now is allow the user to highlight a selection of text, right click to bring up the context menu where my new menu item will be to "Translate Selection", and then display the selection in the modal div. Here's where the problems begin. I can respond to the context and click events in the content script, which is fine if I didn't have to do a translation. The translation is done by a web service and the content script cannot call a web service because the callbacks don't exist in the context of the content script because it is in a proxy sandbox. That means that all web service calls need to come from main.js (this is how it works in the page-mod). The problem is that the context-menu object in main.js does not have access to the DOM to update the content of the modal div and show it, and it cannot send information to the content script so that the content script can update the DOM and show the modal div. So how do I get the translation to the DOM from the add-on script for the context-menu?
Is what I want to do possible with the SDK, or do I have to undo many hours of work to put my project back into the "old school" way of doing things so I can get the context menu to work correctly?
This is what I have (the page-mod works, need help with the context-menu):
exports.main = function (options, callbacks) {
'use strict';
var myAppMenuItem,
myAppContextMenu,
myAppPanel,
myAppMod,
self = require('self'),
contextMenu = require('context-menu');
myAppMenuItem = require('menuitems').Menuitem();
if (myAppMenuItem.getAttribute('checked') === 'false') {
return;
}
myAppMod = require('page-mod');
myAppMod.PageMod({
include: '*',
contentScriptWhen: 'ready',
contentScriptFile: [self.data.url('jquery-1.7.2.min.js'), self.data.url('myAppmod.js')],
contentStyleFile: self.data.url('myAppmod.css'),
onAttach: function (worker) {
worker.port.on(
'translate',
function (data) {
require('request')
.Request({
url: 'http://api.microsofttranslator.com/V2/Ajax.svc/Translate',
content: {
appid : 'myappid',
to : data.to,
from : data.from,
text : data.text
},
onComplete: function (response) {
worker.port.emit('translation', { response : response.text, elementId : data.elementId });
}
})
.get();
}
);
}
});
myAppContextMenu = contextMenu.Item({
label: "Translate Selection",
context: contextMenu.SelectionContext(),
contentScriptFile : [self.data.url('jquery-1.7.2.min.js'), self.data.url('myAppcontextmenu.js')],
onMessage: function (data) {
require('request')
.Request({
url: 'http://api.microsofttranslator.com/V2/Ajax.svc/Translate',
content: {
appid : 'myappid',
to : data.to,
from : data.from,
text : data.text
},
onComplete: function (response) {
<what can I do here to send the information to the content script?>
}
})
.get();
}
});
};
Thank you to Wladimir! The following code does what I want it to:
In the main.js for the context-menu:
myAppContextMenu = contextMenu.Item({
label: "Translate Selection",
context: contextMenu.SelectionContext(),
contentScriptFile : [self.data.url('jquery-1.7.2.min.js'), self.data.url('myAppcontextmenu.js')],
onMessage: function (data) {
var text = require('selection').text;
require('request')
.Request({
url: 'http://api.microsofttranslator.com/V2/Ajax.svc/Translate',
content: {
appid : 'myappid',
to : data.to,
from : data.from,
text : text
},
onComplete: function (response) {
var index,
tabs = require('sdk/tabs');
for (index = 0; index < workers.length; index += 1) {
if (workers[index].tab === tabs.activeTab) {
workers[index].port.emit('selectionTranslation', { text: text, response : response.text, leftOffset : data.leftOffset, topOffset : data.topOffset });
}
}
}
})
.get();
}
});
and in the content script:
self.on(
'click',
function (node, data) {
'use strict';
var selectedElement = $(node),
messageData =
{
to : 'es',
from : 'en',
topOffset : selectedElement.offset().top + (selectedElement.height() / 2),
leftOffset : selectedElement.offset().left + (selectedElement.width() / 2)
};
self.postMessage(messageData);
}
);
There is a global workers array variable defined in the exports.main function that gets populated by the onAttach function of the page mod as so:
workers.push(worker);
worker.on(
'detach',
function () {
var index = workers.indexOf(worker);
if (index >= 0) {
workers.splice(index, 1);
}
}
);

Calling controller method from JQuery calls occurs twice and also returning error?

Hi guys i have posted a similar post before, but that is for another, now i face a strange and odd issue with my Jquery code. Here i was calling a controller method using Jquery but it is calling twice , so that may cause two entries in my db. Here is what i have written in my JQuery
<script type="text/javascript">
$('#btnSubmit').click(function () {
var instructorUrl = '#Url.Action("ApplyToBecomeInstructor", "InstructorApplication")';
var currentUser = '#Model.CurrentUserId';
var user = [];
var educationList = [];
var experience = $('#Experience').val();
var isWilling = $('#WillingToTravel').is(":checked");
$('#editorRows .editorRow').each(function () {
var education = {
UniversityOrCollege: $(this).find('.university').val(),
AreaOfStudy: $(this).find('.area').val(),
Degree: $(this).find('.degree').val(),
YearReceived: $(this).find('.year').val()
}
educationList.push(education);
});
var applicationFromView = {
EducationalBackgrounds: educationList,
CurrentUserId: currentUser,
Experience: experience,
WillingToTravel: isWilling
}
$.ajax({
type: 'POST',
url: instructorUrl,
dataType: 'JSON',
async: false,
data: JSON.stringify(applicationFromView),
contentType: 'application/json; charset=utf-8',
success: function (data) {
return false;
},
error: function (data) {
alert(xhr.status);
alert(thrownError);
alert(xhr.responseText);
return false;
}
});
});
</script>
and my controller action looks like this
[HttpPost]
public ActionResult ApplyToBecomeInstructor(InstructorApplicationViewModel applicationFromView)
{
Student thisStudent = this.db.Students.Where(o => o.StudentID == applicationFromView.CurrentUserId).FirstOrDefault();
List<PaulSchool.Models.EducationalBackground> educationList = new List<EducationalBackground>();
foreach (var educate in applicationFromView.EducationalBackgrounds)
{
var education = new Models.EducationalBackground
{
YearReceived = educate.YearReceived,
Degree = educate.Degree,
AreaOfStudy = educate.AreaOfStudy,
UniversityOrCollege = educate.UniversityOrCollege
};
educationList.Add(education);
}
var instructorApplication = new InstructorApplication
{
BasicInfoGatheredFromProfile = thisStudent,
Experience = applicationFromView.Experience,
EducationalBackground = new List<Models.EducationalBackground>(),
WillingToTravel = applicationFromView.WillingToTravel
};
instructorApplication.EducationalBackground.AddRange(educationList);
this.db.InstructorApplication.Add(instructorApplication);
this.db.SaveChanges();
return this.Redirect("Index");
}
Error message showing is JSON Parsing error.. but it is confusing to me.
I really wondered why this is happening, can anybody please take a look and help me?
This is what your code does:
$('#btnSubmit').click(function () { // attach a click handler for the button.
...
...
// Look for elements inside the button...
UniversityOrCollege: $(this).find('.university').val(),
Change from click to submit:
$('#formId').submit(function (e) {
...
// Now "this" is the form - not the button.
// Look for elements inside the <form>
UniversityOrCollege: $(this).find('.university').val(),
// Prevent the default form submition
return false // Or: e.preventDefault();
Another tip: use jQuery serialize function.
$('#btnSubmit').click() will fire every time the button is pressed. Often users double click buttons even though it only needs a single click or if you don't give any indication that something is happening they get impatient and click it again. You need some way to determine if the request has been made. There's ways to do this client and server side. The easiest client side way is to disable the button to prevent multiple clicks:
$('#btnSubmit').click(function () {
// Disable the button so it can't be clicked twice accidentally
$('#btnSubmit').attr('disabled', 'disabled');
//...
$.ajax({
//...
complete: function() {
// Make sure we re-enable the button on success or failure so it can be used again
$('#btnSubmit').removeAttr('disabled');
}
});
});

Resources