I have a page where i use jQuery to load some content into a div element
<div id="contents-box-in"></div>
jQuery code in page
$(document).ready(function() {
$("#contents-box-in").load("new-01.php", function() {
$('#contents-box-in').fadeIn(120);
});
var updateBoxData = function(data) {
$('#contents-box-in').fadeOut(100, function() {
$('#contents-box-in').html(data).fadeIn(130);
});
}
});
the content that i load is a form that needs to load a new page sending collected data from form
$('#form-buttons-next').click(function(e) {
var formData = new FormData($(this)[0]);
var formS = $.ajax({
url : 'new-02.php',
type : 'POST',
data : formData,
async : false,
cache : false,
processData : false,
contentType : false
});
formS.done(function(data) {
if (data != null) {
updateBoxData(data);
}
});
formS.fail(function(jqXHR, textStatus) {
alert("error");
});
});
since i do this in different step i would like to use a shared function contained in page that is loading the ajax content but i get updateBoxData is undefined
I guess that ajaxed content can't see parent container function
The easy way would be to load a different .js file containing shared function, i was wondering if is possible to access the updateBoxData from ajaxed contents
...i would like to use a shared function contained in page that is loading the ajax content but i get updateBoxData is undefined
I guess that ajaxed content can't see parent container function
No, that's not why. Your updateBoxData variable is scoped to the function it's declared in (your ready) callback. If you want it accessible globally, you'll need to make it global instead.
The problem is, though, the global namespace is already incredibly crowded. So if you put all of your useful functions there as globals, you're going to run into conflicts sooner or later.
For that reason, for now until browsers support ES2015 modules (which will take a while), I suggest giving yourself just one global symbol, something unlikely to conflict with other things, and assigning an object to it with properties for your various utility functions. E.g.:
var MyNiftyUtils = {
updateBoxData: function() {
// ...
}
};
Then you call that via MyNiftyUtils.updateBoxData. If the verbosity bothers you, no worries, just use a scoping function and assign it to a local:
(function() {
var u = MyNiftyUtils;
// ....
u.updateBoxData(/*...*/);
})();
(There are about 18 variations on that theme, this is just one of them.)
The function updateBoxData is defined inside a callback function you passed to .ready and hence its scope is limited to that function. Let us call this callback function Fx.
The click handler (the function passed to .click in the second part), which we call it Fy is defined outside of Fx and as a result does not have access to the variables defined in Fx (remember updateBoxData is a variable inside Fx).
That is why your code does not work. To get it working simply take updateBoxData out of the callback in .ready function:
$(document).ready(function() {
$("#contents-box-in").load("new-01.php", function() {
$('#contents-box-in').fadeIn(120);
});
});
function updateBoxData(data) {
$('#contents-box-in').fadeOut(100, function() {
$('#contents-box-in').html(data).fadeIn(130);
});
}
...
The rest is the same.
Related
I'm trying to create a custom component loader within knockout but I'm struggling with the view model. Essentially I want to remotely go grab both the HTML template and the JavaScript view model, but in this instance I don't want to use a traditional AMD module loader.
I've managed to get some of this working, specifically loading the HTML template but I can't figure out how to load the view model. Before I start here's my directory structure:
-- index.html
-- customerLoader.js
-- comps
-- myCustom.html
-- myCustom.js
So I've created my component loader like so. getConfig basically takes the name of the component and turns that into a path for the viewModel and the html template.
var customLoader = {
getConfig: function(name, callback) {
callback({ template: "comps/" + name + ".html", viewModel: "comps/" + name + ".js" });
},
loadTemplate: function(name, templateConfig, callback) {
console.log("loadTemplate", name, templateConfig);
$.get(templateConfig, function(data) {
callback(data);
});
},
loadViewModel: function(name, templateConfig, callback) {
console.log("loadViewModel", name, templateConfig);
$.getScript(templateConfig, function(data) {
callback(data);
});
}
};
ko.components.loaders.unshift(customLoader);
This successfully makes a request to load the template, which brings back some basic content. What I'm struggling with is the view model. I'm not sure what should be in the target of my JavaScript file?
I assumed that I'd want to return a function that would take some parameters, most likely a params object. However if I try and do this I get an error, telling me the JavaScript is invalid:
Uncaught SyntaxError: Illegal return statement
This is the current content I've got that is producing this error:
return function(params) {
console.log("myCustom.js", name, viewModelConfig);
// Add a computed value on
params.bookNum = ko.computed(function() {
switch(this.title()) {
case "A": return 1;
case "B": return 2;
case "C": return 3;
default: return -1;
}
});
//ko.components.defaultLoader.loadViewModel(name, viewModelConstructor, callback);
};
So ultimately I'm not sure how to achieve this, but I guess there are 3 basic questions that explain the gaps in my understanding:
What should my "view model" JavaScript file contain exactly? A function? An object? etc...
Do I need to call the ko.components.defaultLoader.loadViewModel at all?
Within my customLoader what should loadViewModel() be doing with the result of the jQuery callback? I'm not sure if I get back a JavaScript object, or just a string?
I'm open to achieve this in a different way if need be (e.g. not using jQuery but getting files a different way), but I don't want to use a module loader (e.g. require.js/curl.js in this instance).
First lets figure out what is happening...
From the docs:
This ($.getScript()) is a shorthand Ajax function, which is equivalent to:
$.ajax({
url: url,
dataType: "script",
success: success
});
And from jQuery.ajax():
...
dataType: ...
"script": Evaluates the response as JavaScript and returns it as plain text.
So your code is fetched, evaluated and then would have been returned as text, but evaluation first fails because you can't return if you're not within a function.
So what can be done? There are several options:
Use a module loader.
jQuery isn't a module loader, and as such it doesn't have the ability to parse fetched code and create a value / object from that code. A module loader is designed specifically for this task. It will take a script written in a specific pattern and "evaluate" it into a value (typically an object with 1 or more properties).
Change your script to a legal script
Because it's illegal to have a return statement in global code, your current code fails. You could however create a named function (or a variable with a function expression) and then use that name to reference that function. It could look like this:
function myCreateViewModel(param) {
// whatever
}
And the usage would be:
$.getScript(templateConfig, function() {
callback(myCreateViewModel);
});
The downside here is that if you ever go through that code path twice in the same page, your script will overwrite the old declaration. That might not ever be a problem, but it feels dirty.
Not use $.getScript(), use $.ajax() (or $.get()) with dataType: 'text' and evaluate yourself.
Remove the return from your code, and wrap it with an eval(). It will be evaluated as a function expression, the return value of the eval will be your function, and you could pass that directly to the callback:
$.get({
url: templateConfig,
dataType: 'text',
success: function(text) {
callback(eval(text));
}
});
This will work, but it will use the frowned upon eval(), which is exposing you to various risks.
I have created a javascript file that contains the following:
(function ($) {
//Define a Drupal behaviour with a custom name
Drupal.behaviors.jarrowDynamicTextfieldValidator = {
attach: function (context) {
//Add an eventlistener to the document reacting on the
//'clientsideValidationAddCustomRules' event.
$(document).bind('clientsideValidationAddCustomRules', function(event){
//Add your custom method with the 'addMethod' function of jQuery.validator
//http://docs.jquery.com/Plugins/Validation/Validator/addMethod#namemethodmessage
jQuery.validator.addMethod("typeValidator", function(value, element, param) {
...bunch of code here...
}, jQuery.format('Field can not be empty'));
});
}
};
})(jQuery);
What I would like to do is add an change listener to a select box so that when the selection changes it would call this validation function. I am not sure if I can do this since the validation code is buried within several functions. Is this possible?
the way your original code is showing it, no, you wouldn't be able to call any of those functions because they're anonymous and are within the scope of the parent functions.
If you were to declare a variable for the function outside of the function that calls it, then you'd be able to reuse the function, because it will be global to the scope of the other function. Note: if you wanted the variable to be completely global, or rather, have it be accessible no matter where you are in the code, just don't write the var in front of the variable, it will then be "global", which is actually the equivalent of putting the variable within the window namespace.
Here's an example of that, using your code:
(function ($) {
var customCallback = function(event){
//Add your custom method with the 'addMethod' function of jQuery.validator
//http://docs.jquery.com/Plugins/Validation/Validator/addMethod#namemethodmessage
jQuery.validator.addMethod("typeValidator", function(value, element, param) {
...bunch of code here...
}, jQuery.format('Field can not be empty'));
};
//Define a Drupal behaviour with a custom name
Drupal.behaviors.jarrowDynamicTextfieldValidator = {
attach: function (context) {
//Add an eventlistener to the document reacting on the
//'clientsideValidationAddCustomRules' event.
$(document).bind('clientsideValidationAddCustomRules', customCallback);
}
};
//now you can use that function again...
$('#something').on('someEvent', customCallback );
})(jQuery);
Please note that you'll have to make some adjustments to that function to make sure all of your variables are available and things like that due to variable scope. So, this may need some tweaking to make it work for your scenario.
Normally you wouldn't be able to call that anonymous function without modifying the code a little, however that seems to be the way of registering custom validation rules for the jQuery Validation Plugin and once registered, you can definitely use that custom rule through the plugin's API.
For instance, the following code adds a custom rule:
jQuery.validator.addMethod("typeValidator", function(value, element, param) {
...bunch of code here...
}, jQuery.format('Field can not be empty'));
Now you can initialize the plugin on your form and call the valid function to validate the form.
$('#someForm').validate({
rules: {
someField: {
typeValidator: true //use the custom validation rule
}
}
});
Now you can check if the form is valid using $('#someForm').valid().
Have a look at the plugin's API for more infos.
i am using Modernizr to load ressources to construct a jQuery layout.
The problem is that i cannot retrieve my $('body').layout() variable since it is inside one of the loaded resources (in script/webframe.js). I tried :
var myLayout;
function loadDefaultLayout() {
Modernizr.load([{
load: ['stylesheets/jquery.layout.css',
'script/jquery.layout.min.js',
'script/webframe.js'],
complete: function () {
myLayout = onDesktopWebFrameLoadComplete();
}
}]);
alert(myLayout.options.west.resizable);
}
Where onDesktopWebFrameLoadComplete is in script/webframe.js and returns the layout.
I tried moving alert(myLayout.options.west.resizable); just after onDesktopWebFrameLoadComplete and the alert was showing true. But when I move it out of the load() scope, I have an undefined error (for myLayout variable).
My question is :
I would like to know if it is possible to retrieve a variable outside of Modernizr.load() complete function scope.
My problem was only that I did not define my global variable in the right html file that called the function from the JavaScript file.
So in summary. Define your variable this way before everything else :
window.myLayout;
Then add your resource that will change your variable (that contains the load() function)
Change your variable in this resource : window.myLayout = "something-else";
You can now use it in the html file that included your resource.
I am using MVC3 with heavy usage of ajax to get Partial Views. If Partial view contains JavaScript then it is added as a new js file as shown in snapshot:
so If I have a js function:
function checkValue(){
//do work
}
on ajax call a new dynamic JS file will be added contained this function and it conflicts with old once.
myfile.js contained:
function checkValue(){
//do work
}
and 1.js (dynamic file) will contain it too
function checkValue(){
//do work
}
So when I call it due to presence in old file it call already present function which is outdated. How to solve this situation like new JavaScript replace old one.
Thanks
You can check whether something is defined and redefine it only if it is not:
var checkValue = checkValue || function () {
//do work
};
If you want your definitions to override each-other instead of defining the function with a name, define them on the global object each time:
window.checkValue = function () {
//do work
};
Sorry I couldn't be anymore specific with the title.
I'm building a web-site (personal), which displays different content to the user depending on the query string that is used in the url.
e.g. page=home.html would display home.html
The websites Javascript is wrapped inside an object, with each value containing different data, some pseudo code:
(function(){
var wrapper = {
init: function(){
//Runs on document ready
this.foo();
this.nav.render();
},
foo: function(){
//Some functionality goes here for the website, e.g. Display something from an API
},
nav: {
//Functionality to handle the navigation, has different properties
config: {
//Contains the config for nav, e.g. page names + locations
dir: '/directory/to/content/',
pages: {
page_name: wrapper.nav.config.dir + 'page_value'
}
},
render: function(){
//some code
},
routes: function(){
//some code}
}
}
};
$(function(){
wrapper.init();
});
})();
My problem is that I'm trying to prepend the dir value to each of the page values (inside the object where the pages are defined), expecting to get the output of (in this pseudo code case) of directory/to/content/page_value, but instead dir is undefined when I'm trying to access it, I've tried the following to achieve what I want:
wrapper.nav.config.dir + 'page_value'
I've been playing around with the last 30 minutes trying to find out what I'm doing wrong, and even thought about hard-coding the URL in for each page.
The reasoning for wanting to do this is that my local development server and web host have different directory structures, so I don't want to re-write the URL's each time I want to develop + publish. As for why everything is wrapped inside an object, I thought it would be easier to maintain this way.
Hopefully the answer is simple and it's just an amateur mistake / lack of understanding.
The issue is that you can't refer to a variable that is being defined in that very definition.
So, inside the definition of wrapper, you can't refer to wrapper. And, inside the definition of config, you can't refer to config either and so on.
The usual design pattern for solving this is to initialize as much as you can in the declaration of your data structure and then do the rest in .init() when you can freely access all of it.
Change the first two lines to:
var wrapper = null;
(function(){
wrapper = {
Otherwise, the wrapper is a local variable to your anonymous function.
The problem is that you're still busy defining the wrapper when you ask for its value, which is why it's still undefined.
The code below fails too:
var x = {
y:"1",
z:x.y
}
Why not:
//...
init: function(){
//Runs on document ready
this.foo();
var config = this.nav.config;
for (var page in config.pages) {
config.pages[page] = config.dir + config.pages[page];
}
},
//...