So i have two arrays:
1:
"movies":
{
"id": "123bb",
"category": "3345",
"content": "Sinister"
}
Second:
"categories":
{
"id": "3345",
"code": "Movie",
"name": "Horror"
},
I also have random movie:
TS:
loadData() {
this.PagesService.loadData().subscribe(response => {
console.log(response)
this.movies = response
this.movies[Math.floor(Math.random() * this.movies.length)];
this.randomValue = this.movies[Math.floor(Math.random() * this.movies.length)];
return this.randomValue
})
console.log(this.randomValue)
};
HTML:
<p>{{ this.randomValue.content }}</p>
So if I have something like this:
<p>{{ this.randomValue.category }}</p>
There is "3345" as category but I would like to have "name" from this second array but I am not sure how to do that
My recommendation:
store the data as observable
map the data in observable pipe (maybe use combineLatest to combine movies and categories data)
use the async pipe in template
use an own structural directive to pick the datas random element
Here is how it could look like...
The directive
import { Directive, Input, TemplateRef, ViewContainerRef } from '#angular/core';
interface RandomContext<T> {
appRandom: T[];
$implicit?: T;
}
#Directive({
standalone: true,
selector: '[appRandom]',
})
export class RandomDirective<T> {
private context: RandomContext<T> = {
appRandom: [],
$implicit: undefined,
};
#Input()
set appRandom(elements: T[]) {
this.context.appRandom = elements;
this.pickNewElement();
}
constructor(
private templateRef: TemplateRef<any>,
private viewContainer: ViewContainerRef
) {
this.viewContainer.createEmbeddedView(this.templateRef, this.context);
}
private pickNewElement(): void {
this.context.$implicit =
this.context.appRandom[
Math.floor(Math.random() * this.context.appRandom.length)
];
}
}
And your template:
<p *appRandom="movies$ | async; let randomMovie">
{{ randomMovie.category }}
</p>
And your component:
movies$: Observable<ExtendedMovie[]>;
ngOnInit() {
this.movies$ = combineLatest([
this.PagesService.loadData(),
this.CategoriesService.loadCategories(),
]).pipe(
map(([movies, categories]) => {
return /*map movies array to extend it with your required data from categories array*/;
}),
);
}
Related
I have a json file contains objects that have sub-objects like that :
[
{
"index": 0,
"name": "Médecine et spécialités médicales",
"specialties": [
{
"id": 0,
"name": "Médecine interne"
},
{
"id": 1,
"name": "Maladies infectieuses"
},
{
"id": 2,
"name": "Carcinologie médicale"
}
]
},
{
"index": 1,
"name": "Chirurgie et spécialités chirurgicales",
"specialties": [
{
"id": 0,
"name": "Chirurgie générale"
},
{
"id": 1,
"name": "Chirurgie carcinologique"
},
{
"id": 2,
"name": "Chirurgie thoracique"
}
]
}
]
I want to get the value of selected item every time I change the selected data
here is stackblitz that I'm working on, the value of selected item shown as number, how can I get the value as string name field?
According to your stackblitz you passing in value the index not the category name ,
I would do something like this:
app.html:
<ng-template [ngIf]="selectedCategory">
<label for="category">Category</label>
<select
name="category"
id="category"
class="form-control"
(change)="onCategorySelected($event.target.value)"
>
>
<option
*ngFor="let category of categories; index as i;"
[value]="category.name"
>
{{ category.name }}
</option>
</select>
<label for="specialty">Specialties</label>
<select
[(ngModel)]="selectedSpecialty"
name="specialty"
id="specialty"
class="form-control"
(change)="onSpecialtySelected($event.target.value)"
>
<option
*ngFor="let specialty of selectedCategory.specialties;"
[value]="specialty.name"
>
{{ specialty.name }}
</option>
</select>
</ng-template>
I had an ng template to avoid console errors and change the value of the first select tag by category.name and remove ngModel (don't know why this doesn't work sorry).
app.ts:
import { Component, OnInit } from "#angular/core";
import { HttpClient, HttpClientModule } from "#angular/common/http";
interface Speciality{
name: string
}
interface Category{
name: string,
specialities: Speciality[]
}
#Component({
selector: "my-app",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent {
categories: Category[];
selectedCategory: Category;
selectedSpecialty: Speciality;
constructor(private http: HttpClient) {
this.http.get<Category[]>("assets/json/data.json").subscribe(res => {
this.categories = res;
this.selectedCategory = this.categories[0]
});
}
onCategorySelected(categoryName: string) {
console.log(categoryName);
this.selectedCategory = this.categories.find(cat => cat.name === categoryName)
}
onSpecialtySelected(value: string) {
console.log(value);
this.selectedSpecialty.name = value;
}
}
I create interface to type your category and specialities, and in the subscribe method add a line to init your selectedCategory with the first object of your categories array.
here is the link to the stackblitz
you can use a getter. that's you create a function
categories: any=[]; //<--initizalize your variable with a empty array
get categoryName()
{
const cat=this.categories.find((_,index)=>index==this.selectedCategory)
return cat?cat.name:null
}
Then each time you write in .html
{{categoryName}} //you see the category selected
And you can forget all yours (change) in select. BTW, remove also the [selected]="i" in your .html. You are using [(ngModel)], so you needn't selected
your forked stackblitz
A second question about my project "Avatar database". I want to collect all tags from all avatars into one array. Of course the list should not contain duplicates.
For example, I have 3 avatars with the tag "red" and in the array "allTags" - "red" is displayed only once.
Data structure
data() {
return {
avatars: [
{
name: "Butterfly Blue",
tags: ["animal", "butterfly", "blue"]
},
{
name: "Butterfly Green",
tags: ["animal", "butterfly", "green"]
},
{
name: "Deer Shining",
tags: ["animal", "deer"]
}
]
}
}
I'm trying to get those tags using a computed property:
allTags() {
var result = [];
for (var avatar in this.avatars) {
for (var tag in avatar.tags) {
if (!tag in result) {
result += tag
}
}
}
return result
}
But... The only output I can see is: [] - an empty array.
I want the computed property allTags to return an array ([]) of all tags from all avatars.
Using the example data above {{ allTags }} should be:
[ "animal", "butterfly", "blue", "green", "deer" ]
You should use !result.includes(tag) instead of !tag in result, and result.push(tag) instead of result += tag:
allTags() {
var result = [];
for (let avatar of this.avatars) {
for (let tag of avatar.tags) {
if (!result.includes(tag)) {
result.push(tag)
}
}
}
return result
}
Also, I have replaced the use of for..in with for..of, this is the recommended construct.
See demo:
new Vue({
el: '#app',
data() {
return {
avatars: [{
name: "Butterfly Blue",
tags: ["animal", "butterfly", "blue"]
},
{
name: "Butterfly Green",
tags: ["animal", "butterfly", "green"]
},
{
name: "Deer Shining",
tags: ["animal", "deer"]
}
]
}
},
computed: {
allTags() {
var result = [];
for (let avatar of this.avatars) {
for (let tag of avatar.tags) {
if (!result.includes(tag)) {
result.push(tag)
}
}
}
return result
}
}
})
<script src="https://unpkg.com/vue#2.6.11/dist/vue.min.js"></script>
<div id="app">
<h4>Avatars:</h4>
<p>{{ avatars }}</p>
<hr>
<h4>allTags:</h4>
<p>{{ allTags }}</p>
</div>
I have an somewhat complex object that includes nested objects as follows
"data": {
"John": {
"title": "John",
"value": "john"
},
"Ben": {
"title": "Ben",
"value": "ben"
},
"Workers": {
"title": "Working Data",
"startData": {
"title": "Start Date",
"value": "Mon, 27 Nov 2017 16:57:56 GMT"
},
"isPermanant": {
"title": "Is Permanant",
"value": "True"
}
},
"Family": {
"title": "Family Data",
"familyMembers": {
"title": "Family Members",
"value": "4"
},
"pets": {
"title": "Pets",
"value": "2"
}
},
"education": {
"title": "Education Details",
"degree": {
"title": "Degree",
"value": "Yes"
},
"graduated": {
"title": "Graduated Year",
"value": "2015"
}
}
Expected outcome is something like this
<p>John <span>john</span><p>
<p>Ben <span>ben</span><p>
<p>Working Data<p>
<p>Start Date <span>Mon, 27 Nov 2017 16:57:56 GMT</span><p>
<p>Is Permanant <span>True</span><p>
<p>Family Data<p>
<p>Family Members <span>4</span><p>
<p>Pets <span>2</span><p>
<p>Education Details<p>
<p>Degree <span>Yes</span><p>
<p>Graduated Year<span>2015</span><p>
I created a component that using a recursive way of displaying data
import { Component, Input, OnInit } from '#angular/core'
#Component({
selector: 'timeline-data',
template: 'timeline-data.html'
})
export class TimelineDataComponent implements OnInit {
#Input('data') data: any[];
ngOnInit() {}
}
timeline-data.html as follows
<ng-container *ngIf="data.length">
<ng-container *ngFor="let item of data">
<ng-container *ngIf="item.value">
<p>{{ item.title }} <span>{{ item.value }}</span></p>
</ng-container>
<ng-container *ngIf="!item.value">
<timeline-data [data]="[item]"></timeline-data>
</ng-container>
<ng-container>
<ng-container>
But when I run this angular give me a RangeError: Maximum call stack size exceeded
What am I doing wrong here? How should I show this? Thanks in advance.
Based on your sample html, it will be difficult to do recursive rendering and not end up with nested <p> tags.
I took a slightly different approach and used an unordered list with an extra conditional to eliminate the empty li from the first iteration. If someone has a better way to do this, I'm all ears :)
I broke the rendering up into two main components:
tree.component.ts
import { Component, Input } from '#angular/core';
#Component({
selector: 'tree-component, [tree-component]',
template: `
// omit the <li> wrapper for the "parent" iteration
<ng-container *ngIf="parent">
<tree-item [data]="data"></tree-item>
</ng-container>
<li *ngIf="!parent">
<tree-item [data]="data"></tree-item>
</li>
`
})
export class TreeComponent {
#Input()
data: object;
#Input()
parent = false;
}
tree-item.component.ts
import { Component, Input } from '#angular/core';
#Component({
selector: 'tree-item',
template: `
// iterate over the keys for each data item
<ng-container *ngFor="let key of data | keys; let i = index;">
// if the value is not an object, output the values
// I assumed there would only be two values to wrap the second
// value in a <span> according to your sample
<ng-container *ngIf="!isObject(data[key])">
<ng-container *ngIf="i === 0">{{ data[key] }}</ng-container>
<span *ngIf="i === 1">{{ data[key] }}</span>
</ng-container>
// if the value is an object, recursively render the tree-component
<ul tree-component *ngIf="isObject(data[key])" [data]="data[key]"></ul>
</ng-container>
`
})
export class TreeItemComponent {
#Input()
data: object;
isObject(value: any): boolean {
return value instanceof Object && value.constructor === Object;
}
}
and a pipe utility to get the keys for each object, keys.pipe.ts:
import { PipeTransform, Pipe } from '#angular/core';
#Pipe({
name: 'keys'
})
export class KeysPipe implements PipeTransform {
transform(value, args: string[]): any {
return Object.keys(value);
}
}
Give your data, and an implementation of:
<tree-component [data]="data" [parent]="true"></tree-component>
You end up with the result of this Plunkr: https://plnkr.co/edit/9DiCymkBDUNJSCFObV2G
I'm working with angular2 ( version 5).
I make an http request an get back json.
I know how to access and use value but not the array.
and I don't find how to extract the two array inside element.
here my json:
{ "ImpiantiProva": [
{
"nomeImpianto":"MFL1",
"descrImpianto":"Multifilo 1",
"posizione":"Place1",
"dati_status": "true",
"unita_misura": "m/s",
"vel_attuale": 11.5,
"vel": [24.5,13.6,34.6,12.1],
"orario": ["17.05","17.06","17.07","17.08"]
},
{
"nomeImpianto":"MFL2",
"descrImpianto":"Multifilo 2",
"posizione":"Place2",
"dati_status": "true",
"unita_misura": "m/s",
"vel_attuale": 12.5,
"vel": [24.5,13.6,34.6,12.1],
"orario": ["17.05","17.06","17.07","17.08"]
}
]
}
In the data.service.ts I have the http request and it store values on :
stream$: Observable<ImpiantoModel[]>;
here my definition of the model:
#impianto.model
export class ImpiantoModel {
nomeImpianto: string;
descrImpianto: string;
posizione: string;
dati_status: string;
unita_misura: string;
vel_attuale: number;
vel: VelocitaModel[];
orario: OrariModel[];
}
#orari.model.ts
export class OrariModel {
orario: string;
}
#velocita.model.ts
export class VelocitaModel{
vel : number;
}
is it the right why to define my object?
How can I use the array "vel" and "orario"?
How can I print (access) the array "vel" of machine with "nomeImpianto" = "MFL1" ?
and how can I copy the array "vel" on new array?
thank you very much!
Here is what I understood of what you want to do : get the item in your json resp and put it in your object , so the best way is to create a static method directly when you get the json response, before returning the value create this adapter adaptImpiant(jsonObj) which will do something like :
adaptImpiant(jsonObj) {
let impiantTab = [];
jsonObj.ImpiantiProva.forEach((item) => {
let impiantoModel = {};
// impiantoModel = item if the model (below) match the item;
// if not manually set all your var like your velocita
let velocita = [] // is an array or an object with an array
// if class velocita = {}
velocita = item.vel.slice(0);
// if class velocita.valuesTab = item.vel.slice(0);
impiantoModel.velocita = velocita;
impiantTab.push(impiantoModel);
}
}
Your model seems wrong in this case, because you already use a ImpiantoModel array, so just create a class with whatever you want in :
#impianto.model
export class ImpiantoModel {
nomeImpianto: string;
descrImpianto: string;
posizione: string;
dati_status: string;
unita_misura: string;
vel_attuale: number;
vel: VelocitaModel // or simply [];
orario: OrariModel // or simply [];
}
I'm not sure I understand you, but I'll try.
is it the right why to define my object?
It should be:
export class ImpiantoModel {
nomeImpianto: string;
descrImpianto: string;
posizione: string;
dati_status: string;
unita_misura: string;
vel_attuale: number;
vel: Array<string>;
orario: Array<string>;
}
(But I have to confess, I don't know why model and not an interface)
How can I use the array "vel" and "orario"?
What do you mean?
How can I print (access) the array "vel" of machine with
"nomeImpianto" = "MFL1"
const thisContainsTheDataFromVel = whereYourDataIsStored['ImpiantiProva'].find((item) => { item['nomeImpianto'] === 'MFL1'})['vel'];
and how can I copy the array "vel" on new array?
UPDATE after reading your comment under this answer:
I took code from your example and added what you are missing. I made it so it can be more reusable (it can be enhanced even more, but I hope you understand the code and do what you need).
copyArray(data, targetValue) {
const mfl1Data = data.find((item) => item['nomeImpianto'] === targetValue);
if (mfl1Data) {
return mfl1Data.vel;
}
return [];
}
getdata2() {
this.http.get<ImpiantoModel[]>(this.myUrl)
.subscribe(
data => {
this.variableToStoreIn = this.copyArray(data, 'MFL1');
data.forEach(item => {
this.sub$.next(item);
});
});
return this.sub$;
}
CopyArray finds the data and returns it. If you don't want it like this, but just set a value of some property to the value of vel array then you can change it to:
copyArray(data) {
const mfl1Data = data.find((item) => item['nomeImpianto'] === targetValue);
if (mfl1Data) {
this.yourVariable = mfl1Data.vel;
}
}
If this answer is sufficient, please consider to mark it as the best answer, thank you.
According to your model classes, your JSON is wrong. You should have something like this:
{ "ImpiantiProva": [
{
"nomeImpianto":"MFL1",
"descrImpianto":"Multifilo 1",
"posizione":"Place1",
"dati_status": "true",
"unita_misura": "m/s",
"vel_attuale": 11.5,
"vel": [
{
"vel": 24.5
},
{
"vel": 13.6
}
...
],
"orario": [
{
"orario": "17.05"
},
{
"orario": "17.06"
}
...
]
}
]
}
Your model expects ImpiantoModel.vel and ImpiantoModel.orario to be arrays of objects. In your JSON response one is an array of numbers and the other of strings.
An if you want to use it in an HTML template, considering that you have a class attribute in your .ts file like this:
private impiantoModels: ImpiantoModel[];
You could do something like this inside your .html template:
<div *ngFor="let impModel of impiantoModels">
...
<div *ngFor="let v of impModel.vel">
<p>{{v.vel}}</p>
</div>
<div *ngFor="let o of impModel.orario">
<p>{{o.orario}}</p>
</div>
</div>
Component:
import { Component, OnInit } from '#angular/core';
import * as _ from "lodash";
import { AF } from '../angularfire.service';
#Component({
selector: 'app-record-chart',
templateUrl: './record-chart.component.html',
styleUrls: ['./record-chart.component.less']
})
export class RecordChartComponent implements OnInit {
currentUser = [];
userRecords = [];
topRecords = [];
topRecordLabels = [];
movements = [
"Back Squat",
"Bench Press",
"Clean",
"Clean & Jerk",
"Deadlift",
"Front Squat",
"Jerk",
"Power Clean",
"Power Snatch",
"Push Press",
"Snatch",
"Strict Press"
];
public barChartOptions:any = {
scaleShowVerticalLines: false,
responsive: true
};
public barChartLabels = this.topRecords[0];
public barChartType:string = 'bar';
public barChartLegend:boolean = true;
public barChartData:any[] = [
{data: [65, 59, 80, 81, 56, 55, 40], label: 'Series A'},
{data: [28, 48, 40, 19, 86, 27, 90], label: 'Series B'}
];
// events
public chartClicked(e:any):void {
console.log(e);
}
public chartHovered(e:any):void {
console.log(e);
}
constructor(private afService: AF) {
// Get current user details.
afService.getCurrentUserInfo().then(currentUserDetails => {
this.currentUser.push(currentUserDetails);
}).then(() => {
// Populate lifts array
for(let movement of this.movements) {
this.afService.getRecords(movement, this.currentUser[0].userID).subscribe((data) => {
var sortedArray = _.orderBy(data, ['weight']);
var sortedArray2 = _.uniqBy(sortedArray,'weight');
// console.log(sortedArray2);
this.userRecords.push(sortedArray);
var newRecords = sortedArray
.filter(function(record) {
return sortedArray.find(function(innerRecord) {
return innerRecord.name === record.name && innerRecord.weight > record.weight; }) === undefined;
});
for (let record of newRecords) {
this.topRecords.push(record);
}
});
}
}).then(() => {
// console.log(this.topRecords);
for (item in this.topRecords) {
this.topRecordLabels.push(item.movement);
}
console.log(this.topRecords);
})
}
ngOnInit() {
}
}
this.topRecords Array output:
How do I iterate through every object in this array and push all of the movement values into their own array? I thought I would be able to access them individually with this.topRecords[0] in a for loop, but it always returns a length of 0.
This is what I thought would work:
for (item in this.topRecords) {
this.topRecordLabels.push(item.movement);
}
But it makes 0 iterations. I'm stuck on figuring out how to access and cycle through the objects of this array.
You can iterate through the array with the map operator and then return another array for your field like this:
this.topRecordLabels = this.topRecords.map((item)=> item.movement);
Example usage: https://jsfiddle.net/echonax/n0e0qxng/
Please remove this line first :
public barChartLabels = this.topRecords[0];
This doesn't make nay sense.
You need to read the differnce b/w for in and for of
Replace your code with :
for (let item of this.topRecords) {
this.topRecordLabels.push(item.movement);
}
For more information , please checkout the link :
What is the difference between ( for... in ) and ( for... of ) in javascript?
#echonax figured this out:
this.topRecordLabels = this.topRecords.map((item)=> item.movement)