Nesting [innerHTML] in AngularJS - javascript

How can I 'nest' innerHTML bindings? Is it a sanitation problem?
I am using angular to create a website to play chess. I am dynamically creating a table and putting it into the component with: <div [innerHTML]="board | safeHTML"> where board with a string with the HTML for the table in and safeHTML is a pipe that bypasses sanitisation so the HTML isn't just read as a string. The code to generate the table is at the bottom of the post.
I would like to then be able to change the contents of each cell in the table.
Here is an example cell:
<td id="n${uniqueNumber}" class="board" [innerHTML]="boardValues.${uniqueNumber}">{{boardValues.${uniqueNumber}}}</td>
boardValues is an object that contains all of the cell data.
The table correctly displays when bound with [innerHTML] however the cells don't and [innerHTML] is displayed as an attribute (e.g. this image). Similarly {{boardValues.value}} shows up as plain text rather than the value.
How can I 'nest' innerHTML bindings? Is it a sanitation problem?
My html:
Running that doesn't work but it shows my main loops (the important one is generateBoard)
Here is a stack blitz project that sort of works but doesn't have formatting for some reason. Still shows my issue
generateBoard(n, playColour) {
var tblBuild = ""
var tblFooter = "<tr><td id=\"blank\"></td>"
for (var i = 0; i < 8; i++) {
tblFooter += `<th class="coordinates letters">${String.fromCharCode(97+i)}</th>`
}
tblFooter += "</tr>"
for (var i = 0; i <= n; i++) {
tblBuild += `<tr class="n${n-i+1}">`
tblBuild += `<th class="coordinates numbers">${n-i+1}</th>`
for (var j = 0; j <= n; j++) {
var idValue = `n${8*(n-i)+j}`
//want to assign the innerHTML to an object
tblBuild += `<td id="n${idValue}" class="board" [innerHTML]="boardValues.${idValue}">{{boardValues.${idValue}}}</td>`
}
tblBuild += ("</tr>")
}
var tbl = "<table id=\"board\" class= \"board " + playColour + "\"" + "<tbody>" + tblBuild + "</tbody>" + tblFooter + "</table>"
console.log(tbl)
return tbl
}
generateBoardValues(n) {
interface LooseObject {
[key: string]: any
}
var boardValues: LooseObject = {}
console.log("running generateBoardvalues")
console.log("n", n)
for (var i = 0; i <= n; i++) {
for (var j = 0; j <= n; j++) {
boardValues[`n${8*(n-i)+j}`] = `n${8*(n-i)+j}`
console.log("generating boardValues", boardValues[`n${8*(n-i)+j}`])
}
}
boardValues.n15 = "maually set"
console.log("n15", boardValues.n15)
console.log(Object.keys(boardValues).sort((a, b) => Number(a.slice(1)) - Number(b.slice(1)))) //sorted values
return boardValues
}
n = 7
boardValues = generateBoardValues(n)
board = generateBoard(n, "white")
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
<p [innerHTML]="boardValues.n12"></p> <!--Works-->
<div [innerHTML]="board | safeHTML"> <!-- displays table but not desired cell contents-->

This approche is very expensive. I wouldn't use innerHTML in your case. You should handle this in your template file and use *ngFor.

Related

Appending to DOM after the loop

I would like to know how to append to the DOM just once after these nested loops.
Also, the variable letters is dynamic, so I would need to 'reset' my appended grid when a new string is passed to letters
let letters = "abcdefghijkl"
for (let i = 0; i < letters.length; i++) {
var musicRowID = `${letters.charAt(i)}01`;
$("#music-grid").append(`<div id="music-row-${musicRowID}" class="row no-gutters"></div>`);
for (let j = 1; j <= 12; j++) {
var columnID = letters.charAt(i) + (j < 10 ? "0" : "") + j;
$(`#music-row-${musicRowID}`).append(
`<div class="col-1"><button id="${columnID}" class="btn bar song">${columnID.toUpperCase()}</button></div>`
);
}
}
Thank you in advance!
EDIT
After the answer from T.J. Crowder I tried to incorporate my code to be able to populate my grid from the inputs, but when I unselect one of the inputs, that row isn't cleared.
let letters = 'abcdefghijkl';
let html = "";
$(".list-checkbox-item").change(function() {
let chosenLetters = $(this).val();
if ($(this).is(":checked")) {
arrayOfChoices.push(chosenLetters);
} else {
arrayOfChoices.splice($.inArray(chosenLetters, arrayOfChoices), 1);
}
letters = arrayOfChoices.sort().join(""); // Gives me a string with chosen letters ordered alphabetically
console.log(
`This is my string in var 'letters' ordered alphabetically (Need to clear grid after each instantiation or append after the loop): %c${letters}`,
"color: red; font-weight: bold; font-size: 16px"
);
for (let i = 0; i < letters.length; i++) {
var musicRowID = `${letters.charAt(i)}01`;
html += `<div id="music-row-${musicRowID}" class="row no-gutters">`; // *** No `</div>` yet
for (let j = 1; j <= 12; j++) {
var columnID = letters.charAt(i) + (j < 10 ? "0" : "") + j;
html += `<div class="col-1"><button id="${columnID}" class="btn bar song">${columnID.toUpperCase()}</button></div>`;
}
html += "</div>";
}
$("#music-grid").html(html);
});
What am I doing wrong?
Assuming #music-grid is empty before you run this code the first time, build up the HTML in a string and then use html() to replace the contents of #music-grid rather than appending to it:
let html = "";
for (let i = 0; i < letters.length; i++) {
var musicRowID = `${letters.charAt(i)}01`;
html += `<div id="music-row-${musicRowID}" class="row no-gutters">`; // *** No `</div>` yet
for (let j = 1; j <= 12; j++) {
var columnID = letters.charAt(i) + (j < 10 ? "0" : "") + j;
html +=
`<div class="col-1"><button id="${columnID}" class="btn bar song">${columnID.toUpperCase()}</button></div>`;
}
html += "</div>";
}
$("#music-grid").html(html);
You also see people building up the HTML in an array and using array.join("") at the end to get a single string, but with modern JavaScript engines it's really a wash either way...

How do I add 'x' number of <td> elements into a <tr> element in a for loop? [jQuery]

I am trying to make a square table dynamically through jQuery
My code so far is
$(document).ready(function() {
$('body').append('<table></table>');
initial();
});
var input = 16
function initial () {
for (i = 0; i < input; i++) {
$('table').append('<tr></tr>');
$('tr').append('<td></td>');
}
}
What I am trying to do is if I add 16 table row elements, then 16 table data elements will be added to each one, effectively creating a 16x16 grid
My current code only creates half of the table
I have to do this through jQuery
Sorry if it's simple, but I'm a bit daft
Thanks
You have to make two loops one after another:
$(document).ready(function() {
$('body').append('<table></table>');
initial();
});
var input = 16
function initial () {
for (i = 0; i < input; i++) {
$('table').append('<tr></tr>');
}
for (j = 0; j < input; j++) {
$('tr').append('<td>content</td>');
}
}
Btw its wrong way to create table, because you every time referring to DOM, which is expensive. You should first create string with table, then append it once to DOM:
var input = 16
function initial () {
var output = "<table>"
for (i = 0; i < input; i++) {
output += "<tr>";
for (j = 0; j < input; j++) {
output += "<td>content</td>";
}
output += "</tr>";
}
output += "</table>"
$('body').append(output);
}

How to append multiplication result of 2 vars in table cells?

I'm prompting the user to enter a number for row and another for column, then construct a table using the given numbers and numbering each cell accordingly.
However, I want my final result to be displayed as a multiplication table, like the image below:
multiplication table image
And here's what my code looks like so far:
var table = document.getElementById("table");
var temp = "<table border = 1 border-collapse = collapse>";
for (var i = 0; i < row; i++){
temp += "<tr>";
if (i == 0){
for (var j = 0; j < column; j++){
temp += "<td height = 20 width = 40>" + (j+1) + "</td>";
}
} else{
for (var j = 0; j < column; j++){
temp += "<td height = 20 width = 40>" + (i+1) + "</td>";
}
}
temp += "</tr>";
}
temp += "</table>";
table.innerHTML=temp;
That is nearly right already. What you are looking for is to put (i+1)*(j+1) in the second for statement.
I'm not sure how you get your user input. Prompt is not usually a great way to get input but because you said that's what you used the example below uses prompt. The problem with it is that it blocks everything else: not only your own webpage, but the entire browser. If you have not done so already, you might consider getting your user-inputted numbers from an HTML form.
You might also want input checking code. I've included it below in a full HTML/JS solution. It gives the user 3 chances to enter a number for both the row and the column. If the user fails to do so, it outputs an error message.
<html>
<head>
</head>
<body>
<div id='table'></div>
<script>
var table = document.getElementById('table');
var attemptCounter = 0;
var maxAttempts = 2;
var temp='<table border = 1 border-collapse = collapse>';
var row;
var column;
while ((typeof row !=='number' || attemptCounter <= maxAttempts) && isNaN(row) ){
var row = parseInt(prompt("Please enter the number of ROWS for your table:"),10);
if(typeof row ==='number' && !isNaN(row)){break;};
attemptCounter++;
}
if(attemptCounter >= maxAttempts+1){
table.innerHTML = 'Error: expected NUMBER for number of rows';
}
if(attemptCounter < maxAttempts+1){
attemptCounter = 0;
while ((typeof column !=='number' || attemptCounter <= maxAttempts) && isNaN(column) ){
var column = parseInt(prompt("Please enter the number of COLUMNS for your table:"),10);
if(typeof column ==='number' && !isNaN(column)){break;};
attemptCounter++;
}
if(attemptCounter < maxAttempts+1){
for (var i = 0; i < row; i++){
temp += "<tr>";
if (i == 0){
for (var j = 0; j < column; j++){
temp += "<td height = 20 width = 40>" + (j+1) + "</td>";
}
} else {
for (var j = 0; j < column; j++){
temp += "<td height = 20 width = 40>" + (i+1)*(j+1) + "</td>";
}
}
temp += "</tr>";
}
temp += "</table>";
table.innerHTML=temp;
} else {
table.innerHTML = 'Error: expected NUMBER for number of columns';
}
}
</script>
</body>
</html>
Your code nearly works. If you change your (i+1) and (j+1) to (i * j) you'll see that the table created works, except for the first row and first column (as multiplying by 0 will always give 0).
However to deal with the multiplication with 0 in the first column and row there needs to be some conditional that checks if one of the values (either i or j are 0) and then changes the value to 1.
I've only changed what happens in your for loop.
for (var i = 0; i < row; i++){
temp += "<tr>";
for (var j = 0; j < column; j++){
// Here I have split your temp string.
temp += "<td height = 20 width = 40>";
if (i == 0 && j ==0){ // if both i and j are 0 then add a 0 to temp.
temp += 0;
} else {
// Multiply them together changing 0 to 1 (solving the 0's problem)
temp += (i == 0 ? 1 : i) * (j == 0 ? 1 : j);
}
temp += "</td>";
}
temp += "</tr>";
}
temp += "</table>";
table.innerHTML=temp;
i==0?1:i is a ternary operator which is just like a little if statement. It checks if i is 0 and if it evaluates to true it returns 1, otherwise it returns the value of i. Read more about it here
Putting it all together,
temp += (i==0?1:i) * (j==0?1:j) multiplies the values in the table together and also prevents multiplication with 0 in the headings.
Build HTML as string is not bad but it is better to work with DOM model. Here is an example.
<!DOCTYPE html>
<html>
<head>
<title>Build Table</title>
<meta charset="utf-8" />
<style type="text/css">
.tbl{border:solid 1px #ccc}
.tbl tr:first-child td,
.tbl td:first-child{background:#ccc;padding:4px}
</style>
<script type="text/javascript">
'use strict';
function buildTable() {
//get numbers from micro-form
var rows = document.getElementById('rows').value;
var cols = document.getElementById('cols').value;
//create table
var tbl = document.createElement('table');
tbl.className = 'tbl';//it is better then inline style
//note that HTML table has its own DOM model
var tr = tbl.insertRow(-1);//insert new row
//first row is special
tr.insertCell(-1).innerHTML = 'X';
//so treat it accordingly
for (var i = 1; i < cols; i++) {
tr.insertCell(-1).innerHTML = i;//insert new cell and set value inside
}
//remaining rows
for (i = 1; i < rows; i++) {
tr = tbl.insertRow(-1);
//first column is special
tr.insertCell(-1).innerHTML = i;
for (var j = 1; j < cols; j++) {
tr.insertCell(-1).innerHTML = i * j;
}
}
//well done. Place our table in a container
document.getElementById('table').appendChild(tbl);
}
</script>
</head>
<body>
<div>
Rows: <input type="number" id="rows" min="2" max="10" value="10" />
Columns: <input type="number" id="cols" min="2" max="10" value="10" />
<button onclick="buildTable()">Build Table</button>
</div>
<div id="table"></div>
</body>
</html>

Dynamic creation of table for web service response in html js

Below is the code for displaying webservices json response using li,ul tags dynamically it works fine but i need the same for table using tr,td the main thing to be noted is i need to save the onclick data here which is data(doc1) .help please thanks in advance
ul = $('#mydemo1');
ul.html(null);
for (var i = 0; i <= result.length; i++)
{
var doc1 = result[i];
li = $('<li/>').html(doc1.Name).data(doc1);
li.append($('<br/>'));
li.append($('<hr>'));
ul.append(li);
}
<ul id="mydemo1" style="text-align:left;"> </ul>
Try this...
var tableelement= $('#mydemo1'); //where mydemo1 is the id of some table in DOM
for (var i = 0; i < result.length; i++) {
tableelement.append(createRow(result[i]));
}
function createRow(rowObject) { //dynamically adding rows to the Table where each row contains the name
var trElement = "<tr>";
trElement += "<td>" + rowObject.Name + "</td>";
return trElement;
}

Java Script add value of variable to object property?

I'm new here and to JavaScript. I have an assignment that asks "create a new property in the foodInfo object plus the value of the toppings variable, and set the ne property's value to to value of the current element in the toppingBoxes array."
Here is the code I have that is not working, I have tried multiple things but cant get it to print out the toppings on the page:
for (var i = 0; i < toppingBoxes.length; i++) {
if (toppingBoxes[i].checked) {
toppings = toppings + 1;
foodInfo.topping[toppings] = toppingBoxes[i].value;
}
}
Here is the code the assignment gave me to print it, so this code is correct, but the code above is what I need help with:
foodSummary.innerHTML += "<ul>";
for (var i = 1; i < 6; i++) {
if (foodInfo["topping" + i]) {
foodSummary.innerHTML += "<li>" + foodInfo["topping" + i] + "</li>";
}
}
foodSummary.innerHTML += "</ul>";
I know the code stops running when it hits the line "foodInfo.topping[toppings] = toppingBoxes[i].value;" so I know that is wrong. I am having trouble with the instructions I mentioned above...any help to get this working? Thank you in advance!!
Try this:
var toppings = 0;
for (var i = 0; i < toppingBoxes.length; i++) {
if (toppingBoxes[i].checked) {
toppings = toppings + 1;
foodInfo['toppings' + toppings] = toppingBoxes[i].value;
}
}

Categories