In my Angular2 app I am looping through a list of users, and for each user in the list, I am populating an icon to the view to represent that person. With that working, I'd like to now use material2's tooltip (mdTooltip) to show the name when someone scrolls over the icon. I can get the tooltip working when I connect it to a singular property in my component via string interpolation, like for "name: 'John Smith'" I can just use "{{name}}" in my component HTML. But when I try and pull the name out of an array from that same component, it doesn't work.
This is what my component looks like:
import { User } from './../../views/user/user';
import { Component, OnInit, Input } from '#angular/core';
import { AuthenticationService } from './../../data/authentication.service';
import { Router } from '#angular/router';
#Component({
selector: 'app-room',
templateUrl: './room.component.html',
styleUrls: ['./room.component.less']
})
export class RoomComponent implements OnInit {
otherImg = 'app/img/photo-ph.png';
model: any;
loading = false;
name = 'John Smith';
others = [
{ id: 1, name: 'John Smith', avatar: 'app/img/photo-ph.png' },
{ id: 2, name: 'Javier Sanchez', avatar: 'app/img/photo-ph.png' }
];
user;
token;
nickname;
constructor(private authenticationService: AuthenticationService,
private router: Router) { }
isLoggedIn() {
this.loading = true;
if (this.authenticationService.isAuthenticated()) {
return true;
}
}
ngOnInit() {
}
}
And here's the version of my component HTML that works:
<div *ngIf="isLoggedIn()" class="others">
<span *ngFor="let other of others"><i [ngClass]="'material-icons'" [routerLink]="['/chat']" mdTooltip="{{name}}" tooltip-position="below">person</i></span>
<a [routerLink]="['/login']">Logout</a>
</div>
But when I try string interpolation to pull a value out of an array and use it in the tooltip, it doesn't work:
<div *ngIf="isLoggedIn()" class="others">
<span *ngFor="let other of others"><i [ngClass]="'material-icons'" [routerLink]="['/chat']" mdTooltip="{{others.name}}" tooltip-position="below">person</i></span>
<a [routerLink]="['/login']">Logout</a>
</div>
In your case others is an array, so it doesn't have a "name" property. By you already iterate over it, and put each value into "other".
So this will work:
mdTooltip="{{other.name}}"
I think you are using the array and not the instance var
{{others.name}}
should be
{{other.name}}
Related
I dont know if thats possible or not, but here is my ideia:
I have a class with methods and i want to present the method in a h1 Html component that matches the selected string.
Basically, i have a class of Pokemons, that each method is a pokemon, and i want to switch methods when my user select one type of pokemon.
Here is the code:
The service class (data) :
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root'
})
export class GenIService {
swampert = {
hp: 138,
atk: 121,
def: 110.63,
type: ' Water Ground',
};
}
The main page :
import { Component, OnInit } from '#angular/core';
import {GenIService} from "../Pokemons/gen-i.service";
#Component({
selector: 'app-home',
templateUrl: 'home.page.html',
styleUrls: ['home.page.scss'],
})
export class HomePage implements OnInit {
genOne = [];
button1clicked= false;
button2clicked= false;
pokemon1Selected = '';
pokemon2Selected = '';
constructor(private gen1: GenIService ) {}
ngOnInit(): void {
console.log(this.gen1.swampert.hp);
}
buttonOneSelected() {
this.button1clicked= true;
this.button2clicked=false;
}
buttonTwoSelected() {
this.button1clicked= false;
this.button2clicked=true;
}
pokemon1SeletectedSwampert() {
this.pokemon1Selected = "Swampert";
}
pokemon2SeletectedVenusaur(){
this.pokemon2Selected = 'Venusaur';
}
The Html code:
<ion-item>
<ion-label>Name: {{}}</ion-label>
<ion-label>HP: {{this.gen1.{pokemon1Selected}.hp}}</ion-label>
<ion-label>ATK: {{}}</ion-label>
<ion-label>DEF: {{}}</ion-label>
<ion-label>Type: {{}}</ion-label>
</ion-item>
So, in the Html code i am tring to have this variable = this.gen1.swampert.hp, but switch the pokemon name with the variable of pokemon1Selected, that in this case is equal to "Swampert".
How can i do that?
Based on your declaration genOne is an array so I'm not sure what you are trying to get when you call
console.log(this.gen1.swampert.hp);
If I understand correctly what you need is something like this (provided you add name property to your pokemon object)
.ts
public getPokemonOneSelected() {
return this.genOne.find(x => x.name === this.pokemon1Selected);
}
HTML
<ion-label>HP: {{this.getPokemonOneSelected()?.hp}}</ion-label>
That '?.' is to prevent error if .find() returns undefined
As Phix pointed out it would be better to track selected pokemon with just 1 variable so instead of
pokemon1Selected = '';
pokemon2Selected = '';
just have
selectedPokemon = '';
...
public getSelectedPokemon() {
return this.genOne.find(x => x.name === this.selectedPokemon);
}
...
<ion-label>HP: {{this.getSelectedPokemon()?.hp}}</ion-label>
I am new to Angular and trying to make a small application.
I referred 'object' does not contain such a member
answer but I am not getting my solution from there.
profile.component.ts
import { Component, OnInit } from '#angular/core';
import { AuthService } from '../../services/auth.service';
import { Router } from '#angular/router';
#Component({
selector: 'app-profile',
templateUrl: './profile.component.html',
styleUrls: ['./profile.component.css']
})
export class ProfileComponent implements OnInit {
user: Object;
constructor(private authService: AuthService, private roter: Router) {}
ngOnInit() {
this.authService.getProfile().subscribe(
profile => {
this.user = profile.user;
},
err => {
console.log(err);
return false;
}
);
}
}
profile.component.html
<div *ngIf="user">
<h2 class="page-header">
{{ user.name }}
</h2>
<ul class="list-group">
<li class="list-group-item">
Username: {{ user.username }}
</li>
<li class="list-group-item">
Email: {{ user.email }}
</li>
</ul>
</div>
Visual studio code is showing this
Error:
[Angular] Identifier 'username' is not defined. 'Object' does not contain such a member
property user of ProfileComponent
Either change
user: Object;
by
user: any;
In your profile.component.ts this will surely work,because initially you have declared it as object so while running the or building app typescript compilation fails due to accessed as user.username.
Either you change the type to any or create interface or type having required properties and assign this type to user
Ex:
profile.component.ts:
interface userObject {
username:string,
password:string
}
access as
export class ProfileComponent implements OnInit {
user : userObject;
}
you have defined user in ProfileComponent class as Object type.wich has no Typescript Model defined.Therefore Typescript is unaware of the structure of the User Object.
So you create a model like this.
interface User{
username : String;
password: ...
....
}
and then use it as type like user : User
The problem will be solved.
When you define an object it doesn't have firstName or lastName. So when accessing from ui it shows the error. So first initialize as given below. Then the issue will be solved.
let user = {firstName: "", lastName:""};
Code:
import { Component, OnInit } from '#angular/core';
import { AuthService } from '../../services/auth.service';
import { Router } from '#angular/router';
#Component({
selector: 'app-profile',
templateUrl: './profile.component.html',
styleUrls: ['./profile.component.css']
})
export class ProfileComponent implements OnInit {
let user = {firstName: "", lastName:""};
constructor(private authService: AuthService, private roter: Router) {}
ngOnInit() {
this.authService.getProfile().subscribe(
profile => {
this.user = profile.user;
},
err => {
console.log(err);
return false;
}
);
}
}
Object refers to the inbuilt object constructor function, which obviously doesn't have the property username. Since you're setting the type of user to be Object and trying to access the property username that doesn't exist, it's showing error.
If you want to define your own types, refer this.
I am trying to pass the string value of this.title from my LandingPage.component to my ResultPage.component.
I retrieve the list.show value, and send it to my TitleService in like so in my:
landingpage.component.html
<ol>
<li (click)="selectShow(list.show)" [routerLink]="['/details', list.id]" *ngFor="let list of shows">{{list.show}}
</li>
</ol>
landingpage.component.ts
import { TitleService } from '../../services/title.service';
constructor(private TitleService: TitleService) {}
selectShow(show) {
this.TitleService.fetchTitle(show)
}
The above sends the list.show value to my:
title.service.ts
// this gives us the name of the clicked show, which we send to TitleResolver
#Injectable()
export class TitleService {
fetchTitle(title) {
console.log("title is " + title); // this outputs correctly
return title;
}
}
And here is how I manage the routing in my:
app-routing.module.ts
import { TitleService } from './services/title.service';
const routes: Routes = [
{ path: '', component: LandingPage },
{
path: 'details/:id', component: ResultPage
}
];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
providers: [TitleService]
})
My question
Once I receive the title.show value in my service component, I'm unsure how to then send it to my receiving component (resultpage.component)
How can I send my title value from my service to my ResultPage.component?
Make the title a public property of the service like this:
// this gives us the name of the clicked show, which we send to TitleResolver
#Injectable()
export class TitleService {
selectedTitle: string;
fetchTitle(title) {
console.log("title is " + title); // this outputs correctly
this.selectedTitle = title;
return title; // No need to return it.
}
}
Then any other component can inject this service and access this.titleService.selectedTitle
In title.service.ts you can declare a variable called title and have setter and getter:
title: string ="";
// replace fetchTitle with setTitle
// remember to change it in the component too
setTitle(title) {
this.title = title;
}
getTitle() {
return this.title;
}
Then, when ResultPage.component is initialized, call getTitle() from TitleService and set the result to a variable declared in the component.
Here's an example of sharing data via shared services.
Separation of concerns... Your landing page is used to select the list item and navigate to the result page. Let it do just that and only that. Let the ResultPage.component do the rest. Note: Other answers recommend storing the value of the last title in the TitleService. It's not a good idea to store state in a service. Then TitleService cannot be used as a generic way to get any title separate from your current navigation, without side effects.
Remove (click) event. Add 'show' as a QueryParam.
landingpage.component.html
<li [routerLink]="['/details', list.id]"
[queryParams]="{show: list.show}"
*ngFor="let list of shows">
{{list.show}}
</li>
Subscribe to router params and queryparams to get the id and show.
resultpage.component.ts
import { Component, OnInit, OnDestroy } from '#angular/core';
import { ActivatedRoute, Router } from '#angular/router';
import { TitleService } from '../../services/title.service';
#Component({
...
})
export class ResultPageComponent implements OnInit, OnDestroy {
itemId: string;
show: string;
subParams: any; // infinite Observable to be unsubscribed
subQueryParams: any; // infinite Observable to be unsubscribed
constructor(
...
private TitleService: TitleService,
protected route: ActivatedRoute,
protected router: Router,
...
) {}
ngOnInit() {
this.subParams = this.route.params.subscribe(this.onParams);
this.subQueryParams = this.route.queryParams(this.onQueryParams);
}
ngOnDestroy() {
// Delete active subscribes on destroy
this.subParams.unsubscribe();
this.subQueryParams.unsubscribe();
}
onParams = (params: any) => {
this.itemId = params['id'];
}
onQueryParams = (data: any) => {
this.show = data.show;
if(this.show) {
this.TitleService.fetchTitle(this.show)
}
}
I have a component that pulls in a value posts like so:
import { Component, OnInit} from "#angular/core";
import template from "./event.component.html";
import style from "./event.component.scss";
#Component({
selector: "EventComponent",
template,
styles: [ style ]
})
export class EventComponent implements OnInit {
posts = [];
constructor() {}
ngOnInit() {
this.posts = {'test': 0,'test': 1};
}
}
This is then looped over in a html template like so AND injected into another component in this case called "mapCompenent" it is also filter in the html using a pipe:
loop 'EventComponent' content
<input id="search_events" type="text" name="search_events" [(ngModel)]="search" ngDefaultControl/>
<mapCompenent [(posts)]="posts"></mapCompenent>
<div class="col s6 m6 l4 cards-container" *ngFor="let post of posts | searchPipe:'name':search "></div>
filter
import { Pipe, PipeTransform, Input, ChangeDetectorRef } from '#angular/core';
import { FormGroup, FormControl, FormBuilder, Validators } from '#angular/forms';
#Pipe({
name : 'searchPipe',
pure: false,
})
export class SearchPipe implements PipeTransform {
public transform(value, key: string, term: string) {
if(term === '' || typeof term === undefined ){
return value;
}
return value.filter((item) => {
if (item.hasOwnProperty(key)) {
if (term) {
let regExp = new RegExp('\\b' + term, 'gi');
//this.ref.markForCheck();
return regExp.test(item[key]);
} else {
return true;
}
} else {
return false;
}
});
}
}
mapComponent
import { Component, OnInit, Input, OnChanges, SimpleChanges, SimpleChange } from "#angular/core";
import template from "./map.component.html";
import style from "./map.component.scss";
#Component({
selector: 'mapCompenent',
styles: [ style ],
template
})
export class MapComponent implements OnInit, OnChanges{
#Input() posts: object = {};
ngOnInit() {
}
ngOnChanges(changes: SimpleChanges) {
const posts: SimpleChange = changes.posts;
console.log('prev value: ', posts.previousValue);
console.log('got posts: ', posts.currentValue);
}
}
As soon as the page is loaded the mapcomponent grabs the ngOnChanges BUT not when the filter is used to filter the posts, the loop updates the posts fine and the filter works there the problem is the mapcomponent. What is the best way to notify the mapcomponent of a change to the posts Object?
The pipe will not overwrite the original posts property in EventComponent, so you are only using the filtered version in the *ngFor:
<input id="search_events" type="text" name="search_events" [(ngModel)]="search" ngDefaultControl/>
<mapCompenent [(posts)]="posts"></mapCompenent>
<div class="col s6 m6 l4 cards-container" *ngFor="let post of posts | searchPipe:'name':search "></div>
One solution is to add the pipe to the <mapComponent>'s posts attribute as well, but note it can't be two-way binded ([()]) then, you should change it to one-way ([]).
<input id="search_events" type="text" name="search_events" [(ngModel)]="search" ngDefaultControl/>
<mapCompenent [posts]="posts | searchPipe:'name':search"></mapCompenent>
<div class="col s6 m6 l4 cards-container" *ngFor="let post of posts | searchPipe:'name':search"></div>
A better solution would be to inject that pipe into the EventComponent constructor, listen for changes on the search input or watching search and update another attribute, let's say filteredPosts accordingly using the pipe, and use that one both in the *ngFor and the <mapCompenent>:
#Component({ ... })
export class EventComponent implements OnInit {
posts = [];
filteredPosts = [];
constructor(private searchPipe: SearchPipe) {}
ngOnInit() {
this.posts = ...;
this.form.search.valueChanges.subscribe((value) => {
this.filteredPosts = this.searchPipe.transform(this.posts, 'name', value);
});
}
}
Hoping for some help on a more complex example that expands on the examples in angular's Tour of Heroes
Rather than submitting a single string each time, how would you submit multiple values like in the following example e.g.:
export class LittleTourComponent {
heroes = [ {
'name':'Hulk',
'power':'strength'
},{
'name':'Bulk',
'power':'appetite'
}];
I presume a new 'entry' made up of the submitted values should be pushed to the heroes array something like this:
addHero(newHero) {
if (newHero) {
var entry = {
'name': newHero.name,
'power': newHero.power
};
this.heroes.push(entry);
}
}
But what would be required in the template? Would you still use keyup.enter in this case?:
template:
<label>name</label
// how should the inputs be filled out in this scenario?
<input >
<label>power</label>
<input >
<button (click)=addHero(newHero)>Add</button>
<ul *ngFor="let hero of heroes">
<li>name:{{hero.name}}</li>
<li>power:{{hero.power}}</li>
</ul>
example also on plnkr
Any help appreciated. thanks!
Try and do this in your ts file:
import {Component} from '#angular/core';
class Hero {
name: string;
power: string;
}
export class LittleTourComponent {
newHero: Hero;
constructor() {
this.newHero = new Hero();
}
heroes = [{
'name': 'Hulk',
'power': 'strength'
}, {
'name': 'Bulk',
'power': 'appetite'
}];
addHero() {
if (this.newHero) {
var entry = {
'name': this.newHero.name,
'power': this.newHero.power
};
this.heroes.push(entry);
}
}
}
...and this in your html
<label>name</label>
<input [(ngModel)]="newHero.name">
<label >power</label>
<input [(ngModel)]="newHero.power">
<button (click)=addHero()>Add</button>
<ul *ngFor="let hero of heroes">
<li>name:{{hero.name}}</li>
<li>power:{{hero.power}}</li>
</ul>
your click listener is calling what it thinks is a reference to an element in the DOM which u havent defined nor would take paramaters. Trying putting quotes around that callback
<label>name</label
// how should the inputs be filled out in this scenario?
<input >
<label>power</label>
<input >
<button (click)="addHero(newHero)">Add</button>
<ul *ngFor="let hero of heroes">
<li>name:{{hero.name}}</li>
<li>power:{{hero.power}}</li>
</ul>
after further review, i notice ur referencing newHero in the little-tour component which does not exist in that components scope. Also, uve bound correctly to your inputs but i dont believe .value is the correct property to return the input... try
[(ngModel)]="input1"
in your class declaration ad
input1: String;
and then using that variable.
I didnt notice until right now that you arent importing your directive
import { Component } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: 'app/app.component.html'
})
export class AppComponent { }
since u are calling
<little-tour></little-tour>
in your app.component.html then this should be your app component
import { Component } from '#angular/core';
import {LittleTourComponent} from 'path-to-little-tour'
#Component({
selector: 'my-app',
templateUrl: 'app/app.component.html',
directives: [LittleTourComponent]
})
export class AppComponent { }