How to Use Jquery Smart Wizard with some ajax pages - javascript

I am having difficulty in understanding how to use the jQuery smart wizard.
I have managed to get it going with static html for my first three tabs but I now want to add another tab which uses an ajax call to get the data based on the data I have collected in the previous tabs.
I know there are callback functions for leaving and entering steps but the documentation is unclear on how to use them (or it maybe I don't understand enough jQuery/JavaScript to correctly interpret the documentation)
The way I read it is I would use
$("#smartwizard").on("stepContent", function(e, anchorObject, stepIndex, stepDirection) {
// if this is the correct step index then
// do my ajax call using a promise
// (Which I do not understand fully how to do.
// I have never used a promise before.)
// and return my html. (Where is the html going to be put? is it the 'tabpanel'?)
return myHTMLfromtheajaxcall;
// else do nothing
// How do I stop it overriding the existing content?
// Do I return '' or false or what?
});
What and where do the parameters for the function come from. What is 'e'? What is 'anchorObject'? The other parameters are self explanatory.
I hope somebody can explain things in simple terms with some examples.
Thank you in advance.

Related

Promise object with alerts and return values causing errors

I have an interface that I'm working on that uses javascript to override the keyboard functionality. My initial question, before going into more depth of this issue is, when an alert is called, is there a way to have functions resolve after it without it missing timing, or am I just screwed in this sense.
I have tried using the Promise object but either I am doing something wrong or this is the wrong way to do it. I can get my meta data to reset if I simply just log a response in the callback function that is passed, but if I alert it, and I would assume the same if I did an ajax call, it fails.
So that is my issue, and I'm hoping this makes sense to someone.
I will explain the interface quickly so you can understand, i apologize ahead of time for the crude list layout:
overboard
constructor : Overboard
this.handle_keydown : gets the key event and makes the magic happen
this.handle_keyup : get the key event on up and clears the cache essentially
this.listen : generates the event listeners forkeyup and keydown
#return this
private : keyAction
the main engine of the Overboard class functionality. Checks to see if the option is set within the options object, checks for additional parameter sets, attempts the callback function, resets all meta data.
code: https://jsfiddle.net/gtqqewdd/14/
how you call the class
this is where I was running into the issue with the alert.
var override = overboard(window, {
a : {
ctrlKey : true,
callback : function(k){
if(k.ctrlKey){
alert('ctrl+a worked');
return true;
}
}
}
});
override.listen();
If I didn't explain anything well enough or clarification is needed on something, please let me know! Thank you in advance!
Opps misread the question. You can always use a library, which will be much less error prone, something like https://dmauro.github.io/Keypress/

Functionally test that an Element is visible (no elements covering) with Intern

I am currently using the JavaScript framework Intern to test my site, I am wanting to ensure that specific element's are truly visible. Intern currently has an option "isDisplayed" which half does this. But what I am wanting to also do is check that it would be truly visible to the user and that any other elements on the page do not cover (perhaps by z-index issues) etc.
Does anyone have an suggestions?
Thanks in advance.
Usually, it's better to focus on your unit tests and make sure the styles you expect to be setting are coming back correctly from the utility/helper functions you use to do so. Otherwise, you're going to end up testing things you probably don't mean to, such as functionality of the browser itself to compute the styles. Therefor, it's bad practice in many situations.
However, if you do need this, such as when testing a 3rd party JavaScript library against a customer's site, Intern's Leadfoot provides a .execute() method you'll want to use.
Example:
return this.remote // represents the browser
.get('mysite.com') // navigate to a page
.execute( // send a callback to the browser
function (selector) {
var elem = document.querySelector(selector),
result;
// collect some data for analysis...
result = getComputedStyle(elem).zIndex;
return result;
},
['div'] // arguments to send to the remote callback
)
.then(
function (zIndex) {
// analyze the data and make assertions about it...
assert(zIndex > 999);
}
);
Please note: This is awesome but be careful. Most of your test runs inside of Node.js, but the callback to .execute() does not and so it does not have access to any of your previously defined variables, etc.
As for strategies to determine when an element is truly visible to the user, it's very subjective, but getBoundingClientRect() is going to be your friend for determining when one element is overlapping another. There are good techniques here: Determine visibility / real z-index of html elements

Dynamically Included Javascript and Dependencies

So, as a sort of exercise for myself, I'm writing a little async script loader utility (think require.js, head.js, yepnope.js), and have run across a little bit of a conundrum. First, the basic syntax is like this:
using("Models/SomeModel", function() {
//callback when all dependencies loaded
});
Now, I want to know, when this call is made, what file I'm in. I could do it with an ajax call, so that I can mark a flag after the content loads, but before I eval it to mark that all using calls are going to be for a specific file, then unset the flag immediately after the eval (I know eval is evil, but in this case it's javascript in the first place, not json, so it's not AS evil). I'm pretty sure this would get what I need, however I would prefer to do this with a script tag for a few reasons:
It's semantically more correct
Easier to find scripts for debugging (unique file names are much easier to look through than anonymous script blocks and debugger statements)
Cross-domain requests. I know I could try to use XDomainRequest, but most servers aren't going to be set up for that, and I want the ability to reference external scripts on CDN's.
I tried something that almost got me what I needed. I keep a list of every time using is called. When one of the scripts loads, I take any of those using references and incorporate them into the correct object for the file that just loaded, and clear the global list. This actually seems to work alright in Firefox and Chrome, but fails in IE because the load events seem to go off at weird times (a jQuery reference swallowed a reference to another type and ended up showing it as a dependency). I thought I could latch on to the "interactive" readystate, but it doesn't appear to ever happen.
So now I come asking if anybody here has any thoughts on this. If y'all want, I can post the code, but it's still very messy and probably hard to read.
Edit: Additional usages
//aliasing and multiple dependencies
using.alias("ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js", "jQuery");
using(["jQuery", "Models/SomeModel"], function() {
//should run after both jQuery and SomeModel have been loaded and run
});
//css and conditionals (using some non-existant variables here)
using.css({ src: "IEFix", conditionally: browser === "MSIE" && version < 9 });
//should include the IEFix.css file if the browser is IE8 or below
and to expound more on my response below, consider this to be file A (and consider the jquery alias from before to be there still):
using(["jQuery", "B"], function() {
console.log("This should be last (after both jQuery and B have loaded)");
console.log(typeof($));
});
Then this would be B:
using("C", function() {
console.log("This should be second");
});
And finally, C:
console.log("This should be first");
The output should be:
This should be first
This should be second
This should be last (after both jQuery and B have loaded)
[Object Object]
Commendable that you are taking on such an educational project.
However, you won't be able to pull it off quite the way you want to do it.
The good news is:
No need to know what file you are in
No need to mess with eval.
You actually have everything you need right there: A function reference. A callback, if you will.
A rough P-code for your using function would be:
function using(modules, callback) {
var loadedModules = []
// This will be an ajax call to load things, several different ways to do it..
loadedModules[0] = loadModule(modules[0]);
loadedModules[1] = loadModule(modules[1]);
// Great, now we have all the modules
// null = value for `this`
callback.apply(null, loadedModules);
}

Long running load operations in durandal

I'm trying to work out where the best place to run a long-running load operation is using Durandal.
From what I can tell, the general recommendation for loading data is in the ViewModel's activate method, which is what I usually do - something like:
viewModel.activate = function () {
var loadPromise = myService.loadData();
return $.when(loadPromise).then(function (loadedData) {
viewModel.data(data);
});
};
I know that if I don't return the promise here, then there's usually problems with the bindings - as this question and answer indicates.
However, executing a long running load operation in the activate method makes the app "freeze" while the load operation completes. For example, what if my load was now something like this?
viewModel.activate = function () {
// All loads return a promise
var firstLoad = myService.loadFirstData();
var secondLoad = myService.loadSecondData();
var thirdLoad = myService.loadThirdDataWhichTakesAges();
return $.when(firstLoad, secondLoad, thirdLoad).then(function (one, two, three) {
viewModel.one(one);
viewModel.two(two);
viewModel.three(three);
});
};
In this scenario, the URL is updated to reflect the page which is being loaded, but the page content still shows the previous page (which is what I mean by "freezes").
Ideally, it would be good if the URL should change to the new page, and the page content should show the new page too (even though the data for that page has not yet been returned). Then, as each load operation returns, the relevant part of the page should be updated when the data is bound into the view model.
Is there a recommended way for achieving this inside Durandal?
My current solution is to kick-off the load in the activate method, and then populate the data in the viewAttached method:
var loadPromise;
viewModel.activate = function () {
// All loads return a promise
var firstLoad = myService.loadFirstData();
var secondLoad = myService.loadSecondData();
var thirdLoad = myService.loadThirdDataWhichTakesAges();
loadPromise = $.when(firstLoad, secondLoad, thirdLoad);
// Don't return the promise - let activation proceed.
};
viewModel.viewAttached = function () {
$.when(loadPromise).then(function (one, two, three) {
viewModel.one(one);
viewModel.two(two);
viewModel.three(three);
});
};
It seems to work, but I remember reading somewhere that relying on viewAttached wasn't a good solution. I'm also not sure if there is potential for a race condition since I'm allowing the activate to proceed.
Any other recommendations?
You don't have to return a promise but in that case you must handle this in you knockout bindings so you woun't bind to elements that are undefined. You can try to get rid of that 'return' in activate but add a property indicating if model is still loading. Something like this:
viewModel.isLoading = ko.observable(false);
viewModel.activate = function () {
isLoading(true);
var loadPromise = myService.loadData();
$.when(loadPromise).then(function (loadedData) {
viewModel.data(data);
isLoading(false);
});
};
And then, in your view, you can have a section that shows up when view is still loading and one that shows up when loading is done. Sometinhg like:
<div data-bind:"visible: isLoading()">Loading Data....</div>
<div data-bind:"visible: !isLoading()">Put your regular view with bindings here. Loading is done so bindings will work.</div>
Which version of Durandal are you using? In Durandal 2.0.0pre you would be allowed NOT returning a promise in activate so that the composition of the view (without data) could happen immediately.
You might consider refactoring viewModel.one etc. into a module that returns a constructor function, so that each one, two, three would be responsible for retrieving their own data. That way you first two calls wouldn't have to wait on loadThirdDataWhichTakesAges. That would make sense in scenarios where one, two, three are not heavily depend on each other.
For reference; I posted a similar question on the Durandal Google Group (effectively asking if using activate and viewAttached in this manner is an OK idea) and got this reply from Rob Eisenberg:
That will probably work. The problem is that Knockout will destroy
databindings on elements if the properties are updated and the element
isn't currently in the document. This can happen depending on the
timing of the async code. Because of the way composition worked in
1.x, this would cause problems if you didn't return the promise from your activate function. It should work better in viewAttached, but
depending on the nature of your composition, the view may be attached
to its parent, but still not in the document. It depends on the depth
of the composition. So, you could encounter issues with this too if
you have this in a deeply composed module. Unfortunately, there isn't
a clean way about it in Durandal 1.x due to the knockout behavior. In
Durandal 2.x we have reworked composition so that this problem is
non-existent and returning the promise is no longer necessary (though
you can still do it). Durandal 2.0 will be releasing in about two
weeks.

using JQuery to fetch an html document and parse it into a DOM tree

So essentially I'm trying to build my own version of GitHub's tree slider. The relevant Javascript/JQuery code is:
// handles clicking a link to move through the tree
$('#slider a').click(function() {
history.pushState({ path: this.path }, '', this.href) // change the URL in the browser using HTML5 history module
$.get(this.href, function(data) {
$('#slider').slideTo(data) // handle the page transition, preventing full page reloads
})
return false
})
// binds hitting the back button in the browser to prevent full page reloads
$(window).bind('popstate', function() {
$('#slider').slideTo(location.pathname)
}
Ok, hopefully that's understandable. Now here's my interpretation of what's going on here, followed by my problem/issue:
The callback function for the GET request when navigating through the tree is the slideTo method, and an HTML string is passed in as an argument to that function. I'm assuming that slideTo is a function defined elsewhere in the script or in a custom library, as I can't find it in the JQuery documentation. So, for my purposes, I'm trying to build my own version of this function. But the argument passed into this function, "data", is just the string of HTML returned from the GET request. However, this isn't just a snippet of HTML that I can append to a div in the document, because if I perform the same GET request (e.g. by typing the url into a web browser) I would expect to see a whole webpage and not just a piece of one.
So, within this callback function that I am defining, I would need to parse the "data" argument into a DOM so that I can extract the relevant nodes and then perform the animated transition. However, this doesn't make sense to me. It generally seems like a Bad Idea. It doesn't make sense that the client would have to parse a whole string of HTML just to access part of the DOM. GitHub claims this method is faster than a full page reload. But if my interpretation is correct, the client still has to parse a full string of HTML whether navigating through the tree by clicking (and running the callback function) or by doing full page loads such as by typing the new URL in the browser. So I'm stuck with either parsing the returned HTML string into a DOM, or ideally only fetching part of an HTML document.
Is there a way to simply load the fetched document into a Javascript or JQuery DOM object so I can easily manipulate it? or even better, is there a way to fetch only an element with an arbitrary id without doing some crazy server-side stuff (which I already tried but ended up being too spaghetti code and difficult to maintain)?
I've also already tried simply parsing the data argument into a JQuery object, but that involved a roundabout solution that only seems to work half the time, using javascript methods to strip the HTML of unwanted things, like doctype declarations and head tags:
var d = document.createElement('html');
d.innerHTML = data;
body = div.getElementsByTagName("body")[0].innerHTML;
var newDOM = $(body);
// finally I have a JQuery DOM context that I can use,
// but for some reason it doesn't always seem to work quite right
How would you approach this problem? When I write this code myself and try to make it work on my own, I feel like no matter what I do, I'm doing something horribly inefficient and hacky.
Is there a way to easily return a JQuery DOM object with a GET request? or better, just return part of a document fetched with a GET request?
Just wrap it; jQuery will parse it.
$(data) // in your callback
Imagine you want to parse a <p> tag in your normal HTML web page. You probably would use something like:
var p = $('<p>');
Right? So you have to use the same approach to parse an entire HTML document and then, navigate through the DOM tree to get the specific elements you want. Therefore, you just need to say:
$.get(this.href, function(data) {
var html = $(data);
// (...) Navigating through the DOM tree
$('#slider').slideTo( HTMLportion );
});
Notice that it also works for XML documents, so if you need to download via AJAX a XML document from the server, parse the inner information and display it on the client-side, the method is exactly the same, ok?
I hope it helps you :)
P.S: Don't ever forget to put semicolons at the end of each JavaScript sentence. Probably, if you don't put them, the engine would work but it is better to be safe and write them always!

Categories