ngClass doesn't refresh css class - css

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

Related

How can I use data from JSON file to set the width of some HTML elements?

I want to get specific data from JSON objects and use it to set the width of various tags.
The JSON object is like this:
"habilidades": [
{
"name": "Manejo del Tiempo",
"description": "Capacidad para priorizar tareas a fin de cumplir con los plazos.",
"percentage": "85%",
"width": "85%"
},
{
"name": "Trabajo en Equipo",
"description": "Facilidad para trabajar en grupos interdisciplinarios",
"percentage": "90%",
"width": "90%"
},
]
I have a service that sends a GET request that returns the set of data:
import { HttpClient } from '#angular/common/http';
import { Injectable } from '#angular/core';
import { Observable } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class PortfolioService {
constructor(private http: HttpClient) { }
obtenerDatos(): Observable<any> {
return this.http.get('./assets/data/data.json');
}
}
Then, the component suscribes to this and assigns the array to a variable:
export class HabilidadesComponent implements OnInit {
habilidadesList: any;
constructor(private datosPortfolio: PortfolioService) { }
ngOnInit(): void {
this.datosPortfolio.obtenerDatos().subscribe(data => {
this.habilidadesList = data.habilidades;
})
}
}
So far so good (I think)...
Then, in HTML I have a *ngFor directive that iterates through the array:
<div *ngFor="let habilidad of habilidadesList">
<div class="col d-flex align-items-start">
<div>
<h4 class="fw-bold mb-0">{{ habilidad.name }}</h4>
<p>{{ habilidad.description }}</p>
<div class="barra-progreso">
<span style="width: ">{{ habilidad.percentage }}</span> <!-- I want habilidad.width value to set the width of span.-->
</div>
</div>
</div>
I want habilidad.width to set the width of the <span> element. I can't realize how to do this. I've tried property binding, using the *ngStyle directive but couldn't make it work.
You can bind the width using [style.width.%]
<span [style.width.%]="habilidad.width"></span>
You can also make use of px, em like below
<div [style.width.em]="width"> Width Size with em</div>
<div [style.width.pt]="width"> Width Size with pt</div>

ionic async ngFor data

UPDATE ON BOTTOM
I am trying to show data in an *ngFor that i'm getting from an object that is getting retrieved asynchronously from ionic storage. At the moment i am getting a blank screen.
I have tried multiple things like using async pipes in different manners.
Does anybody know the right way?
Here is my storage service method that is getting called:
public getFlow(flowId:number){
return this.storage.get(FLOWS_KEY).then((flows:Map<number,Flow>)=>{
return flows.get(flowId);
});
}
this returns a Promise<Flow>
this is my component code:
import { Component, OnInit } from '#angular/core';
import { ModalController } from 'ionic-angular';
import { IonicPage, NavController, NavParams } from 'ionic-angular';
import { Flow } from '../../model/Flow';
import { FlowService } from '../../model/services/flowService';
import {CreateTaskPage} from '../create-task/create-task'
import { Task } from '../../model/Task';
#IonicPage()
#Component({
selector: 'page-flow',
templateUrl: 'flow.html',
})
export class FlowPage {
flow;
constructor(public navCtrl: NavController, public navParams: NavParams,private flowService:FlowService,public modalCtrl: ModalController) {
this.flow = this.flowService.getFlow(Number(this.navParams.get("flowId")))
}
ngOnInit(): void {
}
ionViewDidLoad() {
console.log('ionViewDidLoad FlowPage');
}
createTask(){
const modal = this.modalCtrl.create(CreateTaskPage,{flowId:this.flow.flowId});
modal.present();
}
swipe(e,task:Task){
if(e.direction == 2){
console.log("panUp");
task.column--;
}
if(e.direction == 4){
console.log("panDown");
task.column++;
}
}
}
My html:
<ion-content padding>
<div *ngIf="(flow | async)">
<div *ngFor="let col of flow.columns;index as i">
<h2>{{col}}</h2>
<div *ngFor="let task of flow.getTasksFromCol(i)">
<ion-card (swipe)="swipe($event,task)">
<ion-item>
<h2>{{task}}</h2>
<button ion-button item-end clear icon-end>
<ion-icon name='more'></ion-icon>
</button>
<p>{{task}}</p>
</ion-item>
</ion-card>
</div>
</div>
</div>
<ion-fab right bottom>
<button ion-fab color="light"><ion-icon name="arrow-dropleft"></ion-icon></button>
<ion-fab-list side="left">
<button (click)="createTask()" ion-fab><ion-icon name="add-circle"></ion-icon></button>
<button ion-fab><ion-icon name="create"></ion-icon></button>
</ion-fab-list>
</ion-fab>
</ion-content>
Thanks for helping.
UPDATE:
I found one big mistake in my component it now looks like this:
flow:Flow;
constructor(public navCtrl: NavController, public navParams: NavParams,private flowService:FlowService,public modalCtrl: ModalController) {
this.flowService.getFlow(Number(this.navParams.get("flowId"))).then(flow =>{
this.flow = flow;
})
}
i also updated my html but it still isn't working: i now get error:
ERROR TypeError: _co.flow.getTasksFromCol is not a function
at Object.eval [as updateDirectives]
This is weird because this method exists in my Flow Model:
import { Task } from "./Task";
export class Flow {
//PK for 1-n relation with task
flowId:number;
projectName:string;
columns:string[];
tasks: Map<number,Task>;
constructor(flowId:number,projectName:string,columns:string[],tasks:Map<number,Task>){
this.flowId = flowId;
this.projectName = projectName;
this.columns = columns;
this.tasks = tasks;
}
public getTasks(){
return Array.from(this.tasks.values())
}
public getTasksFromCol(colNumber:number){
var tasks = new Array<Task>();
for(let task of Array.from(this.tasks.values())){
if(task.column == colNumber){
tasks.push(task)
}
}
return tasks;
}
}
UPDATE2
i now added this to my service
public getTasksFromCol(flowId:number,colNumber:number){
return this.storage.get(FLOWS_KEY).then((flows:Map<number,Flow>)=>{
var flow:Flow = flows.get(flowId);
var tasks = new Array<Task>();
for(let task of Array.from(flow.tasks.values())){
if(task.column == colNumber){
tasks.push(task)
}
}
return tasks;
});
}
do i just call this in my html page? i'm kinda stuck
I found your mistake.
You have created one variable.
flow:Flow;
You have assigned value to that variable.
this.flow = flow;
Now you need to understand that variable has contains some value related to what you have assigned. So you can't access flow.getTasksFromCol()
Thats the reason you have faced this error.
ERROR TypeError: _co.flow.getTasksFromCol is not a function at Object.eval [as updateDirectives]
Solution:-
Just move this getTasksFromCol() method to service and apply html like following,
I hope it's working. Let try this once and let me know if any error.

Having issues trying to get downloadURL for multiple images from firebase storage using angular fire2

I have created a collection that holds the news title, news img, news desc, etc.. I also created the upload method that works fine , uploads the file to storage and saves the path to the collection.
Issues when trying to retrieve the image using getDownloadUrl();
Here are the files
news.component.html
<div class="container-fluid">
<div *ngFor="let item of thenews | async">
<p>
{{ item.newsTitle }} {{ item.newsDesc }}
</p>
<div class="col-md-4">
<img [src]="getImgUrl(item.newsImg)" class="img-responsive">
</div>
</div>
</div>
<div class="container-fluid">
<button mat-raised-button (click)="onLogout()">Logout</button>
</div>
News.component.ts
import { tap } from 'rxjs/operators';
import { AngularFireStorage } from 'angularfire2/storage';
import { AngularFirestoreCollection, AngularFirestoreDocument, AngularFirestore } from 'angularfire2/firestore';
import { Component, OnInit, Injectable } from '#angular/core';
import { AuthService } from './../auth.service';
import { DataService } from '../services/data.service';
import { News } from './../models/newsModel';
import { Observable } from 'rxjs';
#Component({
selector: 'app-news',
templateUrl: './news.component.html',
styleUrls: ['./news.component.css']
})
#Injectable()
export class NewsComponent implements OnInit {
news = {} as News;
newsCol: AngularFirestoreCollection<News>;
thenews: Observable<News[]>;
imgUrl: Observable<any>;
theData: News[] = [];
constructor(public authservice: AuthService, public dataservice: DataService, public afs: AngularFirestore,
private storage: AngularFireStorage) { }
ngOnInit() {
this.newsCol = this.afs.collection<News>('news');
this.thenews = this.newsCol.valueChanges();
}
getImgUrl(img) {
return this.storage.ref(img).getDownloadURL();
}
addNews(news) {
this.dataservice.addNews(news);
}
onLogout() {
this.authservice.onLogout();
}
}
When this is served it runs into an infinite loop. and the site goes hung.
Any help?
getDownloadURL() is a observable method (async function), so you have to wait for the observable to return the value i.e. the url or null if the image is not available
for example
imageUrl: Observable<string | null>;
const ref = this.storage.ref('users/davideast.jpg');
this.imageUrl= ref.getDownloadURL();
in template
<img [src]="profileUrl | async" />
Please refer this link from github
Hope this helps

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;
}
}

Angular2 communication between 2 components

i am using Angular2 rc5 and i try to build a component communication through a service.
For arrays it works like expected but if i change a string its not updating.
My main component looks like:
import {Component} from '#angular/core';
import {ShareServiceService} from "../share-service.service";
import {ChildTwoComponent} from "../child-two/child-two.component";
import {ChildOneComponent} from "../child-one/child-one.component";
#Component({
selector: 'parent',
template: `
<h1>Parent</h1>
<div>
<child-one></child-one>
<child-two></child-two>
</div>
`,
providers: [ShareServiceService],
directives: [ChildOneComponent, ChildTwoComponent]
})
export class ParentComponent {
constructor() {}
}
My first children component:
import {Component} from '#angular/core';
import {ShareServiceService} from "../share-service.service";
#Component({
selector: 'child-one',
template: `
<div style="float:right; width: 45%">
<pre>title: {{title}}</pre>
<pre>title: {{_sharedService.testTitle}}</pre>
<div>
<ul *ngFor="let dataElement of data">
<li>{{dataElement}}</li>
</ul>
</div>
</div>
`
})
export class ChildOneComponent{
data:string[] = [];
title:string;
constructor(public _sharedService:ShareServiceService) {
this.data = this._sharedService.dataArray;
this.title = this._sharedService.testTitle;
}
}
The second children component:
import {Component} from '#angular/core';
import {ShareServiceService} from "../share-service.service";
#Component({
selector: 'child-two',
template: `
<div style="float:left; width: 45%">
<pre>title: {{title}}</pre>
<pre>titleObj: {{titleObj.title}}</pre>
<div>
<ul *ngFor="let dataElement of data">
<li>{{dataElement}}</li>
</ul>
</div>
<input type="text" [(ngModel)]="dataInput"/>
<button (click)="addData()">addData</button>
<button (click)="updateTitle()">updateTitle</button>
</div>
`
})
export class ChildTwoComponent {
dataInput:string = 'Testing data';
data:string[] = [];
title:string;
constructor(public _sharedService:ShareServiceService) {
this.data = this._sharedService.dataArray;
this.title = this._sharedService.testTitle;
}
addData() {
this._sharedService.insertData(this.dataInput);
this.dataInput = '';
}
updateTitle() {
this._sharedService.updateTestTitle(this.dataInput);
this.dataInput = '';
}
}
My service:
import {Injectable} from '#angular/core';
#Injectable()
export class ShareServiceService {
dataArray:string[] = [];
testTitle:string = "should be updated";
insertData(data:string) {
this.dataArray.push(data);
}
updateTestTitle(newTitle:string) {
this.testTitle = {title: newTitle};
}
}
What i try to achieve is, that if i enter something in the input field with binding for "" and press the "updateTitle" that the Title in both components are updated.
But thats doesnt work currently.
if i add my input value to an array, by clicking the "adddata" Button, all works like excepted and my list with data elements shows all elements.
Does someone know why i cant update a string?
Thanks in advance!
If you copy an object or array you copy a reference. Both (source and destination) point to the same object. If one side modifies the object the other side sees the modification.
If you copy a primitive value (string, number, boolean), the the destination gets a copy of the value and source and destination aren't related in any way.
// copies a reference
this.data = this._sharedService.dataArray;
// copies the value
this.title = this._sharedService.testTitle;
What you probably want is an observable that emits events when properties in the shared service are modified.
For more details see https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#bidirectional-service

Resources