Quick Way to "Un-Angularize" a JS Object - javascript

Angular adds expando properties, "hashes," etc., to my deep object structure when two-way binding is in use. That's fine, but I'm interested in retrieving a JSON snapshot of my object tree with only the things that originally belonged there. Does Angular provide a way to get a "vanilla" version of a bound object?
(I wouldn't want to merely store the "original value" before binding kicks in, since I would want to reflect any changes made via the UI.)

short answer:
There's a nice built in function:
angular.toJson(yourObj);
longer explanation:
The only difference between angular.toJson and JSON.stringify is that it runs it through a filter which strips out the hashes/ids and turns window, document, and scope into strings. If you want to roll your own function to do this: here is relevant snippet from Angular.JS source code:
if(/^\$+/.test(key)) {
val = undefined;
} else if (isWindow(value)) {
val = '$WINDOW';
} else if (value && document === value) {
val = '$DOCUMENT';
} else if (isScope(value)) {
val = '$SCOPE';
}
note: the isWindow and isScope functions are not exported, so you'd need a little more hacking to get that function to work exactly the same way.
source: http://docs.angularjs.org/api/angular.toJson and https://github.com/angular/angular.js/blob/master/src/Angular.js
There's also a angular.fromJSon function which is essentially JSON.parse.
Update It's worth noting that the $http service does this automatically for you when you specify a model as the data for an $http request.

Related

Extract SOAP/XML namespaces as a list of key/value pairs using JavaScript

Here's a SOAP response that I want to use javascript to extract a list of key value pairs where the key is the local namespace element prefix, such as:
SOAP-ENVns98ns70
and the value is the namespace definition, such as:
http://schemas.xmlsoap.org/soap/envelope/urn:vertexinc:enterprise:platform:security:messages:1:0urn:vertexinc:enterprise:platform:security:1:0
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<ns98:ExecuteLoginResponse
xmlns:ns70="urn:vertexinc:enterprise:platform:security:1:0"
xmlns:ns98="urn:vertexinc:enterprise:platform:security:messages:1:0">
<ns70:UserLoginConfirmation>
<ns70:UserName>platformadmin</ns70:UserName>
<ns70:LoginResult>SUCCESS</ns70:LoginResult>
<ns70:LastLoginDate>2016-01-26T17:28:02.109</ns70:LastLoginDate>
<ns70:DefaultAsOfDate>2016-01-26</ns70:DefaultAsOfDate>
<ns70:ForcePasswordChange>false</ns70:ForcePasswordChange>
</ns70:UserLoginConfirmation>
</ns98:ExecuteLoginResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
My research thus far indicates a general lack of need to do something like this, most people either strip the name space from the elements of the part of the document that they care about, or they simply ignore the namespace. At the moment I am stripping the namespace values, but for debugging purposes would like to inspect the namespaces that were used in the response.
An interesting aspect of the service being used is that the nsXX values can vary for each invocation (e.g. ns70 could be ns78 next time the method is invoked). This means that I cannot key on a fixed namespace value. Additionally, the namespace values for these messages in the WSDL don't seem to have any correspondence to the namespace values in the response.
XPath seems like it might be able to help here, or I could just write my own, but I'd prefer to leverage an existing approach. At this point I'm just trying to understand the problem space and would appreciate some guidance with regard to where to start my education process. (please point me in the right direction)
The approach I took was to fork angular-soap and add the ability to build a list of response namespaces as the XML response is converted into an object tree:
SOAPClient._node2object = function (node, wsdlTypes, namespaces) {
var namespaceURI = node.namespaceURI;
var prefix = node.prefix;
if ('undefined' != typeof namespaceURI) {
namespaces[prefix] = namespaceURI;
}
...
}
To get the list of namespaces a namespaces object must be passed to the $soap.post() method, as follows.
var namespaces = new Object();
...
$soap.post(webserviceurl, "SomeWebservice", parameters, namespaces).then(function (response) {
...
}
The alteration to the angular-soap code required modification of all functions between the $soap.post(url, action, parameters, namespaces) method and SOAPClient._node2object(node, wsdlTypes, namespaces) function to propagate the namespaces parameter.
http://www.w3schools.com/xml/dom_element.asp

Javascript prototype reflection function

I'm not exactly sure of the name of what I'd like to do but it goes like this:
Currently, I have a bunch of variables in my javascript context that look like $A126 or $B15.
Before running, I have to load in all 9000 of these variables and their current values so later parts of the code can just reference $xxx for the value.
The preloading is not efficient at all and is causing a bottleneck.
As there is a substantial amount of code that uses this $xxx notation I was wondering if it would be possible to make a universal change to $.xxx where $ is a function that performed a lookup of the value passed to it via what was after the period.
So $.xxx would be analogous to GetItem(xxx)
This is for a javascript environment in C# using clearscript, though I don't think that would impact the answer.
It looks like
function Field(val){
var value = val;
this.__defineGetter__("xxx", function(){
return value;
});
this.__defineSetter__("value2", function(val){
value = val;
});
}
var field = new Field("test");
console.log(field.xxx)
---> 'test'
That is almost an example of what I'm looking for. The problem is that I would like to have a general defineGetter that doesn't look for a particular getter by name.

Change meaning of ["foo"] in Javascript?

I'd like to make foo["bar"] return the bar field value normally, but if it doesn't exist, look it up in another object to find it there. Is it possible to replace how [] works?
Why? I am porting a 6 year old Javascript application to AngularJS. In the existing app, there's (of course) one global variable (let's call it i2) that's used as a namespace that has everything in the app attached to it.
i2.models.fooModel.bar += 1; //and the rest of the app is similar
If that's not enough responsibility, i2 is also used as a registry of application "cells" like so:
var myCell = i2["MyCell"];
I'm breaking the global's fields into AngularJS services. The cell lookup feature is also broken out into another servicer "cell registry" service. Since this application has existing plugins that we'd like to be backwards compatible with, I'd like the code in the existing plugins like:
var myCell = i2["MyCell"];
... to still work (though deprecated). Is there a way I can replace the [] function in i2 to look up the field in i2, and if it doesn't exist, look it up in another object (the cell registry)?
No, you cannot directly and literally change the meaning of
i2["MyCell"]
because this is the design of the language and that typically can't be changed (and it would be terrible if you could). However, you can do something like this:
function lookupVar(key) {
var value = i2[key];
if(typeof value !== "undefined") {
return value;
}
// do whatever you want to do now that it couldn't be found
}
var myCell = lookupVar("MyCell");
Of course this can be extended to handle other things than just a single variable i2. It might also not be needed at all and a simple
var myCell = typeof i2["MyCell"] !== "undefined" ? i2["MyCell"] : somethingElse["MyCell"];
might be enough (though it contains annoying duplication). If you know that if they key exists in i2["MyCell"], it won't be a falsy value (0, "", false, …), then this will suffice
var myCell = i2["MyCell"] || somethingElse["myCell"];
Though it's not very future-proof.
Yes, you can use getters (don't work in IE8) or Proxy (not recommended for production code)...
Is there a way I can replace the [] function in i2 to look up the field in i2, and if it doesn't exist, look it up in another object (the cell registry)?
Or just think about prototypes.

Using a function to update a value within the data() method of jQuery sets the variable equivalent to the anonymous function, not the return value

I answered this question: Manipulate Custom Values with jQuery
With this jQuery:
$('img').attr('u', function(i,u) {
/* i is the index of the current image among all the images returned by the selector,
u is the current value of that attribute */
return u.slice(0, -1) + (parseInt(u.replace(/\D/g,''), 10) + 1);
});
Link to answer, JS Fiddle demo from that answer.
But then I felt I should show how to do it 'properly,' using the custom data-* attributes allowed under HTML5 (rather than the invalid, albeit functional, custom attributes), so I adapted the HTML to:
<img src="http://placekitten.com/400/500" class="className" click="" id='button4' data-u="button6" data-r="button5" data-l="ele1" data-d="" />
(No, I've got no idea what the click attribute's meant to do, or why it's there, so I left it alone.)
And tested the following jQuery:
$('img').data('u', function(i,u) {
/* i is the index of the current image among all the images returned by the selector,
u is the current value of that attribute */
return u.slice(0, -1) + (parseInt(u.replace(/\D/g,''), 10) + 1);
});
$('img').each(function(){
console.log($(this).data('u'));
});
JS Fiddle demo.
Now, with the data() method I realise that the attribute wouldn't be updated, which is why I used the console.log() to confirm the updated value, the output, however, is the anonymous function itself, not the value that I expected to be returned from that function. I realise this is unlikely to be a bug, and is probably the expected behaviour, but is there a way to use an anonymous function to update the attributes in the same way as, for example, that used within attr(), text(), etc..?
The difference between data and many of the other jquery functions (such as attr and many others) is that data can store any type of object. attr can only store string values. Because of this, it is completely valid to want to store a function using data.
If the jquery team were to make a similar signature for data, they would need to somehow distinguish between wanting to store the function and wanting to evaluate the function. It would likely get too confusing so they just did not include the ability to execute the function.
I think the best you can do is to use each.
Basically your expectation is wrong.
jQuery's .data does not modify the data attributes of the elements at all; it simply associates the data you provide with the element through a mechanism of its own choosing.
The implementation is intentionally left unspecified, and .data does not process this data at all; you put something in, and when you later ask for it that is exactly what you get back. The data is totally opaque from jQuery's perspective.
It's true that .data provides pre-population of an element's associated data from its HTML data- attributes as a convenience feature, but that is not its main mission. And of course the opaqueness of the data is still upheld in this case: when you ask for data, you get back exactly what was specified in the HTML.
If you need this capability, you can easily add it:
$.fn.fdata = function( name, callback ) {
return this.each( function( i, element ) {
var $element = $(element);
var data = callback( i, $element.data(name) );
$element.data( name, data );
});
};
Now you can use $(sel).fdata( name, callback ); and do what you want in the callback.
It may be tempting to extend the existing $().data() method to add the callback capability, but as other pointed out, this would break any other code that depends on being able to store a function reference as data.
Of course, it's also possible that merely adding this .fdata() method could break other code - if some other code on your page also tries to use the same method name in its own plugin. So it may be wiser to make this a simple function instead. The code is almost identical either way:
function updateData( selector, name, callback ) {
$(selector).each( function( i, element ) {
var $element = $(element);
var data = callback( i, $element.data(name) );
$element.data( name, data );
});
}

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