Dynamic arrays in TypeScript (Angular2+) - arrays

I'm trying to create a dynamic array based on an integer, so, assuming integer i = 3, it would create an array with length 3.
counter (i: number) {
return new Array(i);
}
Why does this return an array with length 1 and a member with a value of i rather than an array with length i?
EDIT:
A walkaround to what I'm trying to do is:
counter (i: number) {
var a = new Array();
for (var x = 0; x < i; x++)
a.push(x);
return a;
}
I'm just curious about the behavior of the array constructor.

By testing your example with angular core 5.0.0, the function will create an array with length 4 for example, and when you do the resulting variable.length it will equal to 4. so, the given link before has all what you need to understand the result.
here is a demo that demonstrate that:
https://stackblitz.com/edit/angular-9effym?file=app%2Fapp.component.html
(direct link, for debugging with dev tools: https://angular-9effym.stackblitz.io)
result
Hello !
Array length: 4
value 0 = undefined
value 1 = undefined
value 2 = undefined
value 3 = undefined
code:
app.component.html:
<hello name="{{ name }}"></hello>
<div>
<br>
Array length: {{xx.length}}
<br>
<br>
<div *ngFor="let value of xx; let i = index">
value {{i}} = {{value+''}}
</div>
</form>
<br>
app.component.ts
import { Component } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
xx:any;
ngOnInit() {
this.xx = this.counter(4);
}
counter(i: number){
return new Array(i);
}
}
Note:
since we didn't initialize the elements of the array, there content will be undefined, I demonstrated that by converting the content to string using {{value+''}} in the template

Related

TypeScript gives error when calling .size on a map in React

I'm writing a React Component where I pass a map in as a prop and read the size of it later. When I type the map with TypeScript, I get no errors, but I get an error when I call .size on the map. In addition, when I try to map over that array, I get an error as well.
The map itself contains key and value pairs, both of which are strings.
Code:
import * as Styled from './assignments-list.styles'
type TMap<T> = {
[index: string] : T
}
type TAssignmentsListProps = {
assignments: TMap<string>
}
const AssignmentsList = ({assignments}: TAssignmentsListProps) => {
return (
<Styled.AssignmentsList>
<Styled.Header>
Assignments
</Styled.Header>
{
(assignments && assignments.size !== 0) ? // error here
<Styled.ListFiles>
{
Array.from(assignments).map(([name, url]) => { // error here
return (
<li key={name}>
<Styled.ListItemWrapper>
<Styled.FileName>
{name}
</Styled.FileName>
<Styled.RemoveFile>
View
</Styled.RemoveFile>
</Styled.ListItemWrapper>
</li>
)
})
}
</Styled.ListFiles> :
<Styled.BodyText>None</Styled.BodyText>
}
</Styled.AssignmentsList>
)
}
export default AssignmentsList
Any ideas?
I don't understand what is the real type of the assignments
Here error assignments.size !== 0 because you are trying to compare a string with a number, which is always false.
If can be fixed, by fixing type
type TMap<T> = {
[index: string]: T
} & {
size: number
}
or
type TMap<T> = Map<string, T>
Or if it map of string, you can check map size with
assignments && Object.keys(assignments).length !== 0
Converting your map to array depending on real map type.
If it Map then Array.from should work
If it map of string you can use How to convert an Object {} to an Array [] of key-value pairs in JavaScript

How to loop through an array of objects, loop through each object and push each object into separate arrays for ngFor looping in angular

I am working on an Angular 7 project and I am dealing with an api that returns an array of (recipes)objects on search and I want to get the all ingredients from each object so I can loop through them, get only what I want because each (recipe)object varys from the other
I have tried looping through the initial array of objects and then looped through each individual object and getting only the available ingredients per (recipe)object but I do not know how to push them into separate arrays. Whenever I push, all ingredients from all the objects gets pushed into a single array.
import { Component, OnInit } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
import { RecipesService } from 'src/app/services/recipes.service';
#Component({
selector: 'app-search',
templateUrl: './search.component.html',
styleUrls: ['./search.component.scss']
})
export class SearchComponent implements OnInit {
s: string;
ingArray: any[] = []
measurementsArr = []
nothingFound = false;
itemsFound = false;
searchedData: any[] = [];
searchresults: number;
constructor(private route: ActivatedRoute, private service:RecipesService) { }
ngOnInit() {
this.route.queryParams.subscribe(params=>{
this.s = params.s;
console.log(this.s)
})
this.searchDB()
}
searchDB(){
this.service.searchRecipeDB(this.s).subscribe((res:any)=>{
if(res.meals === null){
this.nothingFound = true;
}else{
this.searchedData = res.meals
console.log(this.searchedData)
this.itemsFound = true;
this.searchresults=this.searchedData.length;
let resultsarray = this.searchedData
for(let y of resultsarray){
for(let obj in y){
if(obj.includes('strIngredient')){
if(y[obj]!=null){
if(y[obj].length > 0){
this.ingArray.push(y[obj])
console.log(this.ingArray)
}
}
}
}
}
}
})
}
}
I have been able to loop through and get each ingredients from each (recipe)object but have been unable to push them into separate arrays without having to define multiple arrays. Is there a way I can push all ingredients from a (recipe)object into a separate array without having other ingredients from other (recipes)object added? I do not want to define several arrays because I am working with an api and I do not know the expected search results all the time search results showing an array of objects
for(let y of resultsarray){
ingrSub: Array<any> = new Array();
for(let obj in y){
if(obj.includes('strIngredient')){
if(y[obj]!=null){
if(y[obj].length > 0){
ingrSub.push(y[obj])
console.log(this.ingArray)
}
}
}
}
if(ingrSub.length > 0)
this.ingArray.push(ingrSub)
else
ingrSub = null;//just to be sure that we do not leak the memory if engine thinks the oposite in some case
}
Just push obj, not y[obj]. Probably, I do not understand the task. And use of instead of in. Or do you wanna get some ingredient substrings from object?
this.array_a = [] <- this add your array
this.array_b.forEach(array_b_item => {
array_b_item.new_variable = JSON.parse(
JSON.stringify(this.array_a)
);
});
you can add values indivualy to array_b

Angular ngFor out of order

I have an array of months. for some reason angular for loop does not follow the order of the array.
How can that be fixed?
#Component({
selector: 'my-app',
template: `<div *ngFor="let item of collection | keyvalue; index as i">{{item | json}}</div>`,
})
export class AppComponent {
collection : {key : any, value : any}[] = [];
constructor(){
let i = 0;
for(let month of moment().locale('en-US').localeData().monthsShort()){
i++;
this.collection.push({key : i,value : month});
}
}
https://stackblitz.com/edit/angular-i25npn?file=src%2Fapp%2Fapp.component.ts
According to the definition of keyvalue from the docs.
The output array will be ordered by keys. By default the comparator
will be by Unicode point value...
And therefore your keys(string) are ordered by default.
Remove the pipe | keyvalue; index as i from your *ngFor.
To follow the order, replace:
<div *ngFor="let item of collection | keyvalue; index as i">{{item | json}}</div>
by:
<div *ngFor="let item of collection">{{item | json}}</div>

Initializing array of objects - Angular

I have an array of objects and when I try to access to it, I get an error saying:
TypeError: Cannot set property 'ID' of undefined
My code is the following:
export class Carimplements OnInit {
pieces: Piece[] = [];
test(pos){
this.pieces[pos].ID = "test";
}
}
being Piece an object
export class Piece{
ID: string;
doors: string;
}
I call to test(pos) from the HTML with a valid position.
I guess that I am trying to access to the position X of an array that has not been initialized. How could I do it? Is it possible to create a constructor?
Correct syntax for defining array types in TypeScript is this:
pieces: Piece[] = [];
The error is a runtime error. When you run your app you have an empty array pieces (but the variable still initialized with []) but you call test(whatever) which tries to access an array element whatever that doesn't exist.
You can do for example this:
pieces: Piece[] = [{
ID: '1',
doors: 'foo'
}];
and then test this method with test(0).
How about this?
export class Carimplements OnInit {
pieces: Piece[] = [];
test(pos){
this.pieces[pos] = {ID: "test"};
}
}
let pieces: Piece[] = [];
//initialize object before assigning value
test(pos){
this.pieces[pos] = new Piece();
this.pieces[pos].ID = "test";
}
You can try the following method
test(pos){
if(pos < this.pieces.length)
this.pieces[pos].ID = "test";
else
// throw error
}

Forms/Angular 4 - Display elements of an array of components in a dynamic number of columns

My objectif is to display the elements of a given array of components in a number of columns that changes dynamically.
Example:
Let's say that we have the following array:
[a, b, c, d, e, f, g, h]
If the number of columns is 3, the display of this array's elements would be:
a b c
d e f
g h
Otherwise, if the number of columns is 5, the display would be:
a b c d e
f g h
Implementation:
I implemented a solution that serves my purpose in this Plunker (the number of columns is stored in the variable columnsNumber), but it's pure algorithmic and I am wondering if there would be better solutions.
Usages:
This solution can be used for example to construct dynamic forms, where each column contains a form's control or component.
The main problem with the approach that you're using in the plunker is that the function "initArrayOfNumbers" is being called on every ChangeDetection.
To solve your problem I'd recommend you to use SlicePipe, like this:
<div *ngFor="let i of rows">
<span *ngFor="let col of columns | slice: (i * columnSize): (i + 1) * columnSize">
{{ col.name }}
</span>
</div>
Where rows is defined here:
this.rows = Array.from({ length: Math.ceil(this.columns.length / this.columnSize) }, (v, k) => k);
Explanation:
It's a simple array containing a generated sequence, ie: if columnSize is 3 and your array is between (inclusive) 6 and 9 (inclusive) rows will be [0, 1, 2], so you can use it to make calculations on SlicePipe.
You can check this simple DEMO to see it working.
If you're looking for a more generic solution, you could create a custom pipe:
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'chunk'
})
export class ChunkPipe implements PipeTransform {
transform(input: any, size: number): any {
if (!Array.isArray(input) || !size) {
return [];
}
const intSize: number = parseInt(size, 10);
return Array.from({ length: Math.ceil(input.length / intSize) }, (v, k) => input.slice(k * intSize, k * intSize + intSize));
}
}
Template:
<div *ngFor="let col of columns | chunk: columnSize">
<span *ngFor="let item of col">
{{ item.name }}
</span>
</div>
Also, don't forget to add the pipe to your declarations in NgModule:
#NgModule({
...
declarations: [
// Other declarations
ChunkPipe
],
...
})
DEMO

Resources