I have an array with 4 items, and 4 buttons on a dashboard. I want to assign item1 with button1, and item2 with button2 etc. Right now it displays 4 buttons for each "hero" for a total of 16 buttons. I tried {{hero.name[2]}} and similar things but that just grabs letters and not the actual array items. I would appreciate any help.
dashboard.component.html
<h3>Calibrations</h3>
<div class="grid grid-pad">
<a *ngFor="let hero of heroes" [routerLink]="['/detail', hero.id]">
<button style ="min-height: 70px" (click)="gotoClick(1)">{{hero.name}}</button>
<button style ="min-height: 70px" (click)="gotoClick(2)">{{hero.name}}</button>
<button style ="min-height: 70px" (click)="gotoClick(3)">{{hero.name}}</button>
<button style ="min-height: 70px" (click)="gotoClick(4)">{{hero.name}}</button>
</a>
</div>
dashboard.component.ts
import { Component, OnInit } from '#angular/core';
import { Hero } from '../hero.class';
import { HeroService } from '../hero.service';
import { StepService } from '../step.service';
#Component({
moduleId: module.id,
selector: 'app-dashboard',
templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.css']
})
export class DashboardComponent implements OnInit {
heroes: Hero[] = [];
constructor(private heroService: HeroService, private _stepService: StepService, private _teststepService: StepService) { }
ngOnInit() {
this.heroService.getHeroes().subscribe(heroes => this.heroes = heroes);
}
private test: number;
gotoClick(value: number){
this._stepService.setTest(value);
this._teststepService.apiURL();
}
}
hero.service.ts
import { Injectable } from '#angular/core';
import { Headers, Http, Response } from '#angular/http';
import { Hero } from './hero.class';
import { Observable } from "rxjs/Rx";
#Injectable()
export class HeroService {
private headers = new Headers({'Content-Type': 'application/json'});
private heroesUrl = 'api/heroes'; // URL to web api
constructor(private http: Http){ }
getHeroes(): Observable<Hero[]> {
return this.http.get(this.heroesUrl)
.map(response => response.json().data as Hero[]);
}
getHero(id: number): Observable<Hero> {
const url = `${this.heroesUrl}/${id}`;
return this.http.get(url)
.map(response => response.json().data as Hero);
}
}
You can use the built-in index property of *ngFor:
<div class="grid grid-pad">
<a *ngFor="let hero of heroes; let i = index;" [routerLink]="['/detail', hero.id]">
<button style ="min-height: 70px" (click)="gotoClick(i)">{{hero?.name}</button>
</a>
</div>
Documentation: https://angular.io/docs/ts/latest/api/common/index/NgFor-directive.html
Related
Currently i'm developing a web app which shows the Golden State Warriors (Basketball Team) Roster.
My main goal consists in filtering all players by position.
Positions are numbers ordered from 1 to 5.
My question would be: which could be the way to transform my http get request in order to obtain the point-guards (position === 1) ??
roster-data.service.ts
import { Injectable } from '#angular/core';
import { Player } from "../models/player";
import { environment } from "../../environments/environment";
import {HttpClient, HttpErrorResponse} from '#angular/common/http';
import {Observable, throwError} from 'rxjs';
import {catchError, map, filter } from 'rxjs/operators';
#Injectable({
providedIn: 'root'
})
export class RosterDataService {
private readonly devURL = environment.url;
constructor(private http: HttpClient) {
}
jSON_Server_ReadPointGuards():Observable<Player[]> {
return this.http.get<Player[]>(`${this.devURL}/roster_GoldenStateWarriors`).pipe(
map(players => players.filter((player:Player) => player.position === 1)
),
catchError(this.handleError)
);
}
And then my interface
player.ts
export interface Player {
fullName: string,
shirtNumber: string,
position: number,
height: string,
weight: number,
points: number,
rebounds: number,
assists: number,
blocks: number,
steals: number,
imageFile: string,
imageDescrip: string
}
players.component.ts
import { Component, OnInit, isDevMode } from '#angular/core';
import { RosterDataService } from "../../services/roster-data.service";
import { Player } from "../../models/player";
import { imgRoute } from "../../services/img-route.service";
#Component({
selector: 'app-players',
templateUrl: './players.component.html',
styleUrls: ['./players.component.scss'],
providers: [RosterDataService]
})
export class PlayersComponent implements OnInit {
public imageLocation:string;
public bases!: Player[];
public escoltas!: Player[];
public aleros!: Player[];
public alaPivots!: Player[];
public pivots!: Player[];
public getPlayers: Player[] = [];
constructor(private _rosterDataService: RosterDataService) {
this.imageLocation = imgRoute.path;
}
ngOnInit(): void {
this.dev_SuscribeAllPointGuards();
// this.dev_SuscribeAllShootingGuards();
// this.dev_SuscribeAllSmallForwards();
// this.dev_SuscribeAllPowerForwards();
// this.dev_SuscribeAllCenters();
}
dev_SuscribeAllPointGuards(){
this._rosterDataService.jSON_Server_ReadPointGuards().subscribe(
pointguards => {
this.bases = pointguards;
}
);
}
}
players.component.html
<section>
<article>
<h4 id="pointGuard">{{ "players.firstTitle" | translate }}</h4>
<div class="gallery">
<div [routerLink]="'/'+(base.fullName | removespaces)+'/'+base.shirtNumber" *ngFor="let base of bases;">
<figure><img src="{{imageLocation + base.imageFile}}" alt="{{base.imageDescrip}}" title="{{base.imageDescrip}}"></figure>
<section>
<h5>{{base.fullName}}</h5>
<h6>{{base.shirtNumber}}</h6>
<h6>{{base.points}}</h6>
<h6>{{base.rebounds}}</h6>
<h6>{{base.assists}}</h6>
<h6>{{base.blocks}}</h6>
<h6>{{base.steals}}</h6>
</section>
</div>
</div>
</article>
</section>
I am trying to call a list of actors from movies; in the DB I made, they all have commas at the end of each string. When the array is called, the content displays with 2 commas after each other and I am wondering how I can get rid of that. I have tried to use .join but I don't know how to implement it into the HTML (I am new at Angular).
Here is the HTML and .ts files:
import { Component, OnInit } from '#angular/core';
import { Router } from '#angular/router';
import { FetchApiDataService } from '../fetch-api-data.service'
import { MatDialog } from '#angular/material/dialog';
import { GenreComponent } from '../genre/genre.component';
import { DirectorComponent } from '../director/director.component';
#Component({
selector: 'app-movie-card',
templateUrl: './movie-card.component.html',
styleUrls: ['./movie-card.component.css']
})
export class MovieCardComponent implements OnInit {
movies: any[] = [];
actors: any[] = [];
constructor(
public dialog: MatDialog,
public fetchApiData: FetchApiDataService,
public router:Router,
) { }
ngOnInit(): void {
this.getMovies();
}
removeCommas(): void {
this.actors.join(' ');
}
getMovies(): void {
this.fetchApiData.getAllMovies().subscribe((response: any) => {
this.movies = response;
console.log(this.movies);
return this.movies;
});
}
openGenreDialog(genreName: string): void {
this.dialog.open(GenreComponent, {
width: '280px',
data: {
genreName: genreName
}
});
}
openDirectorDialog(directorName: string): void {
this.dialog.open(DirectorComponent, {
width: '280px',
data: {
directorName: directorName
}
});
}
}
<div style="display: flex;">
<mat-card *ngFor="let movie of movies;" style="flex: 1 1 auto;">
<mat-card-header>
<mat-card-title>{{movie.Title}}</mat-card-title>
<mat-card-subtitle>Starring: {{movie.Actors}}</mat-card-subtitle>
</mat-card-header>
<img src={{movie.ImagePath}} alt= {{movie.Title}} />
<mat-card-actions>
<button
mat-button
color="primary"
(click)="openGenreDialog(movie.Genre.Name)"
>
Genre
</button>
<button
mat-button
color="primary"
(click)="openDirectorDialog(movie.Director.Name)"
>
Director
</button>
<button
mat-button
color="primary"
>
Synopsis
</button>
<mat-icon>favorite_border</mat-icon>
</mat-card-actions>
</mat-card>
</div>
You can run the map pipe and replace method in your array.
getMovies(): void {
this.fetchApiData.getAllMovies().pipe(
map((actor) => actor.replace(',', ''))).
subscribe((response: any) => {
this.movies = response;
console.log(this.movies);
return this.movies;
});
}
First of all, I will advice you to not use 'any' everywhere. It removes type checking and that can lead to issues and bugs in future.
As the returned object will be an Observable of type any[] (or Movies[] if you create a movie object with a string property named actor), you can do something like this. It will return an array of actors. For replace function, you will have to use a regexp expression to select all the commas in the value -
getMovies() {
this.fetchApiData
.getAllMovies()
.subscribe((res: any[]) => {
this.movies = res;
this.actors = res.map((movie: any) => movie.actor.replace(/,/g, ''));
console.log(this.actors);
});
}
I'm just learning angular and trying to make a to-do-list app. I'm trying to send array object data to firestore. I have an array object input like this:
[
{
"isChecked": true
"title": "Todo 1",
},
{
"isChecked": true,
"title": "Todo 2"
}
]
I want to enter that into the input field. And here is my input field:
<form action="" #importForm="ngForm (ngSubmit)="importJson(importForm, $event)">
<div class="form-group">
<textarea ngModel name="importjson" #importjson="ngModel" class="form-control" id="exampleFormControlTextarea1" rows="10" required></textarea>
</div>
<button type="submit" class="btn btn-secondary" >Ok</button>
</form>
And this is my app component :
import { Component, ViewChild, OnInit, ElementRef } from '#angular/core';
import { FormsModule } from '#angular/forms';
import { FormGroup } from '#angular/forms';
import { TodolistService } from '../../services/todolist.service';
import { Todolist } from '../../model/todolist.model';
export class TodolistComponent implements OnInit {
importjson: Todolist={};
constructor(private todolistService: TodolistService) { }
#ViewChild('editTitle', {static: false}) input: ElementRef;
ngOnInit(): void{
this.todolistService.getToDoList().subscribe(items => {
this.todos = items;
})
}
importJson(importForm: FormGroup, submit){
console.log(importForm.value.importjson);
this.todolistService.addImport(importForm.value.importjson);
this.importState = false;
}
}
And here is my app service:
import { Injectable } from '#angular/core';
import { AngularFireDatabase, AngularFireList } from '#angular/fire/database' ;
import { AngularFirestore, AngularFirestoreCollection, AngularFirestoreDocument } from '#angular/fire/firestore';
import { Observable } from 'rxjs';
import { Todolist } from '../model/todolist.model';
#Injectable({
providedIn: 'root'
})
export class TodolistService {
itemsCollection: AngularFirestoreCollection<Todolist>;
items: Observable<Todolist[]>;
itemDoc: AngularFirestoreDocument<Todolist>;
constructor(private firebasedb: AngularFireDatabase, public firestore: AngularFirestore) {
this.itemsCollection = this.firestore.collection('titles');
this.items = this.itemsCollection.snapshotChanges().pipe(
map(actions => actions.map(a => {
const data = a.payload.doc.data() as Todolist;
data.id = a.payload.doc.id;
return data;
}))
);
}
addImport(item: Todolist) {
this.itemsCollection.add(item);
}
How can I do that?
I am trying to do a simple import but I am getting a massive stack trace issue.
I have tried searching everywhere for issues related to this but to me, the stack trace doesn't provide much information.
EDIT: I have tried setting it a variable that isn't fetched from Firebase and it works fine. I guess the question now is how do I handle this information from Firebase so that it loads when it is ready.
Here are the relevant files:
main.ts:
import { bootstrap } from '#angular/platform-browser-dynamic';
import {AppComponent} from './app.component';
import { HTTP_PROVIDERS } from '#angular/http';
bootstrap(AppComponent, [HTTP_PROVIDERS]);
player.services.ts:
import { Injectable } from '#angular/core';
import {Player} from "../classes/player";
#Injectable()
export class PlayerService {
player: Player;
getPlayer()
{
return Promise.resolve(this.player);
}
createPlayer(uid: string, name: string, firebaseRef: Firebase)
{
this.player = {
'uid': uid,
'name': name,
'level': 1,
'maxHealth': 100,
'health': 100,
'maxEnergy': 50,
'energy': 50,
'fun': 1,
'skill': 1,
'knowledge': 1
}
firebaseRef.child('players').child(uid).set(this.player);
}
setPlayer(player: Player)
{
this.player = player;
}
}
app.component.ts
import { Component, OnInit } from '#angular/core'
import { PlayerDetailComponent } from './components/player-detail.component';
import {PlayerService} from "./services/player.service";
import {FirebaseEventPipe} from "./firebasepipe";
import {Player} from "./classes/player";
#Component({
selector: "my-app",
templateUrl: 'app/views/app.component.html',
directives: [PlayerDetailComponent],
providers: [PlayerService],
pipes: [FirebaseEventPipe]
})
export class AppComponent implements OnInit{
title = "title";
authData: any;
private firebaseUrl: string;
private firebaseRef: Firebase;
private loggedIn = false;
player: Player;
constructor(private playerService: PlayerService) {
this.firebaseUrl = "https://!.firebaseio.com/";
this.firebaseRef = new Firebase(this.firebaseUrl);
this.firebaseRef.onAuth((user) => {
if (user) {
this.authData = user;
this.loggedIn = true;
}
});
}
getPlayer() {
this.firebaseRef.once("value", (dataSnapshot) => {
if (dataSnapshot.child('players').child(this.authData.uid).exists()) {
this.firebaseRef.child('players').child(this.authData.uid).once("value", (data) => {
this.player = data.val();
this.playerService.setPlayer(this.player);
console.log(this.player);
});
} else {
this.playerService.createPlayer(this.authData.uid, this.getName(this.authData), this.firebaseRef);
this.playerService.getPlayer().then(player => this.player);
console.log(this.player);
}
});
}
ngOnInit() {
this.getPlayer();
}
authWithGithub() {
this.firebaseRef.authWithOAuthPopup("github", (error) =>
{
if (error) {
console.log(error);
}
});
}
authWithGoogle() {
this.firebaseRef.authWithOAuthPopup("google",(error) =>
{
if (error) {
console.log(error);
}
});
}
getName(authData: any) {
switch (authData.provider) {
case 'github':
return authData.github.displayName;
case 'google':
return authData.google.displayName;
}
}
}
player-detail.component.ts
import { Component, Input, OnInit } from '#angular/core';
import { Player } from '../classes/player';
#Component({
selector: "player-details",
templateUrl: "app/views/player-detail.component.html",
styleUrls: ['app/style/player-detail.component.css'],
})
export class PlayerDetailComponent implements OnInit{
#Input() player: Player;
ngOnInit() { console.log(this.player)}
}
app.component.html
<nav class="navbar navbar-default">
<div class="container">
<ul class="nav navbar-nav">
<li class="navbar-link">Home</li>
</ul>
</div>
</nav>
<div class="jumbotron" [hidden]="loggedIn">
<div class="container">
<h1>Angular Attack Project</h1>
<p>This is a project for the Angular Attack 2016 hackathon. This is a small project where set goals
in order to gain experience as a player and person. In order to begin, please register with on of the following services</p>
<button class="btn btn-social btn-github" (click)="authWithGithub()"><span class="fa fa-github"></span>Sign Up With Github </button>
<button class="btn btn-social btn-google" (click)="authWithGoogle()"><span class="fa fa-google"></span>Sign Up With Github </button>
</div>
</div>
<player-details [player]="player" [hidden]="!loggedIn"></player-details>
player-detail.component.html
<div id="player" class="panel panel-default">
<div id="player-stats" class="panel-body">
<img id="player-image" class="img-responsive" src="../app/assets/images/boy.png"/>
<div class="health-bars">
<div class="health-bar">HEALTH:<br/><progress value="{{ player.health }}" max="{{ player.maxHealth }}"></progress></div>
<div class="energy-bar">ENERGY:<br/><progress value="{{ player.energy }}" max="{{ player.maxEnergy }}"></progress></div>
<div class="player-attributes"><span class="fa fa-futbol-o player-attr fun">: {{ player.fun }} </span><span class="fa fa-cubes player-attr skill">: {{ player.skill }}</span> <span class="fa fa-graduation-cap player-attr knowledge">: {{ player.knowledge }}</span></div>
</div>
</div>
</div>
In your service you don't have to return with the promise. You can use a getter
private player: Player;
get CurrentPlayer()
{
return this.player;
}
Then in your component:
getPlayer() {
this.firebaseRef.once("value", (dataSnapshot) => {
if (dataSnapshot.child('players').child(this.authData.uid).exists()) {
this.firebaseRef.child('players').child(this.authData.uid).once("value", (data) => {
this.playerService.setPlayer(this.player);
console.log(this.player);
});
} else {
this.playerService.createPlayer(this.authData.uid, this.getName(this.authData), this.firebaseRef);
console.log(this.player);
}
});
ngOnInit() {
this.player = this.playerService.CurrentPlayer();
this.getPlayer();
}
If you setup the reference first, it should automatically update. You can also throw an *ngIf player-details component definition in the DOM and only show it once the player object isn't undefined.
Edit
Just saw someone else posted about *ngIf prior to me, so if that is the solution, please mark theirs.
The player variable was undefined when the PlayerDetailComponent was loaded therefore there was no such object as player.
To fix this, OnChanges can be implemented like this:
import { Component, Input, OnChanges, SimpleChange } from '#angular/core';
import { Player } from '../classes/player';
import {HealthBarComponent} from "./health-bar.component";
import {ChecklistComponent} from "./checklist.component";
#Component({
selector: "player-details",
templateUrl: "app/views/player-detail.component.html",
styleUrls: ['app/style/player-detail.component.css'],
directives: [HealthBarComponent, ChecklistComponent],
})
export class PlayerDetailComponent implements OnChanges{
#Input()
player: Player;
ngOnChanges(changes: {[propName: string]: SimpleChange}) {
}
}
and then we can add *nfIf="player" within the template to ensure that the player object isn't blank before loading the element.
Is there any angular multiselect controller that lets you insert options on the go?
I need to start from a list, let's say:
Option A
Option B
Option C
But the user might insert new items like:
Option D
Option E
And delete some others, like:
Option A
Option C
So the final list will be:
Option B
Option D
Option E
Perhap I am confusing the name and it is not multiselect, it is just a dropdown list.
In my current project I am using Select2 and its angular-ui counterpart with success. Maybe this is an option for you.
It works well with ng-model objects.
You are right this is not a multi-select if you're just adding and deleting items.
Just bind an array to an ng-repeat and modify the array using functions in your controller.
<div class="btn-group">
<button type="button" class="btn btn-secondary dropdown-toggle" (click)="toggleSelect()">
<span class="pull-left" [innerHtml]="header"></span>
<span class="caret pull-right"></span>
</button>
<ul class="dropdown-menu multi-select-popup" [ngStyle]="{display:isOpen ? 'block' : 'none'}" style="display:block;">
<li *ngIf="enableFilter" class="filter-container">
<div class="form-group has-feedback filter">
<input class="form-control" type="text" [value]="filterText" [placeholder]="filterPlaceholder" [formControl]="filterInput" />
<span class="clear-filter fa fa-times-circle-o form-control-feedback" (click)="clearFilter()"></span>
</div>
</li>
<li *ngFor="let item of _items | filter:{label:filterText}">
<a (click)="select(item)" class="dropdown-item">
<i class="fa fa-fw" [ngClass]="{'fa-check': item.checked, 'glyphicon-none': !item.checked}"></i>
<span [innerHtml]="item.label"></span>
</a>
</li>
</ul>
</div>
component
import {Component,Input,Output,OnInit,ViewChild,EventEmitter,ChangeDetectionStrategy, ChangeDetectorRef} from '#angular/core';
import {Pipe, PipeTransform} from '#angular/core';
import {Observable} from 'rxjs/Rx';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';
import 'rxjs/add/operator/throttleTime';
import 'rxjs/add/observable/fromEvent';
import { FormsModule, ReactiveFormsModule } from '#angular/forms';
import { FormGroup, FormControl } from '#angular/forms';
import { ControlValueAccessor } from '#angular/forms';
#Pipe({
name: 'filter'
})
export class FilterPipe implements PipeTransform {
transform(items: any, filter: any): any {
if (filter && Array.isArray(items)) {
let filterKeys = Object.keys(filter);
return items.filter(item =>
filterKeys.reduce((memo, keyName) =>
(memo && new RegExp(filter[keyName], 'gi').test(item[keyName])) || filter[keyName] === "", true));
} else {
return items;
}
}
}
#Component({
selector: 'multiselect',
templateUrl: 'templates/multiselect.html'
})
export class Multiselect implements OnInit {
public _items: Array<any>;
public _selectedItems: Array<any>;
public isOpen: bool = false;
public enableFilter: bool;
public header: string = "Select some stuff";
public filterText: string;
public filterPlaceholder: string;
public filterInput = new FormControl();
private _subscription: Subscription;
#Input() items: Observable<any[]>;
// ControlValueAccessor Intercace and mutator
propagateChange = (_: any) => {};
get selectedItems(): any {
return this._selectedItems;
};
writeValue(value: any) {
if (value !== undefined) {
this._selectedItems = value;
} else {
this._selectedItems = [];
}
}
registerOnChange(fn: any) {
this.propagateChange = fn;
}
registerOnTouched(fn: any) : void
constructor(private changeDetectorRef: ChangeDetectorRef) {
}
select(item: any) {
item.checked = !item.checked;
}
toggleSelect() {
this.isOpen = !this.isOpen;
}
clearFilter() {
this.filterText = "";
}
ngOnInit() {
this._subscription = this.items.subscribe(res => this._items = res);
this.enableFilter = true;
this.filterText = "";
this.filterPlaceholder = "Filter..";
this.filterInput
.valueChanges
.debounceTime(200)
.distinctUntilChanged()
.subscribe(term => {
this.filterText = term;
this.changeDetectorRef.markForCheck();
console.log(term);
});
}
}
Angular JS Multi Select would be a good choice. I used it in my project: http://isteven.github.io/angular-multi-select/#/demo-dynamic-update
I really like angular material way of multiselecting items - check this out
https://material.angularjs.org/latest/demo/select
scroll to Option Groups section - there is also code snippet for doing this kind of thing, have fun!