I'm new with Ajax.
I'm trying to parse this document.
I've gotten as far as the readystatechange, and it's fetching the XML. But I get confused when it comes to the childNodes and their values.
Here's a bit of the code. If I try to alert that first value, it comes up blank.
var clientList = request.responseXML.getElementsByTagName('client');
for (var i=0;i<clientList.length;i++) {
var client=clientList[i];
var clientName = client.childNodes[0].nodeValue;
alert(clientName)
As far as I understand it, based on the XML document, each "client" tag would have the following ChildNodes:
[0] : clientName,
[1] : clientStreetAddress,
[2] : clientCity
[n] : ...and so on...
So what am I missing here? Clearly I don't have my facts straight. Please help!
You should read the data from the XML using the names of the tags, not based on what order they happen to be. When the document is parsed it might contain textnodes for the whitespace between the elements, which would offset the indexes of the elements containing the data that you want.
var clientName = client.childNodes.selectSingleNode('./clientName').nodeValue;
Thanks to TeslaNick for suggesting I use XPATH instead. The answer was as follows:
var clientDoc = request.responseXML;
var clientName = clientDoc.evaluate("data/client[1]/clientName", clientDoc, null, XPathResult.STRING_TYPE, null).stringValue
Of course, I think this has to be modified to handle IE browsers, and the path has to be set to loop through the clients. However, the actual, simplest answer is above.
Thanks also to Guffa for helping!
Related
Good day everyone,
I am currently trying to append a metadata file. Sorry in advance if I did anything wrong, I am unfamiliar with editing XML codes in JS.. Thanks!
Currently, I am having difficulty getting the results that I expected. I am trying to insert 2 new nodes one nested over the other into the newParentTestNode.
I want to add a couple of nodes within the TestNode as seen in the results I want.. I can't seem to find a solution online. Please do help thanks!
I am currently getting this result:
<gmd:MTTEST><TESTNODE2/></gmd:MTTEST>
But the result I want is:
<gmd:MTTEST>
<gmd:TestNode>
<gmd:TestNode2>
</gmd:TestNode2>
</gmd:TestNode>
</gmd:MTTEST>
xmlTest: function (evt) {
if(this.item.metadata_standard_name == "Correct Data"){
xmlString = this.item.sys_xml_clob;
var metadataXmlString = jQuery.parseXML(xmlString);
let newParentTestNode = metadataXmlString.getElementsByTagName("gmd:MTTEST")
newNode = metadataXmlString.createElement("TestNode")
newNode2 = metadataXmlString.createElement("TestNode2")
let addMe = newNode.appendChild(newNode2)
newParentTestNode[0].appendChild(addMe)
xmlString = (new XMLSerializer()).serializeToString(metadataXmlString);
console.log(xmlString)
}
appendChild() returns the node that is appended not the parent node.
This means that newNode.appendChild(newNode2) returns newNode2, which you'll then append to your root node, effectively removing TestNode2 from TestNode and appending it directly to MTTEST.
You don't need to assign the result of appendChild to a new addMe variable because appendChild modifies the structure in-place, so you gain nothing from the return value (as you already have variables referencing both the parent and the child element). So in the end you just need to append newNode (which will already contain newNode2) to newParentTestNode.
I have a simple script to generate a doc and PDF upon form submission. It worked well on simple template (e.g. Only 1 sentence, First name, Last name and Company name).
However, when I use a template that's longer, having many fields, and formatting, the code runs but replace the text randomly.
I have tried to hardcode the fields of forms in ascending order as the doc template. However it still replace the text randomly
Can anybody points out what have I done wrong?
My code:
function myFunction(e) {
var response = e.response;
var timestamp = response.getTimestamp();
var [companyName, country, totalEmployees,totalPctWomenEmployees,numberNationality,name1,position1,emailAdd1,linkedin1,funFact1,name2,position2,emailAdd2,linkedin2,gameStage,gameStory] = response.getItemResponses().map(function(f) {return f.getResponse()});
var file = DriveApp.getFileById('XXXXX');
var folder = DriveApp.getFolderById('XXXXX')
var copy = file.makeCopy(companyName + '_one pager', folder);
var doc = DocumentApp.openById(copy.getId());
var body = doc.getBody();
body.replaceText('{{Company Name}}', companyName);
body.replaceText('{{Name}}', name1);
body.replaceText('{{Position}}', position1);
body.replaceText('{{Email}}', emailAdd1);
body.replaceText('{{Linkedin}}', linkedin1);
body.replaceText('{{Fun Fact}}', funFact1);
body.replaceText('{{Game Stage}}', gameStage);
body.replaceText('{{Game Story}}', gameStory);
doc.saveAndClose();
folder.createFile(doc.getAs("application/pdf"));}
My template -
Result -
Question - Does that mean the array declaration in line 3 was supposed to match the order of my form responses columns?
You can use Regular Expresion:
body.replace(/{{Company Name}}/g, companyName); // /g replace globaly all value like {{Company Name}}
Finally I found what have went wrong after so many trials and errors!
The reason is because I declared the array variables randomly without following the order of the form responses columns.
The issue is with the part -
var [companyName, country, totalEmployees,totalPctWomenEmployees,numberNationality,name1,position1,emailAdd1,linkedin1,funFact1,name2,position2,emailAdd2,linkedin2,gameStage,gameStory] = response.getItemResponses().map(function(f) {return f.getResponse()});
It's actually pulling responses from the spreadsheet, and should be corrected in order. The wrongly mapped values was what causing the replacement of text went haywire. I corrected the order as per form responses and it is all good now.
Learning points:
If you swapped around the variables, what response.getItemResponses().map(function(f) {return f.getResponse()} does is that it will go through the form responses column by column in order, and it will map the content to the wrong variable. As a result, when you replace your text later using body.replaceText('{{Game Stage}}', gameStage), there might be possibility that whatever stored in gameStage might be name1. Hence the replaced text will be wrong. And you will scratch your head until it bleeds without knowing why.
I saw #Tanaike's comment after I found the answer, but totally spot on!
I have a JSON script which contain live matches. These changes every 5 minutes. The changes could for instance be the keys live_in or score. Beside this matches are also deleted and added to the JSON. I want to keep my html output updated at all time how can i do this the best possible way? So far i've set the updating speed to 5 seconds for testing purposes. I've tried so far to set the divs id to equal to the match_id and thereby update by
$('div#match-date-id-' + match['match_id']).html('test');
However does not seem to update. How can i do this the best possible way? i've created a plnkr which enable you to download it with a json snippet, which can be edited in order to check.
plnkr.co/edit/eQCShhW01OG5jU4VLx04?p=preview
My bad earlier on :-) Now I've done more thorough testing and updated the plunker code. What I found in the test: the filter was trying to use an undefined value (match.id), also the filter was trying to compare values from different datatypes (String vs. Integer); so it wasn't working as intended:
Original:
match = match.filter(
function(match) {
return match.id > lastId;
});
Corrected code:
match = match.filter(
function(match) {
return parseInt(match.match_id) > lastId;
});
Above match_id represents String datatype in your JSON file, so I had to convert it to Integer before comparison. More info about JSON data-types. Following the older filter functionality, no match passed the comparison test.
Also for testing purposes I commented out the following line:
if (match.match_id > lastLoadedMatch) {
//lastLoadedMatch = match.match_id
}
because in the second round of updates with the same testing data, no data passes that condition.
Also, I think you need to convert match_id to Integer as well; like following:
var matchId = parseInt(match.match_id)
if ( matchId > lastLoadedMatch) {
lastLoadedMatch = match.match_id
}
I hope you find it useful :-) + note I added a timestamp for those updates; so it's clearly visible that the updates take place now every 5 seconds.
I've imported some XML files inside InDesign (you can see the structure in the picture below) and I've also created a script to get some statistics concerning this hierarchy.
For example, to count the "free" elements:
var items = app.activeDocument.xmlElements.everyItem();
var items1 = items.xmlElements.itemByName("cars");
var cars = items1.xmlElements.everyItem();
var c_free = cars.xmlElements.itemByName("free");
var cars_free = c_free.xmlElements.count().length;
I also have apartments in my structure that's why I'm using itemByName.
The code above returns the correct number of free cars in my structure.
What I'm trying to do - without any luck so far - is to target those free items (inside cars) and either delete all of them or a specific number.
My last attempt was using:
var del1 = myInputGroup2.add ("button", undefined, "Delete All");
del1.onClick = function () {
cars.xmlElements.everyItem().remove();
}
inside a dialog I've created.
Any suggestions will be appreciated cause I'm really stuck here.
I would probably use XPath for this. You can use evaluateXPathExpression to create an array of the elements you want to target. Assuming your root element is cars and it contains elements called cars1, and you want to delete all free elements within a cars1 element, you could do something like:
var myDoc = app.activeDocument;
//xmlElements[0] is your root element, in this case "cars". The xPath expression is evaluated from cars.
//evaluateXPathExpression returns an array of all of the free elements that are children of cars.
var myFrees = myDoc.xmlElements[0].evaluateXPathExpression("cars1/free");
for (var i = myFrees.length - 1; i>=0; i--){
myFrees[i].remove();
}
Tweaking this would require some knowledge of xPath, but it's not terribly hard to learn the basics and it does seem like the simplest approach.
I think your main problem was that XMLElements hasn't a itemByName method. You can only reference XMLElements through their indeces or ids.
Secondly you assume that you got xmlElements from XPath expression but it's likely that you got nothing as your xpath seems uncorrect.
var myFrees = myDoc.xmlElements[0].evaluateXPathExpression("./cars1/free");
var n = myFrees.length;
if ( !n ) {
alert("Aucun élément trouvé");
}
else {
while (n--) myFrees[n].remove();
}
You need to start your expression by setting the origin of your xpath. Here a dot "./" is used to tell you want to look for cars1/free xml elements at the "root" of the xmlelement. Using "//" on the contrary would have returned any cars/free items unregardingly of their locations.
$('img').click(function(){
var add_to_list = $(this);
// database query for element
});
Currently add_to_list is getting the value 'images/image.jpg'. I want to replace the 'image/' to nothing so I only get the name of the picture (with or without the extension). How can I do this? I couldn't find anything. Also please provide me with further reading on string manipulation please.
Have you tried javascript replace function ?
You should modify your code to something like this:
$('img').click(function(){
var add_to_list = $(this).attr('src').replace('images/', '');
// database query for element
});
use
add_to_list.substring(7);
This will get you the string after the 7th character. If there might be longer paths, you can split() into an array and get the last part of the path using pop().
add_to_list.split("/").pop();
substring
split
pop
This tutorial explains many of the string manipulation methods seen in the answers here.
$('img').click(function(){
var add_to_list = $(this).attr('src').replace('image/', '');
// database query for element
});
var pieces = add_to_list.split('/');
var filename = pieces[pieces.length-1];