How can i get the first hero to be selected as an initial condition in angular2? - css

In angular2 heros tutorial
template: `
<h1>{{title}}</h1>
<h2>My Heroes</h2>
<ul class="heroes">
<li *ngFor="let hero of heroes"
[class.selected]="hero === selectedHero"
(click)="onSelect(hero)">
<div *ngIf="hero ==heroes[0]"> first hero </div>
<span class="badge">{{hero.id}}</span> {{hero.name}}
</li>
</ul>
<hero-detail [hero]="selectedHero"></hero-detail>`
export class AppComponent implements OnInit {
title = 'Tour of Heroes';
heroes: Hero[];
selectedHero: Hero;
constructor(private heroService: HeroService) { }
getHeroes(): void {
this.heroService.getHeroes().then(heroes => this.heroes = heroes);
}
ngOnInit(): void {
this.getHeroes();
}
onSelect(hero: Hero): void {
this.selectedHero = hero;
}
}
How do I make the first hero item selected when it first starts
and i want to change selectedHero when i click another hero item

this.heroService.getHeroes().then(heroes => {
this.heroes = heroes;
this.selectedHero = this.heroes[0];
});

Related

Passing data between components in Polymer 3

I have 3 components: a mainComponent, a SideMenu, and ContentArea (both children of mainComponent). I want to make ContentArea see the changes of popupOpen value.
The function togglePopup() simply toggle the popupOpen boolean value.
In more details: I pass this function as property from mainComponent to SideMenu. The function changes popupOpen value, but this change isn't reflected in ContentArea.
mainComponent
class mainComponent extends LitElement {
constructor(){
super()
this.popupOpen = false
}
togglePopup() {
console.log("togglePopup from main comp");
this.popupOpen = !this.popupOpen
this.requestUpdate();
}
render(){
return html`
<div>
<side-menu .togglePopup='${this.togglePopup}' popupOpen="${this.popupOpen}"></side-menu>
<content-area popupOpen="${this.popupOpen}"></content-area>
</div>
`
}
}
SideMenu
class SideMenu extends LitElement {
constructor(){
super()
}
static get properties(){
return {
popupOpen: Boolean
}
}
render(){
return html`
<section id="side-menu">
<a #click="${this.togglePopup}" >Add contact</a>
</section>
`
}
}
ContentArea
class ContentArea extends LitElement {
constructor(){
super()
}
static get properties(){
return {
popupOpen: Boolean
}
}
render(){
return html`
<section id="content-area">
<p>POPUP VALUE: ${this.popupOpen}</p> <!-- this value doesn't change! -->
</section>
`
}
}
In order to fire togglePopup function properly, try:
<side-menu .togglePopup='${e=> this.togglePopup()}' .popupOpen="${this.popupOpen}"></side-menu>
instead :
<side-menu .togglePopup='${this.togglePopup}' .popupOpen="${this.popupOpen}"></side-menu>
Demo

ngClass doesn't refresh css class

I have great fight with angular 4. I wan`t to make simple star rating component. My template looks like this :
<div class="card rating">
<div class="card-section">
<p class="ratings-card-header">{{name}}</p>
<div class="grid-x grid-margin-x">
<div class="rating-block cell small-12 medium-6 large-3"
*ngFor="let ratingItem of values; let ratingItemIndex = index">
<p class="ratings-type">{{ ratingItem.label }}</p>
<div class="rating-block-rating">
<a *ngFor="let a of getFakeArrayIteration(); let index = index" [ngClass]="time"
(click)="changeValue(ratingItem, index + 1)" >
<i [ngClass]="isStarSelected(index, ratingItem)">
</i>
</a>
</div>
</div>
</div>
</div>
And my controller class look like this:
import {Component, Input, OnInit} from '#angular/core';
import 'foundation-sites';
import {LabelledValue} from './star-rating-vaules';
#Component({
selector: 'star-rating',
templateUrl: './star-rating.component.html',
styleUrls: ['./star-rating.component.scss']
})
export class StarRatingComponent implements OnInit {
#Input() public name: string;
#Input() public maxValue: number;
#Input() public values: LabelledValue[];
private time: Date;
constructor() {
}
ngOnInit() {
console.log(this.values);
}
changeValue(item: LabelledValue, newValue: number) {
item.value = newValue;
this.time = new Date();
}
isStarSelected(index: number, item: LabelledValue): string {
if (index < item.value) {
return 'fas fa-2x fa-minus-square';
} else {
return 'far fa-2x fa-star';
}
}
getFakeArrayIteration(): any[] {
return new Array(this.maxValue);
}
}
export class LabelledValue {
public key: string;
public label: string;
public value: number;
}
Works on beginning. Set proper amount of stars. But if value changes, you cannot set less stars than initial value. I have no clue what`s wrong
Problem is caused because on first adding icon it is translated somehow to svg. After changes of state it is not a problem, but on beginning is unfortunately blocker of css change. Also I created new question: Prevent svg translation of fontawesome

Angular 2, How console the object object or loop through object object

I am pulling the api from http://ecommerce-ux.london/wp-json/wp-api-menus/v2/menus/2 and it seem to have everything setup correctly and now would like to display from the api object.items ( that is the menu children)
When I do {{ menus }} I get [object object].
Apparently you can not use *ngFor if it is an object, so I needed to creat a pipe but still can figure out how to display the elements as all I get Keys or values.
Could someone please tell me where I gone wrong.
main-menu.component.html
<ul>
<li *ngFor="let menu of menus | menu">
{{ menu.key }} {{ menu.value }}
<ul>
<li *ngFor="let items of menu | menu">
{{ items.key }}
</li>
</ul>
</li>
</ul>
menu.service.ts
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import { Menu } from './menu';
#Injectable()
export class MenuService {
private _wpMenuBase = "http://ecommerce-ux.london/wp-json/wp-api-menus/v2/";
constructor(private http: Http) { }
getPosts(): Observable<Menu[]> {
return this.http
.get(this._wpMenuBase + 'menus/2')
.map((res: Response) => res.json());
}
}
main-menu.components.ts
import { Component, OnInit } from '#angular/core';
import { Menu } from '../menu';
import { MenuService } from '../menu.service';
import { Router, ActivatedRoute, Params } from '#angular/router';
#Component({
selector: 'app-main-menu',
templateUrl: './main-menu.component.html',
styleUrls: ['./main-menu.component.css'],
providers: [MenuService]
})
export class MainMenuComponent implements OnInit {
menus: Menu[];
constructor( private menuService: MenuService, private router: Router ) { }
getPosts(){
this.menuService
.getPosts()
.subscribe(res => {
this.menus = res;
});
}
ngOnInit() {
this.getPosts();
}
}
menu.ts
export class Menu {
}
JSON
{
"ID":2,
"name":"Main menu","slug":"main-menu",
"description":"","count":2,
"items":[
{
"id":4,"order":1,"parent":0,
"title":"Sample Page","url":"http:\/\/ecommerce-ux.london\/sample-page\/","attr":"","target":"","classes":"","xfn":"","description":"","object_id":2,"object":"page",
"object_slug":"sample-page",
"type":"post_type",
"type_label":"Page"},
{
"id":7,
"order":2,
"parent":0,"title":"other page","url":"http:\/\/ecommerce-ux.london\/other-page\/","attr":"",
"target":"","classes":"","xfn":"",
"description":"",
"object_id":5,"object":"page","object_slug":"other-page","type":"post_type","type_label":"Page"}
],
"meta":
{
"links":
{
"collection":"http:\/\/ecommerce-ux.london\/wp-json\/wp\/v2\/menus\/",
"self":"http:\/\/ecommerce-ux.london\/wp-json\/wp\/v2\/menus\/2"
}
}
}
menu.pipe.ts
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'menu'
})
export class MenuPipe implements PipeTransform {
transform(value, args:string[]) : any {
let keys = [];
for (let key in value) {
keys.push({key: key, value: value[key]});
}
return keys;
}
}
You do not need a pipe in this case. The answer provided by Robin had a slight error in the inner iteration: <li *ngFor="let menuItem of menu">, you need to specify that the items is a subarray of menu:
<li *ngFor="let menuItem of menu.items">
So your html should look like this:
<div>
<h2>{{menu.name}}</h2>
<div *ngFor="let menuItem of menu.items">
{{menuItem.url}}<br>
<strong> Name: </strong> {{menuItem.id}}<br>
<strong> Title: </strong> {{menuItem.title}}<br>
</div>
</div>
Here's a demo plunker :)
The issue is that you can't ngfor through object object. The way you do it is via filter. Now in the end I created my own pipe, or so call filter. Not sure if it is right, but it worked like a charm.
main-menu.component.html
<ul>
<li *ngFor="let menu of menus | menu">
<a href="{{ menu.menuitem.url }}">
<strong> Name :</strong> {{ menu.menuitem.id }} |
<strong> Title :</strong> {{ menu.menuitem.title }} <br />
</a>
</li>
</ul>
PIPE
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'menu'
})
export class MenuPipe implements PipeTransform {
transform(value, args:string[]) : any {
//return value.filter( item => value[item] != value[item]);
let keys = [];
for (let key in value) {
if(key == 'items'){
for (let a in value[key]) {
keys.push({menuitem: value[key][a]});
// keys.push({menuName: value[key][a].id, menuName: value[key][a].title});
// console.log(value[key][a].id);
}
//keys.push({key: key, value: value[key]});
}
}
console.log(keys);
return keys;
}
}

Angular 2 Http Service Injectable

I'm pulling a big object from my server using Angular 2 service when the website starts. The data I need to pull looks like this:
{
Edu: [...],
Exp: [...],
Links: [...],
Portfolio: [...],
Skills: [...]
}
And I set up the service this way:
AllDataService:
import { Injectable, OnInit } from "#angular/core";
import { Http, Response } from "#angular/http";
import { Observable } from "rxjs/Rx";
#Injectable()
export class AllDataService {
private allDataUrl = ".../allData";
private loading: boolean;
private Edu: Array<any>;
private Exp: Array<any>;
private Links: Array<any>;
private Portfolio: Array<any>;
private Skills: Array<any>;
constructor(private http: Http) {
this.loading = true;
this.Edu = [];
this.Exp = [];
this.Links = [];
this.Portfolio = [];
this.Skills = [];
}
ngOnInit() {
this.getAllData();
}
// Get data from api, aka "Set" methods
getAllData() {
return this.http.get(this.allDataUrl)
.subscribe(
data => {
this.Edu = data.Edu;
this.Exp = data.Exp;
this.Links = data.Links;
this.Portfolio = data.Portfolio;
this.Skills = data.Skills;
this.loading = false;
},
err => console.error(err)
);
}
// “Get” methods
getLoading() { return this.loading; }
getEdu() { return this.Edu; }
getExp() { return this.Exp; }
getLinks() { return this.Links; }
getPortfolio() { return this.Portfolio; }
getSkills() { return this.Skills; }
}
And in my component, I inject the service so that I can get data:
HomeIcons:
import { Component } from "#angular/core";
import { AllDataService } from "../allDataService";
#Component({
selector: "home-icons",
template: `
<div class="home-icons-wrapper">
<ul class="home-icons-ul no-select">
<li class="home-icons-li"
*ngFor="let link of links" >
<a href={{link.url}} target="_blank">
<span class="home-icons-icon {{link.icon}}"></span>
</a>
</li>
</ul>
</div>
`,
providers: [AllDataService]
})
export class HomeIcons {
public links;
constructor(private http: Http, private allDataService: AllDataService) {
this.links = allDataService.getLinks();
}
}
However, in the AllDataService, the error message tells me that properties (Exp, Edu, Skills...) don't exist in Response. How should I setup my http service correctly so that I can pull the data I want at start and make sure all the components get the data? Thanks
All you need to do, is to convert your response to a JavaScript object:
// Get data from api, aka "Set" methods
getAllData() {
return this.http.get(this.allDataUrl)
.map(res => res.json()) // <-- this line here
.subscribe(
data => {
this.Edu = data.Edu;
this.Exp = data.Exp;
this.Links = data.Links;
this.Portfolio = data.Portfolio;
this.Skills = data.Skills;
this.loading = false;
},
err => console.error(err)
);
}
Bind to the method directly in your template:
template: `
<div class="home-icons-wrapper">
<ul class="home-icons-ul no-select">
<li class="home-icons-li"
*ngFor="let link of allDataService.getLinks()" >
<a href={{link.url}} target="_blank">
<span class="home-icons-icon {{link.icon}}"></span>
</a>
</li>
</ul>
</div>
`,

MDL - ripple effect changes margin on some elements

I'm using react with mdl and I want to create a menu with a drawer, and I'm pretty happy with the result, except one thing, when there is a scroller on the menu and I click on a menu item there is a ripple effect that sometimes makes the other menu items move a bit:
I don't know what is going on, here is my menu item component:
import React from 'react'
export default class MenuItem extends React.Component {
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
}
onClick() {
this.props.onClick(this.props.id);
}
render () {
let liClassNames = this.props.isSelected ? 'menuItemSelected' : 'menuItemNotSelected';
return (
<div className={'mdl-list__item mdl-button mdl-js-button mdl-js-ripple-effect menuItem ' + liClassNames} onClick={this.onClick}>
<span className='mdl-list__item-primary-content'>
<i className={'material-icons menuItemIcon md-28'}>{this.props.icon}</i>
<span>{this.props.screenName}</span>
</span>
</div>
)
}
}
React.propTypes = {
id: React.PropTypes.number.isRequired,
screenName: React.PropTypes.string.isRequired,
icon: React.PropTypes.string.isRequired,
isSelected: React.PropTypes.bool.isRequired,
onClick: React.PropTypes.func.isRequired
};
Menu:
import React from 'react';
import MenuItem from './MenuItem';
export default class Menu extends React.Component {
constructor(props) {
super(props);
this.state = {
isMaximized: true,
selectedMenuItem: this.props.modules[0].screens[0].id
};
this.handleMenuToggle = this.handleMenuToggle.bind(this);
this.onMenuItemClick = this.onMenuItemClick.bind(this);
}
handleMenuToggle() {
this.setState({ isMaximized: !this.state.isMaximized });
}
onMenuItemClick(screenId) {
this.setState({ selectedMenuItem: screenId });
}
render () {
let drawerClassNames = this.state.isMaximized ? 'drawerMaximized' : 'drawerMinimized';
return (
<div className='mdl-js-layout mdl-layout--fixed-drawer'>
<div className={'mdl-layout__drawer drawer scrollbar ' + drawerClassNames }>
<ul className='demo-list-item mdl-list content'>
{this.props.modules.map((module, index) =>
<li key={index}>
<span className="mdl-layout-title menuHeader">{module.moduleName}</span>
{module.screens.map((screen) =>
<MenuItem key={screen.id}
id={screen.id}
screenName={screen.screenName}
icon={screen.icon}
isSelected={ screen.id === this.state.selectedMenuItem ? true : false }
onClick={this.onMenuItemClick}/>
)}
</li>)}
</ul>
</div>
</div>
);
}
}
Menu.propTypes = {
modules: React.PropTypes.arrayOf(
React.PropTypes.shape({
moduleName: React.PropTypes.string.isRequired,
id: React.PropTypes.number.isRequired,
screens: React.PropTypes.arrayOf(
React.PropTypes.shape({
id: React.PropTypes.number.isRequired,
screenName: React.PropTypes.string.isRequired,
icon: React.PropTypes.string.isRequired
})
).isRequired
}
)
).isRequired
}
When I remove the ripple effect from the li tag, (mdl-js-ripple-effect class) it works fine.
What is the problem here?

Resources