Html.CheckBox onclick event is getting escaped - javascript

I'm trying to generate a series of check boxes that will update a span to keep track of the number of check boxes that have been checked (shows something like "4 of 12 checked"). I've created the JavaScript function updateSelected that handles that, but it requires the ID of the span and the class of the check boxes to count, so I tried this code to generate each check box:
#Html.CheckBox(string.Format("contentprofilestate[{0}].selected", i),
new { onclick = "updateSelected('" + selectedSpanId + "', '" + checkboxClass + "')" })
This produces the following HTML:
<input id="contentprofilestate_6__selected" name="contentprofilestate[6].selected"
onclick="updateSelected('StMarySpan', 'StMaryCheck')"
type="checkbox" value="true" />
How can I get the onclick event handler to render without escaping the apostrophes? Alternatively, is there a better way to accomplish this task (if so, feel free to use jQuery in your suggestion)?
Note re: Lester
I created a new project and created this view and it still escaped the apostrophes:
#{
ViewBag.Title = "index";
}
<h2>index</h2>
#Html.CheckBox(string.Format("contentprofilestate[{0}].selected", 0), new { onclick = "updateSelected('" + "test" + "', '" + "test2" + "')" })
Alternate Solution
I was not able to figure out how to keep the apostrophes from being escaped, so I wrote some jQuery logic to do what I need without any inline JavaScript:
jQuery(document).ready(function () {
jQuery(':checkbox').click(function () {
var clientHeader = jQuery(this).closest('.client-header');
var clientCheckedSpan = clientHeader.find('.client-checked');
var inputChecked = clientHeader.find('input:checked');
clientCheckedSpan.text(inputChecked.length);
});
});

It's very odd that the apostrophes are being encoded. I didn't think they would be and after doing a quick test they aren't. There's probably something else going on in that view. If you place just that 1 line in an empty view I'm betting you won't get the same problem.
As for any other alternatives, you can easily get the number of selected checkboxes using jQuery:
var numChecked = $("input:checked[id^=contentprofilestate]").size();

Related

How do I encode/decode a fetch URL/JSON using javascript and flask

This is in reference to dynamically change options in a list by another list. Unfortunately, it all works except if the project name has a " or a #. Eg. Project name: '10" Centerline #3 Pipe'
I am a newbie to this and I have been cut/paste (learning) as I go along. If someone can help, it will be great. I am seen some things about escaping and encoding URI stuff but not sure where to put it in. The other problem is just understanding what happens past the onchange event.
I can get the list of project names from a previous list and it shows up in my HTML page. However, when I select the choices that have a ( " ) or a ( # ), the next populated list breaks.
Thanks in advance. I really, really appreciate the time if someone puts in a response.
Here is the javascript portion:
project_name_select.onchange = function(){
project_name = project_name_select.value;
fetch('/sr/new/project/' + project_name).then(function(response){
response.json().then(function(data) {
var areaHTML = '<option value="See List">Select Area</option>';
for (var state of data.project_area) {
areaHTML += '<option value="' + state.project_area + '">' + state.project_area + '</option>'
}
project_area_select.innerHTML = areaHTML;
});
});
}
Here is the flask portion:
#surveys.route("/sr/new/project/<get_project_name>")
def project(get_project_name):
project_dict, dict_type = choice_project_dict(get_project_name)
project_areaArray = []
for proj in project_dict:
ownerObj = {}
ownerObj['id'] = proj['company_name']
ownerObj['owner_company'] = proj['company_name']
ownerArray.append(ownerObj)
return jsonify({'project_area': project_areaArray})

How to pass variable into a onclick function

I am wanting to try and pass record.ItemID to my onclick = buy() function. But I am getting errors like "Uncaught SyntaxError: Unexpected end of input"
I have tried \"record.ItemID \" but that of course just passes the literal string of result.name
I have also tried (\'' + record.ItemID + '\') but get the same Syntax error
function showShop(items) {
let tableContent = "<tr class='orderTitle'><td =imgTable></td><td id = contentTable ></td></tr>\n";
let odd = true;
const addRecord = (record) => {
tableContent += odd ? "<tr class='orderOdd'>" : "<tr class='orderEven'>";
odd = !odd;
tableContent += "<td>" + "<img id = image src="+ "http://redsox.uoa.auckland.ac.nz/ms/MuseumService.svc/shopimg?id=" + record.ItemId + " />" + "</td><td id = content>" + record.Description + "<td><button onclick='buy("+ record.ItemId +")'/> Buy </button></td>";
}
items.forEach(addRecord)
document.getElementById("shop").innerHTML = tableContent;
}
function buy(item){
window.open('http://redsox.uoa.auckland.ac.nz/mss/Service.svc/buy?id='+ item,'_self');
}
I'm not sure if this will solve your problem but it looks like you're mixing up 's and "s.
onclick='buy('record.ItemId')'
You are terminating the onclick attribute right after buy(.
You may need to do something like:
onclick='buy(" + record.ItemId + ")'
Generally speaking though, if you have to build up HTML in strings, you are better off string interpolation. It makes it easier to read and is less prone to these types of issues.
Example:
const html = `<button onclick="buy(${record.ItemId})">Click</button>`;
It looks like you're trying to build up some HTML content to put into a table, and you want some behaviour attached to a button inside the table so that it opens a new window when you click on it.
There are a number of different approaches to what you're trying to do which would be safer in production code, so while you've got some answers to your specific question, please consider these alternative approaches which are more idiomatic:
You could use a link (<a>) instead of a button, and use CSS to make the link look like a button. This avoids needing a click handler at all.
You could use data attributes to store the record in a safe way, then access it from the click event, e.g. e.target.dataset.recordId.
You could use jQuery or a similar toolkit to create the button, then attach a function to the button as a click handler.
When you create HTML directly like you are doing in your question, you're opening your code up to code injection, where someone malicious could craft data that could steal private information from users of your site. It's much safer to use a library to construct your HTML directly rather than building it up in strings.
Really you're much better off separating out your inline JS and using event listeners to target classes on your elements.
Here's a quick example to show you how you might achieve that:
const records = [{ itemId: 1 }, { itemId: 2 }, { itemId: 3 }];
const imgRoot = 'https://dummyimage.com/30x30/676767/fff.png?id=';
// `map` iterates over the array and produces one element of HTML per record
// We use a class on the button to identify it, and a data attribute
// button to hold the itemId
const html = records.map(({ itemId }) => {
return `
<div class="itemWrapper">
<img class="item" src="${imgRoot}${itemId}" />
<button data-itemid="${itemId}" class="buyRecord">Buy record</button>
</div>
`;
});
document.querySelector('.root').innerHTML = html.join('');
// We grab the buttons and iterate over them attaching
// event listeners that call `handleBuy` when the button is clicked
const buyButtons = document.querySelectorAll('.buyRecord');
buyButtons.forEach(button => button.addEventListener('click', handleBuy, false));
function handleBuy(e) {
// Destructure the itemid from the dataset of the button
// click event
const { target: { dataset: { itemid } } } = e;
console.log(itemid);
}
<div class="root" />
Documentation
map
Data attributes
Template literals
Destructuring assignment
The General format of onclick is
onclick="function_name(variable)"
For this case you can do something like this:
tableContent += '<td>' + '<img id = image src="http://redsox.uoa.auckland.ac.nz/ms/MuseumService.svc/shopimg?id=' + record.ItemId + '" /></td><td id="content">' + record.Description + '<td><button onclick="buy('+record.ItemId+')"> Buy </button></td>';

Random quotation mark being inserted into generated onclick html parameter

I'm trying to add parameters to an onclick function when generating HTML via javascript. When I inspect the code it is putting a quotation mark in the onclick function's parameter.
var lengthOfCats = ArrayOfCategories.length;
for (var a = 0; a < lengthOfCats; a++) {
$("#CatTable").append("<div class='better-table-cell'>" + ArrayOfCategories[a].Name + "</div>\
<div class='better-table-cell'>" + ArrayOfCategories[a].DepartmentName + "</div>\
<div class='better-table-cell'>" + ArrayOfCategories[a].Active + "</div>\
<div class='better-table-cell'>\
<button onclick=OpenUpdateCat(" + ArrayOfCategories[a].CategoryID + "," + ArrayOfCategories[a].Name + ");" + ">Edit</button>\
</div>");
Here is an image of the HTML that is getting generated for the edit button.
The browser is normalising the HTML and putting quote marks around the attribute value.
The problem is that because the attribute value includes spaces, and you didn't put the quote marks in the right spots yourself, the attribute value finishes in the middle of the JavaScript.
The next bit of JS is then treated as a new attribute.
Mashing together strings to generate HTML is fundamentally a bad idea. Ensuring all the right things are quoted and escaped is hard.
Use DOM to generate your HTML instead.
It is a little longer, but clearer and easier to maintain in the long run.
var $cat_table = $("#CatTable");
var lengthOfCats = ArrayOfCategories.length;
for (var a = 0; a < lengthOfCats; a++) {
$cat_table.append(
$("<div />").addClass("better-table-cell").text(ArrayOfCategories[a].Name)
);
$cat_table.append(
$("<div />").addClass("better-table-cell").text(ArrayOfCategories[a].DepartmentName)
);
$cat_table.append(
$("<div />").addClass("better-table-cell").text(ArrayOfCategories[a].Active)
);
$cat_table.append(
$("<div />").addClass("better-table-cell").append(
$("<button />").text("Edit").on("click", generate_edit_handler(ArrayOfCategories[a].CategoryID, ArrayOfCategories[a].Name))
)
);
}
function generate_edit_handler(cat_id, name) {
return function () {
OpenUpdateCat(cat_id, name);
};
}

Generating a string of jQuery for Ajax uploading

My page generates a jQuery string (the jqueryBlock below) that gets ajaxed up to a php file that writes it into a new html file, where it will execute. The code "A" below is what I have now to generate the final jQuery, "B" below in the new html file.
"A"
var targetName = "redbox";
target = $('div[filename=' + targetName + ']').hide()[0];
var jqueryBlock= '<script>$(function() {\n';
jqueryBlock += "$('#" + this.id + "').click(function() {\n";
jqueryBlock += "$('#" + target.id + "').show();\n";
jqueryBlock += "}).css('cursor', 'pointer');\n";
jqueryBlock += "$('#" + target.id + "').dblclick(function(){\n$(this).hide();\n});\n";
jqueryBlock += "})<\/script>";
"B"
<script>$(function() {
$('#T_1376594221987').click(function() {
$('#T_1376594237267').show();
})
.css('cursor', 'pointer');
$('#T_1376594237267').dblclick(function(){
$(this).hide();
});
})</script>
This all works, but it's a nightmare to write block A, trying to keep track of the multiple levels of quotes and all the parens and braces and not being able to break lines to make them more readable. I'm thinking that there must be a way to do this where I can write something that looks more like the finished jQuery for "A". Can anyone suggest a better method?
Thanks.
Ok, well... I came up with some ideas... you may like some, all, or none of it. But I figured I'd paste it here. Just a couple of techniques. You can view the JsFiddle as well.
The first, make a function for creating the jQuery selector. This way you can just pass the id, and get your selector, no quotes to worry about.
function makeJqIdSelector(id) {
return "$('#" + id + "')";
}
The same way of thinking, you could write functions to wrap something in <script> tags (or even a function.
function wrapScriptTags(scr) {
return "<script>\n" + scr + "\n<\/script>";
}
Finally, you can use an array to join the elements so you don't have to keep typing out \ns. ie:
var arr = [];
arr.push("a",
"b",
"c"
);
var str = arr.join("\n");
//output:
//a
//b
//c
This has the added effect of being more efficient as well. (probably not an issue for modern browsers, and especially not for this few strings)
Here it is all together:
var thisSelect = makeJqIdSelector(this.id);
var targetSelect = makeJqIdSelector(target.attr('id'));
var jblock = [];
jblock.push(
"$(function() {",
thisSelect + ".click(function() {",
targetSelect + ".show();",
"}).css('cursor', 'pointer');",
targetSelect + ".dblclick(function(){\n$(this).hide();",
"});",
"});"
);
var jqueryBlock = wrapScriptTags(jblock.join("\n"));
output
<script>
$(function() {
$('#T_1376594221987').click(function() {
$('#T_1376594237267').show();
}).css('cursor', 'pointer');
$('#T_1376594237267').dblclick(function(){
$(this).hide();
});
});
</script>
Note: Obviously, I did not spend a lot of time making sure the output was perfect. It was just for the technique - I have no real way of testing it.
If I understanded everything, you could ajax up only the dynamic variables to the PHP file, and change it to something like this:
<script>$(function() {
$('#<?php echo $_GET['id']; ?>').click(function() {
$('#<?php echo $_GET['id']; ?>').show();
})
.css('cursor', 'pointer');
$('#<?php echo $_GET['id']; ?>').dblclick(function(){
$(this).hide();
});
})</script>
In coffeescript you can use a text block that keeps track of the indention level for you. Maybe you can change to coffeescript just for this script.
http://coffeescript.org/#strings

Building html tags on Javascript in ruby on rails

I'm creating my html tags on my Javascript because of some logic, but the expected result is not being displayed. I'm trying to get all checked checkboxes and display their names. I can pull the names and the checked checkboxes. But it is no rendering correctly. Here is my code:
var amenities = <%= #amenities_json.html_safe %>;
var appendAmenitiesAndFeatures= "<p class='p-tab-title3'>Amenities</p><table class='table-amenities-and-features'><tbody><tr>";
for(var i=0; i<amenitiesChecked.length;i++)
{
$.each(amenities, function(index, amenity){
if(amenity.id == amenitiesChecked[i])
{
appendAmenitiesAndFeatures += "<td><p class='p-tab-subtitle2><span> </span>" + amenity.name + "</p></td>";
}
});
}
appendAmenitiesAndFeatures += "</tr></tbody></table>";
$("#dv-amenities-and-features").html(appendAmenitiesAndFeatures);
When I check 3 checkboxes and alert their names, I can see the string of html tags being build properly. I can see 3 sets of tds in my alert. But when the page is being rendered. It only displays one. I really don't know why. I'm appending to a div and build my table using .html. Any ideas?
hum.... You miss a '
replace your line with this:
appendAmenitiesAndFeatures += "<td><p class='p-tab-subtitle2'><span> </span>" + amenity.name + "</p></td>";

Categories