Error when creating a dynamic list from Angular - html

I am trying to create a dynamic table from Angular, my idea is to create a function where the user enters the number of rows and columns and from those values, a table is created with the number of rows and columns that the user has defined. The problem is that when I want to insert a cell for each position of ' i ', the labels are printed literally and a table is not created Code:
HTML
<div class="container" id="tabla">
{{table}}
</div>
TypeScript
export class tableMaker implements OnInit {
table: string = '';
amount: number = 10;
constructor() {
}
dinamicTable(amount: number) {
this.table = `<table>`;
for (let i = 0; i < amount; i++) {
this.table += `
<tr>
<td>7</td>
</tr>
`;
}
this.table += '</table>';
}
}
Esto es lo que se imprime en pantalla: (https://i.stack.imgur.com/weOO7.png)
I would like to be able to print a table with a cell in each position of ' i '

Use one way flow syntax property binding:
<div [innerHTML]="table"></div>
From angular docs: "Angular recognizes the value as unsafe and automatically sanitizes it, which removes the tag but keeps safe content such as the element."

Related

Show a table with specific array content in angular

In my app.component.ts i created the array "data", which consists of a few strings separated by comma.
I parse it into json and with console.log i am able tto clearly present it the way i'd like to doing this:
const jsonData = Papa.parse(content, {skipEmptyLines: true,});
jsonData.data.forEach(function(data){
console.log(data[0], "|" , data[1] , "|" , data[2] , "|" , data[3]);
}
Its basically going through each line and then selecting the three data contents i'd like to be shown.
I want to display the array that way on my website in my app.component.html, but only the first three entries, because it has countless ones...
I've tried using the data as a json element
<td>{{data.entry1}}</td>
But i didn't succeed. Its quite a simple task but im really stuck here. Could anyone explain why I dont get it?
An easy way is to create an array that holds the data you would like to show.
ts
partialData = [];
yourFunction = () => {
:
jsonData.data.forEach(function(column) {
const line = [];
for (let i = 0; i < 4; i++) {
line.push(column[i]);
};
partialData.push(line);
});
}
html
<tr *ngFor="let line of partialData">
<td *ngFor="let column of line">
{{column}}
</td>
</tr>

Typescript - Conditionally create duplicate elements

I have a variable which users of my application can modify, say:
let myValue = 3;
In my html I wish to create as many duplicates of an element as the value of the variable is.
In my case, myValue is 3, then we create the div element 3 times.
<div>I am a duplicate.</div>
<div>I am a duplicate.</div>
<div>I am a duplicate.</div>
myValue can be a higher or lower number as well. Whatever it is, I would like to have that many duplicates of my element. How can I achieve this?
P.S. I am still new to Angular and Typescript so please don't go hard on me if this is a rather simple question.
// creates an array in TS file based on myValue
duplicates = Array(myValue).fill(null).map( (x,index) => index );
// use ngFor in HTML
<div ngFor="let duplicate of duplicates">
<div>I am a duplicate. {{ duplicate }}</div>
</div>
This can be done using the Iterator Protocol.
HTML
<ol>
<li *ngFor="let i of value">{{ i }}</li>
</ol>
TypeScript
export class AppComponent {
title = 'app';
_value = 3;
get value(){
let iterable = {
length: this._value,
index: 0,
next: () => {
if (iterable.index < iterable.length) {
return {value: iterable.index++, done: false};
} else {
iterable.index = 0;
return {done: true};
}
},
[Symbol.iterator]: function() { return this }
};
return iterable;
}
}

Display array from json data to cards

So, im a little bit lost here and i need some help.
I have a json that come from the server with data that i dont know.
Based on that i found a solution to display the data on html here on SO:
https://stackoverflow.com/a/50352965/9721446
But the problem is that each "item" is an entry from array, so if i ngfor array, it outputs each line as an item, and i want the item to be all entries of each result.
heres the html:
<ng-container *ngFor="let item of singleArray | paginate: { itemsPerPage:411, currentPage: p} ">
<!-- All the entries -->
<div class="w3-container">
<!-- Table view-->
<table class="center">
<tr *ngIf="!item.tag.includes('URL') && !item.tag.includes('linkChal')">
<td><div class="col-xs-auto thick">{{item.tag.toLowerCase() | translate}}</div></td>
<td class="tab">{{item.value}}</td>
</tr>
<tr *ngIf="item.tag.includes('URL')">
<td>Link da entrada: </td>
<td> - Ver mais -</td>
</tr>
<tr *ngIf="item.tag.includes('linkChal')">
<td>Link do Challenge: </td>
<td> - Challenge -</td>
</tr>
</table>
<div style="background-color: #ff7d2a">
<ul *ngIf=" item.tag.includes('---------')"><p>New Entry</p></ul>
</div>
</div>
</ng-container>
Ts:
for(let i in res)
{
//array with entities from json
this.entity.push(i);
for(let j in res[i])
{
let val = Number(j)+1;
this.cont.push(i +" - nÂș: " + val );
this.singleArray.push({
tag: i,
value: val
});
for(let t in res[i][j])
{
this.test.push(t);
this.cont.push(t +" - "+ this.responseList[i][j][t]) ;
if(t.split(".",2)[1] === "CompositeId")
{
this.test.push("URL:");
//Get the id
this.cont.push(this.moduleName + "/" + t.split(".",2)[0] + "/" + this.responseList[i][j][t].match(/=(.*)_/)[1]);
//debugger;
this.singleArray.push({
tag: "URL:",
value: this.moduleName + "/" + t.split(".",2)[0] + "/" + this.responseList[i][j][t].match(/=(.*)_/)[1]
});
}
else if(t.split(".",2)[1] === "Challenge")
{
this.singleArray.push({
tag: "linkChal",
value: this.moduleName + "/" +t.split(".",2)[1] + "/" + this.responseList[i][j][t].match(/=(.*)_/)[1]
});
}
else {
this.singleArray.push({
tag: t,
value: this.responseList[i][j][t]
});
}
}
this.test.push("\n");
this.cont.push("\n");
this.singleArray.push({
tag: "---------\n",
value: "--------\n"
});
//it ends an item here
}
}
Heres the output i have with that:
Each one line is an entry from the array, the big question is, how to transform all lines/entries until "New Entry" and made an single item to ngfor and display data into a card that i already have..)
I've tried to create an array and push the singleArray into it (hoping each entry of that new array was an item that i want), at the end of for(let j in res[i]) on .ts but it just repeated all the entries creating a bunch of entries..
here, at the end of that for, i've tried to push an array with something, then ngfor it (it gives me the number items that i want, but then i dont have the results to access them..)
Has anyone had this problem before?
thanks in advance
Edit: here's what singleArray looks like:
Your best bet here is to follow the single responsibility principal and separate the concerns of each class.
Stop trying to do this all in the view and separate out the responsibility of formatting the data and the problem will seem much simpler.
Make a new class to define the model you want your view to use
Have your view implement this new ideal model that you control
Generate some test data to make get this looking like what you want
Create a new class who's entire responsibility is to turn the external model from the api response into this new internal model
json2ts may help generate a better external model from the response, but it may not be of much use in this case
Once you have done the above, based on your sample output, it should be fairly simple to convert from the external model into the internal model. It's hard to convey this, but assuming the hyphens are the item separator you could simply do something like the following:
const externalItems = // data from api
const internalItems = [];
let currentInternalItem = {};
externalItems.forEach(item => {
if (item.tag.startsWith('---------')) {
internalItems.push(currentInternalItem);
currentInternalItem = {};
} else {
currentInternalItem[item.tag] = item.value;
}
});
This would group the array back into an object that you can use in your view.
I think I'm complicating too much.. The objective here is to display what comes from JSON into specific locations, like a card, with header and content, to better display the results.
I have a service that gives me a JSON, that i never knows what inside, that depends on the search term and can bring much information. For example:
If the term is "Idea":
If the term is Challenge:
My .ts file is only console.log what comes from the api.
ngOnInit() {
var setting = {setting to connect the server..}
enter code here
$.ajax(settings).done((rest) => this.response(rest));
}
response(res){
console.log(res);
}
How can i display that data the way i want?
Sorry for the long post, and for not beeing objective on the main question.

Pass an array of checkbox values to a Javascript string

What I want to happen with the following code is when a user checks multiple data centers and then selects a type of change it will automatically refresh the description and Impact text area with a unique string statement including the data centers the user has chosen.
Can someone advise me what I am doing wrong?
JS:
function updateDescImpact() {
var changeSel = document.changeform.change_type;
var ChangeType = (changeSel.options[changeSel.selectedIndex].value);
var description = " ";
var impact = " ";
var data_center = "";
var inputs = document.getElementsByTagName('input');
for (var x = 0; x < inputs.length; x++) {
{
if (inputs[x].type == "checkbox" && inputs[x].name == 'data_center[]') {
if (inputs[x].checked == true) {
data_center += inputs[x].value + ',';
}
}
}
if (/,$/.test(data_center)) {
data_center = date_center.replace(/,$/, "")
}
if (ChangeType == "Filer Changes") {
description = "This is the Filer Change Description for $('data_center')";
impact = "This is the Filer Changes impact statement for $('data_center')";
} else if (ChangeType == "DNS Changes") {
description = "This is the DNS Change Description for $('data_center')";
impact = "This is the DNS Changes impact statement for $('data_center')";
} else {
description = "";
impact = "";
}
document.changeform.description.value = description;
document.changeform.impact.value = impact;
}
HTML:
<form action="" id="changeform" method="post" name="changeform">
<input type="submit" value="submit change">
<table>
<tr valign="top">
<td><strong>Data Center</strong></td>
<td><input name="data_center[]" type="checkbox" value="zone1">Zone1
<input name="data_center[]" type="checkbox" value=
"Zone2">Zone2</td>
</tr>
<tr valign="top">
<td><strong>Change Type</strong></td>
<td><select id="change_type" name="change_type" onchange=
"updateDescImpact()">
<option value="DNS Changes">
DNS Changes
</option>
<option value="Filer Changes">
Filer Changes
</option>
</select></td>
</tr>
<tr valign="top">
<td><strong>Description</strong></td>
<td>
<textarea cols="50" id="description" name="description" rows="10">
This text needs to be updated
</textarea>
</td>
</tr>
<tr valign="top">
<td><strong>Service Impact</strong></td>
<td>
<textarea cols="50" id="impact" name="impact" rows="10">
This text needes to be updated
</textarea></td>
</tr>
</table>
</form>
With only a minimal idea of what it is you want to do with the string, once you've got it, I've put together the following:
function updateDescImpact() {
// declaring all the variables, separating them with commas, at once.
// retrieves the element:
var changeSel = document.getElementById('change_type'),
// defines the various default messages:
changes = {
'DNS': {
'impact': 'This is the DNS Change impact statement for $("data_centre")',
'description': 'This is the DNS Change Description for $("data_center")'
},
'Filer': {
'impact': 'This is the Filer Changes impact statement for $("data_centre")',
'description': 'This is the Filer Change Description for $("data_center")'
}
},
ChangeType = (changeSel.options[changeSel.selectedIndex].value),
// creates an array:
data_center = [],
inputs = document.getElementsByTagName('input');
for (var x = 0, len = inputs.length; x < len; x++) {
{
if (inputs[x].type == "checkbox" && inputs[x].name == 'data_center[]') {
if (inputs[x].checked === true) {
// adds the checkbox-values to the array:
data_center.push(inputs[x].value);
}
}
}
// creates another key for the 'changes' object,
// the value of which is determined by whether the array contains values,
// if it *does*, then those values are joined together using the given string,
// if *not*, then the value is an empty string.
// in both cases the value is followed by a period.
changes.data_centers = (data_center.length > 0 ? ': ' + data_center.join(', ') : '') + '.';
document.getElementById('description').value = changes[ChangeType].description + changes.data_centers;
document.getElementById('impact').value = changes[ChangeType].impact + changes.data_centers;
}
}
JS Fiddle demo.
In the next part I'll try to explain the changes I've made, but the simple, central, premise is to minimise the amount of work you're doing, and to reduce the scope for errors to creep in.
Where you were using:
document.changeform.change_type;
I've chosen, instead, to use:
var changeSel = document.getElementById('change_type');
This is because using the id of the relevant element is faster than navigation through the DOM using two ids, especially since there's no guarantee that the element will be recognised by its id when it's used as a global variable without being declared. It works almost everywhere, yes, but it's not a standard, or specified, behaviour in JavaScript and was a convenience offered, initially, in Internet Explorer. This convenience is not guaranteed to present in future, however.
Also, an id is (and must be) unique within the document, therefore there's no sense in using two, when one will do perfectly well.
The use of an object to contain the various alternative strings you (might) want to use, this is to reduce the need for multiple if/else if/else statements. And makes it somewhat easier to update the function in future to address new options and requirements. As stated, above, this is to minimise the amount of work you have to do (now, and in the future).
Where you used:
if (/,$/.test(data_center)) {
data_center = date_center.replace(/,$/, "")
}
I chose, instead, to use an array:
data_center = [],
This allows us to avoid having to use regular expressions which, while in this case was simple enough, is not always the case and, again, is more difficult than necessary for the finished result. Whereas an array can use the .join() method (used later) which will join the array elements together with the defined string, and will not result in a trailing-comma.
I've also used getElementById() in the last two lines to assign the various strings/values to the relevant elements. For precisely the same reason as outlined above.

In WatiN, how to verify a table's column headers and rows?

Consider this HTML table:
<table id="build-table">
<tr>
<th>Build ID</th>
<th>Build Time</th>
</tr>
<tr>
<td>
5.1
</td>
<td>02.06.2011 13:33:03</td>
</tr>
</table>
How would I verify in WatiN that the table has the correct headers (Build ID and Build Time), and the correct content (in this case, one row containing the given hyperlink and date string)?
Sorry, we created a custom TableHandler, using the basic table building blocks: Here is the sample code:
public TableController(Regex tableControlId)
{ InitializeMembers(Find.ById(tableControlId), true); }
private void InitializeMembers(WatiN.Core.Constraints.AttributeConstraint tableControlId, bool hasColumnHeaders)
{
if (tableControlId == null)
{
throw new ArgumentNullException("tableControlId", "'tableControlId' passed in should not be null.");
}
WatiN.Core.Constraints.AttributeConstraint newTableControlId = tableControlId;
Assert.IsTrue(IE.Table(newTableControlId).Exists, "Table with id '" + newTableControlId.ToString() + "' does not exist on this page.");
_controlId = tableControlId;
_hasColumnHeaders = hasColumnHeaders;
_columnHeaders = (hasColumnHeaders) ? GetTableColumnHeaders() : null;
_totalRows = Table.TableRows.Count;
_totalColumns = GetAllColumnDataFromRow((TableRow)Table.TableRows[0], hasColumnHeaders).Count;
}
private StringCollection GetTableColumnHeaders()
{
return GetAllColumnDataFromRow((TableRow)Table.TableRows[0], true);
}
private StringCollection GetAllColumnDataFromRow(TableRow tableRow, bool isTableHeaderRow)
{
StringCollection RowValues = new StringCollection();
if (tableRow == null)
{
for (int colCounter = 0; colCounter < this.TotalColumns; colCounter++) RowValues.Add(String.Empty);
}
if (isTableHeaderRow)
{
foreach (Element e in tableRow.Elements)
{
if (e.TagName == "TH")
{
RowValues.Add(e.Text);
}
}
}
else
{
foreach (TableCell tc in tableRow.TableCells)
{
if (String.IsNullOrEmpty(tc.Text))
{
RowValues.Add(String.Empty);
}
else
{
RowValues.Add(tc.Text);
}
}
}
//fill up for the missing cells, if any, with blanks
int actualCellsInRow = tableRow.TableCells.Count;
int expectedCellsInRow = this.TotalColumns;
for (int colCounter = actualCellsInRow; colCounter < expectedCellsInRow; colCounter++)
{
RowValues.Add(String.Empty);
}
return RowValues;
}
Hope this helps.
There are at least three ways to do this:
I think Watin provides a tablehandler / tablecontroller related methods using which you can retrieve this information. You might want to search on that topic if you want to take this approach.
Using Xpath query (XML). Since you have the id of the table, you can use a XPath query to reach the node where your header is and verify that using a static string "Build ID" in your code. Same with the other pieces of information.
Regex - Using Regular Expressions, you can check if that text exists on the control / page.
If you view the source of your page, you will know the pattern that you should look for on the page. In fact, a simple Assert.AreEqual(true, new Regex("Build ID")Match.Success) should do the trick. However, this is a check that is purely done to see if the text exists on the page. You will not be looking at anything beyond that. Also, if you have multiple occurrences of the text then you should be considering the array of matches you get before you say, "yep, found it".
Note: You may have to checkout the syntax for using a Regex. The above information is just an abstract of what it would look like.
Cheers.