How to get Selected item value from a selectBox data - arrays

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

Related

Data from 2 arrays

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*/;
}),
);
}

How to count elements in an array depending on value

I have an array of Answer Entities that has an structure that includes an array of Vthumbs.
Entities that can either have a boolean vthumbdown/vthumbup property.
And the problem is this: How can I count the number of elements in the Vthumbs array that have a true "vthumbdown" (and the number of elements that have false "vthumbup")?
{
"accepted": true,
"creationDate": "2018-12-21T15:42:34.497Z",
"id": 0,
"urlvanser": "string",
"userId": 0,
"vquestionId": 0,
"vthumbs": [
{
"creationDate": "2018-12-21T15:42:34.497Z",
"id": 0,
"userId": 0,
"vanswerId": 0,
"vquestionId": 0,
"vthumbdown": true,
"vthumbup": true
}
]
}
]
I can see the total number of items with {{vanswer.vthumbs.length}} but I can not think how to get the number of Ups and Downs
Here is a great reference on javascript arrays and their available methods on MDN. In this instance you would most likely want to utilize the filter method as follows.
vanswer.vthumbs.filter(vthumb => vthumb.vthumbdown).length;
vanswer.vthumbs.filter(vthumb => vthumb.vthumbup).length;
You can get this simply by filtering the array:
const ups = vanswer.vthumbs.filter(v => v.vthumbup).length;
const downs= vanswer.vthumbs.filter(v => v.vthumbdown).length;
Then use it in your template:
Ups: {{ ups }}
Downs: {{ downs }}
Edit: according to comments, you wanna count these things in the template. For that, you can build a simple pipe:
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'countThumbs'
})
export class CounterPipe implements PipeTransform {
transform(obj: any, upsOrDowns = 'ups'): any {
if (!obj.vthumbs || !Array.isArray(obj.vthumbs)) {
return null;
}
if (upsOrDowns === 'ups') {
return obj.vthumbs.filter(item => item.vthumbup).length;
} else {
return obj.vthumbs.filter(item => item.vthumbdown).length
}
}
}
You then use it in the template like this:
Thumbups: {{ vanswer | countThumbs:'ups' }}
Thumbdowns: {{ vanswer | countThumbs:'downs' }}
Here's a more generic approach at Stackblitz.

Rendering nested objects using a recursive method

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

Filter array of objects in Angular4 without pipe

I have an array of links that each element is an object that contain few strings - a link, description and category. I have different components that display the links, and I want in each component to display only the links of its category.
So I want to filter the array by the category.
I have a mock-up array with all the links.
I try to filter the array of objects without a pipe. The reason why: https://angular.io/guide/pipes#appendix-no-filterpipe-or-orderbypipe
Apparently the Angular team suggests to do the filtering in the component level and not using a pipe:
"The Angular team and many experienced Angular developers strongly recommend moving filtering and sorting logic into the component itself."
So here's my component:
#Component({
selector: 'startups',
templateUrl: './startups.component.html'
})
export class StartupsComponent implements OnInit {
constructor(private appLinksService: DigitalCoinHubService) { }
title = 'Startups';
links: DCHlinks[]; // create a new array in type of DCHlinks to get the data
startupsLinks: DCHlinks [] = []; // will build the startsups links only
getLinks(): void {
this.links = this.appLinksService.getLinks(); // gets the array with the data from the service
for (let i in this.links)
{
if (this.links[i].dchCategory == 'startups' )
{
this.startupsLinks[i].push(this.links[i]);
}
}
}
ngOnInit() {
this.getLinks();
}
}
So first I get the big array from the service:
this.links = this.appLinksService.getLinks();
And then I try to build a new array that will contain only the relevant links. The filter is by the category. But then when I try to build the new array by push the elements which their category matches - it gives me error:
Property 'push' does not exist on type 'DCHlinks'.
DCHlinks is the object - this is the class:
export class DCHlinks {
dchLink: string;
dchLinkTitle: string;
dchLinkDescription: string;
dchCategory: string;
}
Any idea how to do this simple filter? (and w/o pipe - see above reason why..)
Thanks!
You need to intialize the array as you did for startupsLinks
links: DCHlinks[] = [];
Or you can simply use array.filter to get the relavant data
this.startupsLinks = this.links.filter(t=>t.dchCategory == 'startups');
I have the same issue but I resolve in different way. I have the Array of object
and want to use filtering using of html select option .which I given the data that filter the array of object.
`$`
heroes = [{
name: "shubh",
franchise: "eng"
},
{
name: "Ironman",
franchise: "Marvel"
},
{
name: "Batman",
franchise: "DC"
},
{
name: "Batman",
franchise: "DC"
},
{
name: "Batman",
franchise: "DC"
},
{
name: "satman",
franchise: "mc"
},
{
name: "monmam",
franchise: "DC"
},
{
name: "loolman",
franchise: "DC"
},
{
name: "Thor",
franchise: "Marvel"
},
{
name: "monmam",
franchise: "DC"
},
{
name: "monmam",
franchise: "DC"
},
{
name: "monmam",
franchise: "DC"
},
{
name: "Thor",
franchise: "Marvel"
},
{
name: "Superman",
franchise: "DC"
},
{
name: "Superman",
franchise: "DC"
},
{
name: "Superman",
franchise: "DC"
},
{
name: "Superman",
franchise: "DC"
},
];
//this is the most imp part in the filter section .I face lot of problem this is not working if this line not write .The filter method works old one time.
newarr = this.heroes;
//So I create new array which store the old array value every time. and we replace the value in the filter function.
filter(heroes: string) {
console.log(heroes);
this.heroes = this.newarr; // replace the value and store old value
let heroesnew = this.heroes.filter((data => data.name == heroes));
this.heroes = heroesnew;
console.log(this.heroes);
}
<!––"#sel" is the template variable which gives the data of value property in option field ––>
<select #sel class="custom-select custom-select-sm" (change)="filter(sel.value)">
<option>All</option>
<option value="Batman">Batman</option>
<option value="Superman">Superman</option>
<option value="satman">satman</option>
<option value="monmam">monmam</option>
<option value="Thor">thor</option>
</select>
<!–– this is the table that I want to filter––>
<div>
<table class="table table-hover ">
<thead class="thead-dark">
<tr>
<th>#</th>
<th>name</th>
<th>franchise</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let incidence of heroes">
<td>{{ incidence.name}}</td>
<td> {{ incidence.franchise }}</td>
</tr>
</tbody>
</table>
</div>
$
use the code for angular 2+,to 8 It working fine ..

Angular how to have multiple selected

I have this array of objects. that holds somethings like this.
[
{
id: 1,
name: "Extra Cheese"
},
{
id: 2,
name: "No Cheese"
}
]
im iterating thru the array here
<select ng-model="item.modifiers" multiple chosen class="chosen-select" tabindex="4" ng-options="modifier._id as modifier.name for modifier in modifiers"></select>
The thing item.modifiers model that has an array of this 2 id
[
1,2
]
I want the multi select to auto selected the two ids that are in the item.model
I want the final result to look something like this
Your code is pretty much working already, maybe some of the variables are not assigned correctly (eg. id instead of _id)
angular.module('test', []).controller('Test', Test);
function Test($scope) {
$scope.modifiers = [
{
id: 1,
name: "Extra Cheese"
},
{
id: 2,
name: "No Cheese"
}
]
$scope.item = {};
// add this for pre-selecting both options
$scope.item.modifiers = [1,2];
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script>
<div ng-app='test' ng-controller='Test'>
<select ng-model="item.modifiers" multiple chosen class="chosen-select" tabindex="4" ng-options="modifier.id as modifier.name for modifier in modifiers"></select>
</div>
If I understand the question correctly, you're wanting to pre-select the two options.
To do this you will need to set your ng-model to point to the actual objects you are iterating over.
You will also need to change your ng-options to ng-options="modifier as modifier.name for modifier in modifiers" rather than just iterating over the ids.
Here's the relevant documentation under Complex Models (objects or collections)
https://docs.angularjs.org/api/ng/directive/ngOptions
Something like this should work:
HTML:
<select ng-model="$ctrl.item.modifiers"
ng-options="modifier as modifier.name for modifier in $ctrl.modifiers"
multiple chosen class="chosen-select" tabindex="4" >
</select>
JS:
app.controller("my-controller", function() {
var $ctrl = this;
$ctrl.modifiers = [{
id: 1,
name: "Extra Cheese"
}, {
id: 2,
name: "No Cheese"
}];
$ctrl.item = {
modifiers: []
}
$ctrl.$onInit = function() {
const id1 = 1;
const id2 = 2;
for (const modifier of $ctrl.modifiers) {
if (modifier.id === id1 || modifier.id === id2) {
$ctrl.item.modifiers.push(modifier);
}
}
}
}
Here's a pen showing the result:
http://codepen.io/Lahikainen/pen/WooaEx
I hope this helps...

Resources