Angular ngOnInit() - loop into a file HTML - html

I would like to display the values following -> 0 1 2 without using an array.
export class AppComponent {
ngOnInit() {
for (let i = 0; i < 3; i++) {
console.log('Number ' + i);
}
}
}
Into the file HTML what should I put to retrieve my values?
Here is my code here Stackblitz
Thank you for your help.

Html is bound with some values from the .ts file which are declared on class level as fields
export class AppComponent {
field1: any
....
Local variables like the ones that you use in a for loop are not bound in the html file and can't be used there.
Html is bound and has access to either public class field variables or public methods. You could make a public method to return something from a for loop and then the html could invoke that public method and render the return value.
example
public method1(): string {
let text = '';
for (let i = 0; i < 3; i++) {
if (text === ''){
text = text + i;
} else {
text = text + ' ' + i;
}
}
return text;
}
and then on .html file you can do
<div> {{method1()}} </div>
But the method ngOnInit() that you use is a method which is invoked automatically by angular in the angular lifecycle so shouldn't be used to achieve that.

I built a pipe that generates a sequence of consecutive numbers (starting with 0) that you can use directly inside of the template, not even needing the for in the ngOnInit method.
#Pipe({
name: 'sequenceGenerator'
})
export class SequenceGenerator implements PipeTransform {
transform(numberOfItems?: number): number[] {
const length = numberOfItems ?? 0;
const sequence = Array.from({ length: length }, (_, i) => i);
return sequence;
}
}
And you can use it like this:
<div *ngFor="let item of (3 | sequenceGenerator)">
{{item}}
</div>
Have a look at the stackblitz example for further reference.

Related

How to change Background image every x amount of seconds

I'm pretty new to Angular and programming in general.
I wanted to change the background image of my Page by using the setInterval method. It should change every second but for some reason, it changes much faster.
Component:
export class AppComponent implements OnInit {
images: Image[] = [];
changeBackgroundCounter = 0;
constructor(private imagesService: ImagesService) {}
getImage() {
setInterval(() => {
this.changeBackgroundCounter = this.changeBackgroundCounter + 1;
if (this.changeBackgroundCounter > this.images.length - 1) {
this.changeBackgroundCounter = 0;
}
}, 1000);
return this.images[this.changeBackgroundCounter].image;
}
ngOnInit() {
this.images = this.imagesService.getImages();
console.log(this.images[0]);
}
}
Template:
<div [ngStyle]="{'background-image': 'url('+ getImage() + ')'}" [ngClass]="{imageBackground: getImage()}">
Stackblitz link
In your template, you have
<div [ngStyle]="{'background-image': 'url('+ getImage() + ')'}" [ngClass]="{imageBackground: getImage()}">
This means angular keeps calling the getImage() method to find out what the background should be. This will happen very frequently. Each time the method is called, a new interval is created, so there end up being loads of them. You can see this by putting a line of logging within your interval and you will see how often it's being triggered.
setInterval(() => {
console.log('interval triggered'); // <------- add this line to see how often this code is running
this.changeBackgroundCounter = this.changeBackgroundCounter + 1;
if (this.changeBackgroundCounter > this.images.length - 1) {
this.changeBackgroundCounter = 0;
}
}, 1000);
To fix your problem, you need to call getImage() only once, which can be done within ngOnInit(). The template can get the image from images[this.changeBackgroundCounter].image.
You're complicating your code for nothing. Create a variable, equal to a string, and assign it a new value every X seconds in your ngOnInit() !
Then set the background image equals to that variable, and voilĂ  !
Here is what it look like in code :
export class AppComponent implements OnInit {
images: Image[] = [];
actualImage: string;
changeBackgroundCounter = 0;
constructor(private imagesService: ImagesService) {}
ngOnInit() {
this.images = this.imagesService.getImages();
this.actualImage = this.images[0].image;
setInterval(() => {
this.changeBackgroundCounter++;
if (this.changeBackgroundCounter > this.images.length - 1) {
this.changeBackgroundCounter = 0;
}
this.actualImage = this.images[this.changeBackgroundCounter].image;
}, 5000);
}
}
I kept as much as possible of your inital code. My new variable is called actualImage, I set a default value in my ngOnInit, right after you get all your images from your service.
Then I call setInterval and set a new value to actualImage every 5 seconds !
https://stackblitz.com/edit/angular-setinterval-f5bghq
CARE: When using setInterval, be used to clear it on ngOnDestroy(), it can lead to some weird bugs you don't want to get involved in.
Simply create an other variable, type any, and do the following :
this.interval = setInterval(() => {...})
ngOnDestroy() {
clearInterval(this.interval);
}

Parse JSON to Angular object?

I have a JSON similar to the following:
{
"A01": "DescA",
"A02": "DescB",
"A03": "DescC",
"A04": "DescE"
}
And a class defined like this:
export class Element {
public id: string;
public description: string;
}
How can I parse the JSON and get an Element array with el[0].id == "A01" and el[0].description == "DescA" and so on?
Since your JSON has a "customized" sturcture (which means it is not the same result as serializing an Element object), you need to assign values manually
class Element {
id = "";
description = "";
constructor(id, desc){
this.id = id;
this.description = desc;
}
};
const apiResult = {
"A01": "DescA",
"A02": "DescB",
"A03": "DescC",
"A04": "DescE"
};
const elementArray = Object.keys(apiResult).map(key => new Element(key, apiResult[key]));
console.log(elementArray);
Or (I assume you are using Angular 2+) you can use typestack/class-transformer library to parse/deserialize from JSON by using decorators. It has some advantage such as nested deserialization.
export class Element {
public id: string;
public description: string;
}
const data = {
"A01": "DescA",
"A02": "DescB",
"A03": "DescC",
"A04": "DescE"
};
const elements: Element[] = Object.keys(data)
.map(key => {
const element = new Element();
element.description = data[key];
element.id = key;
return element;
});

Limiting the adding of rows in a form to a certain number

I am working on adding and deleting a set of fields in a row, i am able to do this via the following code,
but will have to limit it to a certain number, how would i be able to do that? Thanks in advance
The below is the code.
I am using an array and then using add and delete function for
Component File
`
get formArr(){
return this.finDetailsForm.get('itemRows') as FormArray;
}
initItemRows(){
return this.fb.group({
acc_name: '',
inv_amt: '',
v_inv_date: '',
});
}
addNewRow(){
this.formArr.push(this.initItemRows());
}
deleteRow(index: number) {
this.formArr.removeAt(index);
}
Attaching an image below of it
you can count length of array before push element.
addNewRow(){
if(this.formArr.lenght < 5){
this.formArr.push(this.initItemRows());
}
}
You have 2 solutions:
Update your component function as below:
class YourComponent {
private static MAX_ITEMS = << a number >>;
// ... constructor and rest ...
addNewRow(): void {
if (this.formArr.length < YourComponent.MAX_ITEMS) {
return;
}
this.formArr.push(this.initItemRows());
}
or you simply hide the button "+" when you reached the maximum item you want:
your-component.ts
get maxItemsReached(): boolean {
return this.formArr.length >= YourComponent.MAX_ITEMS;
}
your-component.html
<input type="button" *ngIf="!maxItemsReached">Add</input>

Explicitly Trusting HTML - Angular 5

I have created a pipe to format the XML string that I get from a server as a pretty printing.
import { Pipe, PipeTransform } from '#angular/core';
import * as jQuery from 'jquery';
import { escape } from 'querystring';
import { DomSanitizer, SafeHtml } from '#angular/platform-browser';
//import * as angular from '../angular.js';
//CAMBIAR a string
#Pipe({
name: 'formatXml'
})
export class FormatXmlPipe implements PipeTransform {
constructor(private sanitized: DomSanitizer) { }
transform(xml: String): SafeHtml {
var formatted = '';
var reg = /(>)(<)(\/*)/g;
xml = xml.replace(reg, '$1\r\n$2$3');
var pad = 0;
jQuery.each(xml.split('\r\n'), function (index, node) {
var indent = 0;
if (node.match(/.+<\/\w[^>]*>$/)) {
indent = 0;
} else if (node.match(/^<\/\w/)) {
if (pad != 0) {
pad -= 1;
}
} else if (node.match(/^<\w[^>]*[^\/]>.*$/)) {
indent = 1;
} else {
indent = 0;
}
var padding = '';
for (var i = 0; i < pad; i++) {
padding += ' ';
}
formatted += padding + node + '\r\n';
pad += indent;
});
var escaped = formatted.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/ /g, ' ').replace(/\n/g, '<br />');
// https://stackoverflow.com/questions/39240236/insert-xml-into-dom-in-angular-2
let safeEscaped = this.sanitized.bypassSecurityTrustHtml(escaped);
return safeEscaped;
//let safeEscaped = this.sanitized.bypassSecurityTrustHtml(escaped);
//return safeEscaped;
}
}
the string that I have is shown in the following div:
<tr *ngFor="let message of messages; let i = index;">
<td *ngIf="i == _rowToShow" >{{message.xmlString | formatXml}}</td>
</tr>
_rowToShow is just a variable to limit the rows to show to an specific one and it works so it can not be the problem
so basically what I think that it is happening is that I am calling correctly to the pipe formatXml and I get the correct value of the string, but it is not a trusting HTML so it does not show it properly (it shows only the original XML).
I have read that to solve this problem in Angular 1.2 was just needed to use $sce.trustAsHtml but I do not really know how to do it.
https://odetocode.com/blogs/scott/archive/2014/09/10/a-journey-with-trusted-html-in-angularjs.aspx
Update
At the end what I get is
SafeValue must use [property]=binding:
plus my XML
Any body know what can be the problem?

searchbox in angular 2 doesn't return the whole object

I'm very new to Angular2 and 4.
I have a list of items of type class (Item).
Items fields are name, price and description.
I want to make a searchbox when the user types the name of the item, it displays the correct item object.
I followed this example: http://www.angulartutorial.net/2017/03/simple-search-using-pipe-in-angular-2.html
but It didnt work, I think because if was searching between strings not objects of type item.
If you used the code in the tutorial, with an array of objects you just need to update the return statement of the transform method in your Angular2 Pipe like this:
return value.filter(function (el: any) {
return el.name.toLowerCase().indexOf(input) > -1;
})
PS: I added el.name but you can search through the description or whatever you like.
Create a custom pipe and pass search parameters to that pipe, something like below example
<li *ngFor="let n of list | FilterPipe: queryString : searchableList ">
{{n.name}} ({{n.age}})
</li>
this.searchableList = ['name','age']
And your custom pipe
#Pipe({
name: 'FilterPipe',
})
export class FilterPipe implements PipeTransform {
transform(value: any, input: string,searchableList : any) {
if (input) {
input = input.toLowerCase();
return value.filter(function (el: any) {
var isTrue = false;
for(var k in searchableList ){
if(el[searchableList[k]].toLowerCase().indexOf(input) > -1){
isTrue = true;
}
if(isTrue){
return el
}
}
})
}
return value;
}
}