How to handle failed JQuery DOM conversion - javascript

I have a common function to display errors as a result of an AJAX call. Some of those messages are HTML strings, which I want to convert to a DOM object then search for elements inside it using .find(). Others will be just strings (not HTML). In this case, I don't know how to handle this...it is generating exceptions.
var messageTest = "" + this;
if ($(messageTest).length == 0) {
message += this;
} else {
message += $(messageTest).find('.message-error').html();
}
FYI, "this" in this case seems to be a String object with an array in which each item is a character, so in the inspector, it isn't showing "my message here" but:
[0]: "m"
[1]: "y"
etc
When it is just a string, which is not HTML, I get an error at the if statement line:
Uncaught Error: Syntax error, unrecognized expression:<The contents of messageText>
So how to I gracefully handle this when the input could be an HTML string or just a string?
Note...in the end, I just want the string, as I am going to wrap it in it's own HTML.

If it's either a string or HTML, it can always be appended to an element, and then sorted out:
var msg = $('<div />').append(this),
err = msg.find('.message-error'),
txt = err.length ? err.text() : msg.text();
message += txt;
append the string or HTML to an empty div, if the div contains a .message-error element, get the text of that element, otherwise get the text of the div, which would equal the original string etc.

One way, very close to what you have, is to catch the exception, and in this case consider it's a string (assuming this contains the response string):
var messageString = this;
var messageDOM;
try {
messageDOM = $(messageString);
} catch(ex) {
// If we got here, messageString is not HTML
messageDOM = $('<div/>').text(messageString);
}

I know this doesn't answer your question, but I feel like what you're wanting can be better accomplished by a slight change in methodology.
If you have control of the server sending these responses, i would recommend sending them back as JSON objects, if you make every result a JSON object then you can set an error property to true/false in the object that comes back along with a message value with your HTML or error message.
This is faster on the client system as well since its a native object to javascript and jquery has excellent JSON support using $.getJSON.

Related

C# JSON String returned in ambiguous format

i have an issue with my logged Json String as it replaces the double quotes with "
Controller Code :
var message = new SuccessMessagesVM()
{
Title = successMessageType == (int)EnumHelper.SuccessMessageTypes.Add ? "It's beautiful" : CustomModel.Resources.SuccessMessagesResources.EditFormSuccess,
Message = successMessageType == (int)EnumHelper.SuccessMessageTypes.Add ? CustomModel.Resources.SuccessMessagesResources.AddFormSuccess : CustomModel.Resources.SuccessMessagesResources.EditFormSuccess,
ColorCode = (int)EnumHelper.MessagesCodes.Success
};
var json = JsonConvert.SerializeObject(message);
ViewBag.SuccessMessage = successMessageType == 0 ? null : json;
Javascript just logs the ViewBag.SuccessMessage as following:
console.log('#ViewBag.SuccessMessage');
and the object is displayed as {"Message":"تم إضافة النموذج بنجاح","Title":"It's beautiful","ColorCode":3}
replacing all single quotes with ' and all double quotes with "
I expect the output to be {"Message":"تم إضافة النموذج بنجاح","Title":"It's beautiful","ColorCode":3}
This is because you are using ViewBag variable.
In order to use ViewBag, you can write it as followed:
First, in view:
#{
var jss = new System.Web.Script.Serialization.JavaScriptSerializer();
var successMessageJson = jss.Serialize(ViewBag.SuccessMessage);
}
Then use it:
<script>
//use Json.parse to convert string to Json
var successMessageInfo = JSON.parse('#Html.Raw(successMessageJson)');
</script>
There's a few different issues and a few different approaches, and the OP doesn't give us any view code, so it's a bit of guesswork.
My feeling is that you should just return the SuccessMessagesVM from your method, and access the properties individually in your view (probably using #Html.DisplayFor and HTML). This will ensure that any redundant quotation marks are never created in the first place, and will give you total control over how the values are displayed.
You should not expect console.log(#ViewBag.Anything) to display properly. If you need the data as a JavaScript object, don't stick it in the ViewBag, as it will get rendered as HTML.
If you must write to a JavaScript object you can, but it begs the question why are you not using an ajax request if all you want is data?

Getting Number from Element's Text and Injecting into an xpath string via ProtractorJS

Background
Essentially what I want to do is to grab text from an element on one page. Parse that grabbed text into a number but which remains in string format, so I can further use it for an xpath query string. See the code below
var bookingRefString = element(by.css(".panel-header span")).getText();
var bookingRef = String(bookingRefString).match(/\d+/);
element(by.id("menu_item_control")).click();
element(by.xpath(".//tr/td[1]//*[text()='" + bookingRef + "']/../..")).click();
The error I get back
Failed: No element found using locator: By(xpath, .//tr/td[1]//*[text()='null']/../..)
What I can conclude from this problem
So to conclude it's quite clear that it's not parsing the bookingRefString for the number as stated from 'null' being injected into the xpath.
Try that:
element(by.css(".panel-header span")).getText().then((bookingRefString) => {
var bookingRef = bookingRefString.match(/\d+/);
element(by.id("menu_item_control")).click();
element(by.xpath(".//tr/td[1]//*[text()='" + bookingRef + "']/../..")).click();
});

Why is the .text() function returning [object Object] in ngScenario for AngularJS e2e?

Update: A Solution below!
I'm fairly new to website development but I've been tasked with developing e2e (end-to-end) tests for a developing website that uses AngularJS. As a result I've been looking down the road of using AngularJS's karma-run ngScenario testing wrapper.
Anyway, just getting started I want to make sure that a simple hyperlink's text matches part of its href address. There isn't need to know the structure of this code snippet, but these are thumbnails for user profiles, and you can click the thumbnail object completely (the first 'a') or you can click a link displaying their name (the second 'a').
There dozens of these on a page.
Here is what a part of the page looks like loaded with a user "PurplePenguin".
<div class="thumbnail__section">
<a href="/profile/PurplePenguin">
<div class="thumbnail__bottom">
<div class="thumbnail__rating ng-binding"> </div>
<div class="thumbnail__info">
<div class="thumbnail__name">
<a class="ng-binding" href="/profile/PurplePenguin">PurplePenguin</a>
</div>
[...]
Essentially I want a test that will take the text of the second 'a' element and check it against the href attribute: "assert that href '/profile/PurplePenguin' equals '/profile/' + 'PurplePenguin'"
This is what I've made just wanting to test the first thumbnail's 'a', (in my time writing "PurplePengiun" is the first user every time so I could hard code it).
it('should have the performer\'s name match the link', function() {
// "eq(n)" gets the nth instance of the element;
// "> a:eq(0)" grabs its first child a
var nameElement = element('.thumbnail__name:eq(0) > a:eq(0)');
// These work and pass
expect(nameElement.text()).toBe('PurplePenguin');
expect(nameElement.attr('href')).toBe('/profile/PurplePenguin');
// This does not
var textString = nameElement.text(); // textString == "[object Object]"
expect(nameElement.attr('href')).toBe('/profile/' + textString);
This gets returned:
expect element '.thumbnail__name:eq(0) > a:eq(0)' get attr 'href' toEqual "/profile/[object Object]"
expected "/profile/[object Object]" but was "/profile/PurplePenguin"
So I've figured out how to find the particular element on the page I need, but trying to manipulate the text of a simple 'a' element only gives me [object Object].
Some things I've tried with my basic knowledge of JS:
nameElement.text().toString(), nameElement.text().value(), nameElement[0].text()
Trying things other than text()
nameElement.html() and nameElement.val()
They too return [object Object] when trying to use them as strings.
It seems that looking at the values and attributes of these elements only works when using the very specific API functions like .toBe or .toEqual, but I want to assert that a specifically formatted string is made.
Thank you for any help!
Solution
Thanks for that bit of insight Andyrooger, I had actually taken a stab at the query function before posting my question but gave up on it too quick. Your explanation gave me the idea to start looking deeper into the samples that have been posted in the official docs. I ended up taking a hint from a Adi Roiban's post to another Angular e2e writer's question talking about query, done() messages, and promises. This ended up leading me to my eventual solution.
So I've made myself a solution and in the spirit of cooperation made a set of examples for others to learn by. There are four examples, the first two are just getting the text and the href and comparing them against hard-coded values. The third one uses indexOf to do dirt-simple comparisons. The fourth one shows how you can make your own more specific pass/fail conditions (more than what Jasmine provides with its matchers).
Number 1: User name text vs hard coded value
it('should have the user\'s name be \'PurplePenguin\'', function() {
var textPromise = element('.thumbnail__name:eq(0) > a:eq(0)').query(function (nameElement, done) {
var text = nameElement.text(); // Can finally access this guy!
// The first param null indicates a nominal execution, the second param is a return of sorts
done(null, text);
});
// Passes
expect(textPromise).toBe('PurplePenguin')
});
Number 2: Profile href value vs hard coded value
it('should have the user\'s link be \'/profile/PurplePenguin\'', function() {
var textPromise = element('.thumbnail__name:eq(0) > a:eq(0)').query(function (nameElement, done) {
var href = nameElement.attr('href');
// The first param null indicates a nominal execution, the second param is a return of sorts
done(null, href);
});
// Passes
expect(textPromise).toBe('/profile/PurplePenguin')
});
Number 3: Simple string comparison
it('should have the user\'s name match the link', function() {
var textPromise = element('.thumbnail__name:eq(0) > a:eq(0)').query(function (nameElement, done) {
var text = nameElement.text();
var href = nameElement.attr('href');
// The first param null indicates a clean pass, the second param is a return of sorts
done(null, href.indexOf(text)); // indexOf returns -1 if text is _not_ a sub-string of href
});
expect(textPromise).toBeGreaterThan(-1);
// In the case of failure reports this in the console (if using Karma):
// expect element .thumbnail__name:eq(0) > a:eq(0) custom query toBeGreaterThan -1
// expected -1 but was -1
// In the runner.html page for the test simply reports this useless/misleading log:
// expected -1 but was -1
});
Number 4: More detailed string comparison doing it your own way plus a better error message
it('should have the user\'s name match the link', function() {
var nameStringPromise = element('.thumbnail__name:eq(0) > a:eq(0)').query(function (nameElement, done) {
var text = nameElement.text();
var href = nameElement.attr('href');
var message;
if (href.indexOf(text) == -1)
message = 'Did not find "' + text + '" in "' + href + '"';
done(message, href.indexOf(text));
});
expect(nameStringPromise);
// An expect() with no Jasmine matcher means that it only uses the done message to determine
// a pass or fail for the test. So I wrote that if my conditions weren't met it would print
// an informative message in the case of failure
// Example failure output with a broken url generator:
// Did not find "PurplePenguin" in "/profile/"
});
I'm struggling to find references to back this up or explain a little more nicely, other than these docs which don't have much info. Therefore this answer is mainly from memory of reading the relevant code.
When you write a test in ngScenario, you are not writing a series of immediate actions as you would do in a unit test. What you are actually doing queuing up a list of commands to execute when the test starts.
Methods that looks like normal jQuery functions, such as text() or attr() are actually part of ngScenario's DSL and return future objects (meaning they will at some point execute and have the result of the call you wanted.)
expect() already knows this so waits for the result before comparing to your expected value. The code underneath, in your example, is reading directly and immediately so will see the future object.
If you do need to read the element in this way I suggest looking at the query function which should allow it. On the other hand if you need to just check the formatting of the string you can use toMatch which has been implemented in ngScenario too.

How to display raw JSON data on a HTML page [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
JSON pretty print using JavaScript
I'd like to display my raw JSON data on a HTML page just as JSONview does. For example, my raw json data is:
{
"hey":"guy",
"anumber":243,
"anobject":{
"whoa":"nuts",
"anarray":[
1,
2,
"thr<h1>ee"
],
"more":"stuff"
},
"awesome":true,
"bogus":false,
"meaning":null,
"japanese":"明日がある。",
"link":"http://jsonview.com",
"notLink":"http://jsonview.com is great"
}
It comes from http://jsonview.com/, and what I want to achieve is like http://jsonview.com/example.json if you use Chrome and have installed the JSONView plugin.
I've tried but failed to understand how it works. I'd like to use a JS script (CSS to highlight) to custom format my raw JSON data which is retrieved by ajax and finally put it on a HTML page in any position like into a div element. Are there any existing JS libraries that can achieve this? Or how to do it?
I think all you need to display the data on an HTML page is JSON.stringify.
For example, if your JSON is stored like this:
var jsonVar = {
text: "example",
number: 1
};
Then you need only do this to convert it to a string:
var jsonStr = JSON.stringify(jsonVar);
And then you can insert into your HTML directly, for example:
document.body.innerHTML = jsonStr;
Of course you will probably want to replace body with some other element via getElementById.
As for the CSS part of your question, you could use RegExp to manipulate the stringified object before you put it into the DOM. For example, this code (also on JSFiddle for demonstration purposes) should take care of indenting of curly braces.
var jsonVar = {
text: "example",
number: 1,
obj: {
"more text": "another example"
},
obj2: {
"yet more text": "yet another example"
}
}, // THE RAW OBJECT
jsonStr = JSON.stringify(jsonVar), // THE OBJECT STRINGIFIED
regeStr = '', // A EMPTY STRING TO EVENTUALLY HOLD THE FORMATTED STRINGIFIED OBJECT
f = {
brace: 0
}; // AN OBJECT FOR TRACKING INCREMENTS/DECREMENTS,
// IN PARTICULAR CURLY BRACES (OTHER PROPERTIES COULD BE ADDED)
regeStr = jsonStr.replace(/({|}[,]*|[^{}:]+:[^{}:,]*[,{]*)/g, function (m, p1) {
var rtnFn = function() {
return '<div style="text-indent: ' + (f['brace'] * 20) + 'px;">' + p1 + '</div>';
},
rtnStr = 0;
if (p1.lastIndexOf('{') === (p1.length - 1)) {
rtnStr = rtnFn();
f['brace'] += 1;
} else if (p1.indexOf('}') === 0) {
f['brace'] -= 1;
rtnStr = rtnFn();
} else {
rtnStr = rtnFn();
}
return rtnStr;
});
document.body.innerHTML += regeStr; // appends the result to the body of the HTML document
This code simply looks for sections of the object within the string and separates them into divs (though you could change the HTML part of that). Every time it encounters a curly brace, however, it increments or decrements the indentation depending on whether it's an opening brace or a closing (behaviour similar to the space argument of 'JSON.stringify'). But you could this as a basis for different types of formatting.
Note that the link you provided does is not an HTML page, but rather a JSON document. The formatting is done by the browser.
You have to decide if:
You want to show the raw JSON (not an HTML page), as in your example
Show an HTML page with formatted JSON
If you want 1., just tell your application to render a response body with the JSON, set the MIME type (application/json), etc.
In this case, formatting is dealt by the browser (and/or browser plugins)
If 2., it's a matter of rendering a simple minimal HTML page with the JSON where you can highlight it in several ways:
server-side, depending on your stack. There are solutions for almost every language
client-side with Javascript highlight libraries.
If you give more details about your stack, it's easier to provide examples or resources.
EDIT: For client side JS highlighting you can try higlight.js, for instance.
JSON in any HTML tag except <script> tag would be a mere text. Thus it's like you add a story to your HTML page.
However, about formatting, that's another matter. I guess you should change the title of your question.
Take a look at this question. Also see this page.

How do I store xml as a string variable in javascript?

I trying to store a piece of xml into a string variable in javascript and in IE8 it keeps throwing an error. FireFox doesn't show the error but of course in IE8 it does. Swictching browsers isn't an option so I have to try to solve this one.
The purpose of the function is to check if the items of a list exist in an xml object or not. So if there is a better way to do that check I am open to that as well. The system we pull from has a function to convert the xml to a string. At the bottom is an output of what that retrieves. Here is the function.
function commodityExists(newCommodityCode){
var comExists = new Boolean(0);
newCommodityCode = ">" + newCommodityCode + "<"
var strXML = 'tw.local.aribaHeader.commodities.toXMLString()'; //ERROR HERE
strXML = strXML.toString();
if(strXML.indexOf(newCommodityCode,0)>0){
comExists=true;
}
return comExists;
};
Here is the output from strXML.toString(); but as you can see it is essentially xml.
var strXML = ‘<variable type="NameValuePair[]">
<item type="NameValuePair">
<name type="String"><![CDATA[No Data Found]]></name>
<value type="String"><![CDATA[95990070]]></value>
</item>
</variable>’;
I don't know what you think the code is doing, here is an explanation of what it does:
> function commodityExists(newCommodityCode){
> var comExists = new Boolean(0);
Do you really want a Boolean object? This function might return a Boolean object or primitive depending on what happens later. Consider:
var comExists = false;
.
> newCommodityCode = ">" + newCommodityCode + "<"
That overwrites whatever value was passed to newCommodityCode from the call.
> var strXML = 'tw.local.aribaHeader.commodities.toXMLString()'; //ERROR HERE
I can't see how that throws an error, it's a simple assignment of a string.
> strXML = strXML.toString();
That effectivly does nothing - it calls the toString method of a string, which will just return the same string.
> if(strXML.indexOf(newCommodityCode,0)>0){
That test will always be false, since the value of nweCommodityCode is hard coded in the function and does not exist in the (hard coded) value of strXML.
> comExists = true;
> }
> return comExists; };
The function will always return false (though the original will return a Boolean object with a value of false).
You're creating a string:
var strXML = 'tw.local.aribaHeader.commodities.toXMLString()'; //ERROR HERE
^--- ^---
then converting that string to... a string?
strXML = strXML.toString();
Where would this tw object be defined that you seem to be attempting to use? Because as your code is written now, you're not calling a .toXMLString() method on something in this tw object. You're just assigning the literal text of an object call as a string itself.
The approach I was trying to take will not work because I am dynamically populating the xml so there is no way for me to escape the characters (well there probably is somehow but clearly it is not worth it). Storing HTML or XML code in javascript variables
Instead I am moving the comparison to the server side instead of retrieving the xml and comparing on the client side and posting back the results via ajax unless someone has a better reccomendation.

Categories