DOJO: Explanation of function parameters - javascript

So I asked an earlier question (Original Question). I got a great answer that did exactly what I wanted. However, since I am new to Javascript/Dojo, I wasn't able to fully understand it, and neither was the answerer of the question.
My question is: How does the following code work?
dndController: function(arg, params){
return new dijit.tree.dndSource(
arg, // don't mess up with the first parameter
dojo.mixin({}, params, {copyOnly:true}))
//create a copy of the params object, but set copyOnly to true
}
So the part bugging me the most is the "args" and "params" parameters. I don't understand where they come from and what they mean or represent. (If there needs to be more context to the code, I can edit the question later, so just post it in the comments). Also, why couldn't I just use the new dijit.tree.dndSource directly and why did I need to use the function to return it?
Thanks

Take a look at dijit/Tree.js in the dojo source.
in Tree.js, inside the postCreate function (which is used by any widget as a part of the dijit lifecycle):
if(this.dndController){
if(dojo.isString(this.dndController)){
this.dndController = dojo.getObject(this.dndController);
}
var params={};
for(var i=0; i<this.dndParams.length;i++){
if(this[this.dndParams[i]]){
params[this.dndParams[i]] = this[this.dndParams[i]];
}
}
this.dndController = new this.dndController(this, params);
}
You'll see a section which checks what the dndController property is. If it is a string it sets the dndController attribute to the function that creates the class that the string describes (this is what dojo.getObject(string) is doing).
For example if this.dndController was the string "my.special.dnd.controlller", it would return the function that, when called instantiates a new instance of my.special.dnd.controller.
It then copies some parameters into an object, followed by executing the function that was either:
(1) looked up via dojo.getObject
(2) uses the custom function you passed in.
I would assume the maintainer of this widget does it this way as some people only need to specify a specific class to use as the dnd controller, whereas others need to do some something more custom based on what parameters the Tree was passed.

Related

Pass Component Name as Argument and then attach method (not working?)

Maybe I'm not using the right terms/names for my searches but I have not been able to find anything on this topic. I would have thought this would be an easy thing to find. What I'm doing is passing a component name to a function and then trying to use the component's name by attaching a method to it. I have confirmed (via Dev Tools) that the name is correctly being passed but when I use the variable and attach the method the specific request does not work. If I 'hard-code' the exact component name to the method it works perfectly. This makes me think the (various) ways I've been trying to attach the method to the variable name is incorrect (see various attempts below). Can you offer some direction here? Thank you.
Passing to Function ...
const grid_name = "grid_GroupA";
console.log(grid_name); // Shows grid_GroupA
msg_max(newItem, grid_name);
Function (only listing relevant parts)
function msg_max(newItem, grid_target) {
console.log(grid_target); // Shows grid_GroupA
// grid_GroupA.data.add(newItem); // This works ...
// grid_target.data.add(newItem); // This does not work
// (grid_target).data.add(newItem); // This does not work
// [grid_target].data.add(newItem); // This does not work
// grid_target + '.data.add(newItem)'; // This does not work
Thank you ...
Edit ...
In my attempt to provide detail I hope I haven't confused the issue.
In essence, my question is if I can type this exact string
grid_GroupA.data.add(newItem);
and it works for my function, how can I place a variable with the exact string "grid_GroupA" in front of ".data.add(newItem);" and have it seen the same as the line of code that works? Maybe my lack of knowledge here is getting in the way but isn't the line of code that works just a string that is then used to 'find' the object? So, if that assumption is correct, how do I create that same string with the variable? If my assumption is wrong I am a willing learner so I will be all ears. Thank you.
I do not see how grid_target is an object. You are passing grid_name(which is a string) to the function, so grid_target will have no data property, because string doesn't have such a member.
P.S. snake_case is bad option for JavaScript, consider using cameCase instead

PhysicsJs - how to remove a world behavior, "constant-acceration' after it has been added

My behaviors on initialization are added as follows:
world.add([
Physics.behavior('interactive', { el: renderer.el }),
Physics.behavior('constant-acceleration'),
Physics.behavior('body-impulse-response'),
Physics.behavior('sweep-prune'),
edgeBounce
]);
I'd like to, at a later time, remove the "constant-acceleration" behavior. I read a couple of posts that said to use the remove() method but I'm not getting anything to happen using it like follows:
world.remove( Physics.behavior('constant-acceleration') );
Can anyone advise how I could achieve removing a specific behavior from the world after it has been added?
The Physics.behavior docs indicate that a Behavior object is returned when you call Physics.behavior (because it constructs a new one). So you need to keep a reference to the Behavior object you'd get back from the call you've put into your world.add array, then pass that reference to world.remove later. As it is now, you're making a new Behavior (separate from the one you made first) and immediately passing that brand new object to world.remove, which will basically do nothing.

setting default root element in jquery

jQuery currently uses window as its default element so any call like $('div') will look for div tags inside window.
Is there any way to change defaults on jQuery like:
$.defaultRoot = $('.anyOtherRootElement');
$('div').text("Hello");
this will select any div inside the elements containing .anyOtherRootElement class.
Thanks in advance
Upate
just an update refining the question a bit more here:
I would like to perform the actions above based on external queries coming from external script which won't know what defaultRoot is so they can still be calling what is supposed to be the current base, so in this instance, I'm afraid adding the a second parameter wouldn't be an option, unfortunately.
And at the same time creating a function which returns defaultRoot.find(el) would prevent me of using first-level methods such $.trim, $.each, etc… so unfortunately that would not be possible as well.
Ideally (for performance reasons) you'd want to use find()
$.defaultRoot.find("div");
Otherwise you can use the 2 argument form that sets a context
$("div", $.defaultRoot);
In general you don't want to do these types of things implicitly since someone else could easily end up thoroughly confused when having to work with your code later. If you want to do it consistently and make it shorter you should create your own function to do so like:
var $s = function(selector) {
return $.defaultRoot.find(selector);
}
and then you'd just be able to use
$s("div")
or you could also do a scoped higher order function with something like
var withScope = function(scope$) {
return function(selector) {
return scope$.find(selector);
}
}
var $s = withScope($.defaultRoot);
$s("div")
If for some reason you really want to screw around with the default state for client code (begging for chaos IMO), you should look at the functional practice: currying.
$('SELECTOR', 'CONTEXT')
You can use context. As in your case $('div', '.anyOtherRootElement')
For more details, visit http://api.jquery.com/jQuery/
Given that you can pass the context as a second argument, you can easily overwrite the $() operator in Javascript with a version which internally calls JQuery using jQuery.noConflict(); and always passes your new root as the second argument.
I don't think jQuery provide such method or variable. But you can pass second parameter in jQuery method to set context.
$.defaultRoot = $('.anyOtherRootElement');
$('div', $.defaultRoot ).text("Hello"); // all div inside $('.anyOtherRootElement')
$('div' ).text("Hello"); //all div inside body tag

Prototype injection, google maps api

I need to catch the event of getting back suggestions for google maps autocomplete. I know it is undocumented, but doing some research I found that it could be down via some prototype hacking.
<input type='text' id='myInput'>
<script src="http://maps.google.com/maps/api/js?libraries=places&sensor=false&language=en-EN"></script>
<script>
function catcher(key) { console.log(key); }
function MyProto() {}
MyProto.prototype = new google.maps.MVCObject();
MyProto.prototype.changed = catcher;
var gAuto = new google.maps.places.Autocomplete(
document.getElementById('myInput'), ['geocode']);
// one of two should be commented
//gAuto.__proto__.__proto__.changed = catcher; // every key, including 'predictions'
gAuto.__proto__.__proto__ = MyProto.prototype; // only 'types', '0', and 'place' when selected
</script>
JSFiddle link: http://jsfiddle.net/agentcooper/hRyTF/ (check the console)
Check the last two lines. When setting 'changed' function directly on MVCObject prototype (first one, commented), everything works great and I can catch key 'predictions' in the 'catcher' function. The problem is that catcher needs to be different if, for example, I need to have two instances of autocomplete on my page. So when I'm trying to inject custom object in autocomplete's prototype chain (last line) everything fails. Is there any way to solve this?
EDIT: working version, thanks to Sajid :-)
UPDATE: Completed the code, maybe it will be helpful to anyone
In the second line, you are replacing the entire prototype of the MVC object with an instance of the MVC object, and depending on how the thing is initialized, this will likely not work at all. The first like replaces one function, though in the process it completely breaks that function since you don't call its superclass version, so you are not extending, you are really clobbering. To not clobber, you need to do:
(function() {
var oldChanged = gAuto.__proto__.__proto__.changed;
function catcher(key) {
// call old version, and make sure to maintain this reference correctly
oldChanged.call(this, key);
// do your stuff here
}
gAuto.__proto__.__proto__.changed = catcher;
})();
One simple solution is that each object has an idea of this mentioned above. So changed has a reference to this, which will refer to the object being used as the caller's target (except in specific situations that are a bit out of scope here). But basically:
var x = new MVCObject();
x.changed('hi') // this === x
So if your two versions need to do different things, you can check which this the changed method was called from and react appropriately.

pulling an array of objects

I currently have a validation script that has a selection of <input> elements stored in objects with properties such as "id", "type" "isRequired" and"isValid". I currently have this setup() function that does the following:
function setup(obj) {
obj.getElement().onkeyup = function() {validate(obj)}
}
In order to run this setup() function on all of my input objects I need to execute the following addEvents() function
function setEvents() {
setup(firstName)
setup(lastName)
setup(email)
setup(dateOfBirth)
}
I'm helping create a system that has multiple pages of nothing but forms so I'd prefer if I didn't have to type this for each object. Is there a way I can collect an array of all the objects that are based on a specific object template? This way I could loop through the array and apply a setup to each object in a single function. If not, are there alternatives?
(p.s. I've been asking so many object-oriented(oh, I crack myself up sometimes) questions lately because this is my first time messing with objects)
---Edit---
the object template I'm referring to looks something like this:
function input(id,isRequired,type) {
this.id = id
this.isRequired = isRequired
this.type = type
}
this is then followed by a
firstName = new input('firstName',true,'alpha')
As I said in my comment, you could add the element to an array when you create it:
var inputs = [];
var firstName = new input('firstName',true,'alpha');
inputs.push(firstName);
This is not ver convenient yet. But you could create another object which manages all this:
var InputManager = {
elements: [],
create: function(/* arguments here */) {
var n = new input(/* arguments here */);
this.elements.push(n);
return n;
},
setup: function() {
for(var i = this.elements.length; i--;) {
(function(obj) {
obj.getElement().onkeyup = function() {validate(obj)};
}(this.elements[i]));
}
}
};
with which you can do:
var firstName = InputManager.create('firstName',true,'alpha');
// etc.
InputManager.setup();
Something along these lines. I think this would be a quite object oriented way. If you have a collection of objects, you often have another object which handles the functions that should be performed on all those objects.
As with most javascript questions, the easiest way to do this is with a library such as jQuery. If you have a unique way to differentiate these objects with a css selector (e.g., they all have the class "validate" or they're the only input[type="text"] fields on the page or something), then you can do a simple selection like $('.validate') to get an array of all these objects. You can get this array using javascript of course but it's a tad more complicated. Once you have the array you can loop over the elements or you can do a simple bind like $('.validate').change(validate); which will call the validate() method whenever a dom element with the class 'validate' changes.
Edit: So obviously I don't know the entirety of what you're trying to accomplish, but if you're new to web programming, just note also that no matter what you're doing on the client side (ie in the browser), all validation should also be done on the server side. Javascript validation is generally used to just be user-friendly and not to actually validate your inputs, since I could easily just turn javascript off or redefine validate as function validate() {} and bypass javascript validation for whatever reason.
2nd Edit: So I'm not sure if this answer was 100% what you're looking for but it's good to know regardless.
Judging by your examples you are not using jQuery. And for that reason alone, I'm going to up vote you. On the same note, after you get really comfortable with JS and how you can do things, really consider using a framework or saving your scripts so you don't have to reinvent the wheel for each project.
You can actually use the DOM to your advantage!
All the forms in your page can be referenced with document.forms[index]. Alternatively you can also reference a named form with document.formName.
Look at this jsfiddle for an example using the latter.
UPDATE
Reading your update and the fact that you needed a way of creating the input objects and setup the validation. I updated my fiddle with a different approach.
Used the id to hold the validation info regarding the element then the addValidation function reverts the id to it's basic form so you can still use it normally throughout your application.
The only requirement is that you addValidation the first thing after page load. So the ID get revamped first.
The solution is also JS safe, meaning if the user doesn't have JS, apart from no validation, no other things will happen.
I think your problem is that the obj in the onkeyup scope is undefined.
function setup(obj) {
//obj is desired
obj.getElement().onkeyup = function() {validate(obj) //obj is undefined because onkeyup is the new scope of this function
}
instead you could do this:
function setup(obj) {
obj.getElement().onkeyup = function() {validate(this)
}

Categories

Resources