I started learning Angular recently, I'm trying to make a github search app with the github api but I have some problems with local storage. I have an add to favorite button for pin the profile to the page. When it's pinned remove favorite button should be appear instead of add to favorite button and it should be remove the profile. I thought I could do this with adding and removing profiles from local storage. I have an user variable which holds the profile info as an object when the user types username in the search bar. Then I'm passing this data to local storage and take all the local storage data to make it an array so I can display it with *ngFor. The problem is when I pin the profile, I can't remove permanently specific profile from the page. I can only delete pinned profiles temporarily. I'm dealing with this problem for two days, I have shared the part what I did until now. The purple area is where the pinned profiles are shown.
home.component.html:
<input type="text" [(ngModel)]="profile" (ngModelChange)="detectChange($event)" (keyup)="findProfile()" placeholder="Enter the username..." class="input">
<div style="background-color: lightslategrey;">
<ng-template [ngIf]="profile !== '' && user">
<img [src]="user.avatar_url" alt="" class="userAvatar">
<p>Username: {{user.login}}</p>
<p>Location: {{user.location}}</p>
<p>E-mail: {{user.email}}</p>
<p>Blog Link: {{user.blog}}</p>
<p>Member Since: {{user.created_at}}</p>
<button [routerLink]="['', user.login.toLowerCase(), user.id ]" class="viewProfileButton" a>View
Profile</button><br>
<button (click)="localStorage()" class="viewProfileButton">Add to Favorite</button>
</ng-template>
</div>
<div style="background-color: rgb(106, 106, 170);" *ngFor="let item of display">
<button (click)="consoleLog()">consoleLog</button>
<p>Username: {{item.login}}</p>
<p>Location: {{item.location}}</p>
<p>ID: {{item.id}}</p>
<button (click)="localStorage(item.id)">Add to favoriteeee</button>
<button (click)="removeLocal(item.id)" class="viewProfileButton">Remove Favorite</button>
</div>
<button (click)="consoleLog()" class="viewProfileButton">Console Log</button>
<router-outlet></router-outlet>
home.component.ts:
import { Component, OnInit, Input } from '#angular/core';
import { HttpService } from '../http.service';
import { ProfileComponent } from './profile/profile.component';
import { JsonPipe } from '#angular/common';
import { bindCallback } from 'rxjs';
import { FormArray } from '#angular/forms';
#Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.scss'],
})
export class HomeComponent implements OnInit {
user: any;
profile: any;
display: any;
local: any;
randomNumber: any;
randomString: any;
idString: any;
keys: any;
closeDiv: boolean = true;
constructor(private userData: HttpService) {}
ngOnInit() {
this.display = Object.values(localStorage).map((val: any) => JSON.parse(val));
console.log('ngOnInit Works', this.display);
}
findProfile() {
this.userData.updateProfile(this.profile);
this.userData.getUser().subscribe((result) => {
this.user = result;
});
}
localStorage(id: any) {
this.idString = JSON.stringify(id);
localStorage.setItem(this.idString, JSON.stringify(this.user));
this.display = Object.values(localStorage).map((val: any) => JSON.parse(val));
console.log(Object.values(this.display));
}
removeLocal(id: any) {
for (let i = 0; i < this.display.length; ++i) {
if (this.display[i].id === id) {
this.display.splice(i, 1);
localStorage.removeItem(this.display[i].id);
}
}
}
detectChange(ev: any) {
ev.length > 0 ? (this.closeDiv = false) : (this.closeDiv = true);
}
}
in component.ts
let item = 1;
`
localStorage.setItem('itemName',Item);
const getItem = localStorage.getItem('itemName')
so constant getItem will have your value. you can do the same with an array
Related
I am facing a weird issue with the ElementRef in Angular 5.
I have a home component which contains two popups, which consists of different component ListPopUp and tile. I am iterating through a list and creating multiple tiles on the home page. When you click on a tile it will open a listPopup.
From the list popUp you can click a function that will trigger a event in Parent and thus trigger a function in tile component.
Everything works file except when we trigger the function second time the myBar ElementRef (in tile component class) returns undefined. I have tried accessing the bar directly using getElementById("myBar") but it also becomes undefined the second time. I am not sure why this is happening, I have tried to modify the code and look for any explanation in Angular documentation but nothing is helping.
I have provided the code for all the three components.
Please let me know if further info is required.
Here is the code:
TileComponent:
import { Component, Input, ViewChild, ElementRef } from '#angular/core';
#Component({
selector: 'tile',
templateUrl: 'tile.html'
})
export class TileComponent {
#Input('tileinfo') tileinfo;
#ViewChild('myBar') myBar:ElementRef;
constructor() {
};
ngOnInit() {
};
move(value) {
let elem = this.myBar;
let id = setInterval(frame, this.tileinfo.progressSpeed * 1000);
this.tileinfo.active = value;
let val = this.tileinfo;
val.allowNext = false;
function frame() {
if (val.progress >= 100) {
clearInterval(id);
delete val.active;
val.allowNext = true;
val.progress = 0;
} else {
val.progress++;
elem.nativeElement.style.width = val.progress + '%';
}
}
}
}
HomeComponent Code:
import { Component, ViewChild } from '#angular/core';
import { GlobalProvider } from '../../providers/global/global';
import { CommonMethodsProvider } from '../../providers/common-methods/common-methods';
#Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
#ViewChild('tileComponent') tileComponent: any;
tileInfoArr: Array<any> = [];
ageList: Array<any> = [];
gameObject: any;
modalInfo: any = {};
modalList: any = {};
constructor(public navCtrl: NavController, private globalService: GlobalProvider, private commonService: CommonMethodsProvider) {
this.globalService.getGameDummy().subscribe(data => this.gameSetupHelper(data), error => console.log(error));
}
ngOnInit() {
}
fillTiles() {
this.tileInfoArr = [
{
name: 'Research',
progress: 1,
description: "Description Here",
progressSpeed: this.gameObject.researchProgress,
allowNext: true,
},
];
}
tileClicked(tileInfo) {
switch (tileInfo.name) {
case 'Research':
if (this.tileInfoArr[0].allowNext)
this.modalList = this.commonService.generateModalList("Research List", this.globalService.getAgeDataObj().researchList);
break;
default:
break;
}
};
listItemClicked(stuff) {
if (stuff.data === "age") {
this.tileComponent.move(this.gameObject.nextAgeDisplayName);
} else {
this.tileComponent.move(stuff.data.name);
}
};
}
ListPopupComponent:
import { Component, Input, Output, EventEmitter } from '#angular/core';
#Component({
selector: 'list-popup',
templateUrl: 'list-popup.html'
})
export class ListPopupComponent {
#Input('modalList') modalList;
#Output() listItemClicked = new EventEmitter();
text: string;
constructor() {
};
startStuff(listItem): void {
this.listItemClicked.emit({
data: listItem
});
}
closePopup() {
this.modalList.showPopup = false;
};
}
HTML :
HomeComponent:
<div padding style="padding-top: 0px">
<div class="tile-container-container">
<div class="tile-container" *ngFor="let tileinfo of tileInfoArr;">
<tile [tileinfo]="tileinfo" (click)="tileClicked(tileinfo)" #tileComponent></tile>
</div>
</div>
</div>
<info-popup [modalInfo]="modalInfo"></info-popup>
<list-popup (listItemClicked) = "listItemClicked($event)" [modalList]="modalList"></list-popup>
TileHTML:
<div class="tile-box">
<div class="tile-inside">
<h6>
{{tileinfo.name}}
</h6>
<p>
{{tileinfo.description}}
</p>
</div>
<footer *ngIf="tileinfo.progress !== 0">
<div id="myProgress">
<div id="myBar" #myBar></div>
</div>
</footer>
</div>
ListPopupHTML
<div *ngIf="modalList.showPopup" id="myModal" class="modal">
<div class="modal-content card">
<span class="close" (click)="closePopup()">
x
</span>
<div>
<h6>
{{modalList.title}}
</h6>
</div>
<hr>
<div *ngIf="modalList.list && modalList.list.length > 0;else ageChange">
<ul class="list">
<li class="item" (click)="startStuff(listInfo)" *ngFor="let listInfo of modalList.list; index as i; even as isEven; odd as isOdd" class="">
<div>
<span class="">{{listInfo.name}}</span>
<br>
<span>{{listInfo.description}}</span>
</div>
</li>
</ul>
</div>
<ng-template #ageChange>
<div class="age-Change">
<h1>
Enter New Age
</h1>
<div (click)="startStuff('age')" class="new-age-icon">
</div>
</div>
</ng-template>
</div>
</div>
Answering my own question.
Silliest mistake that cost me a day of work.
Using *ngIf="tileinfo.progress !== 0" in the tile template. and then making val.progress = 0; in tileComponet. This was removing the div at the end thus the element was not coming because there was no element at all.
Sorry for wasting everyone's time, who took time to look into the issue...
Transfer single item from a list item to cart list.
I am developing an Angular web app and want that when I click a button the single item of an array gets transferred from one service to another service and is also transferred on another component. I have successfully implemented it with a transfer of whole array but I am facing problem with a single item.Please help.
What I want is that when I click on Add to cart button the list item which is clicked only gets transferred and not the array of list items.
buyGame.html file
<div class="col-xs-6">
<a class="list-group-item clearfix" style="background-color:rgb(3, 0, 48)" *ngFor="let buying of buy">
<div class="pull-left" style="max-width:330px">
<h5 style="color:white">{{buying.names}}</h5>
<p style="color:white">{{buying.desc}}</p>
<button class="btn btn-danger ; pull-left" (click)= "onAddToCart()">Add To Cart</button>
</div>
<div>
<span class="pull-right">
<img [src]="buying.getImg" alt="image not loaded" class="img-responsive" style="max-height:100px">
</span>
</div>
</a>
</div>
buygame.service.ts file :
import { gameBuy } from "./buygame.model";
import { Injectable,EventEmitter } from "#angular/core";
import { cartService } from "./cart.service";
#Injectable()
export class gameService{
private gameServ: gameBuy[] = [
new gameBuy('batman','Batmobile and enhancements to signature features',"https://www.geek.com/wp-content/uploads/2016/02/batmans-625x352.jpg"),
new gameBuy('GTA 5',
"PlayStation 3 or Xbox 360 will be able to transfer their current Grand Theft Auto Online characters and progression to their choice of PlayStation 4 Xbox One or PC",
"http://onlysp.com/wp-content/uploads/2015/01/maxresdefault.jpg")
];
constructor(private cartSer: cartService){}
getBuyingList(){
return this.gameServ.slice();
}
addItemToCart(game:gameBuy[]){
this.cartSer.addItem(game);
}
}
buyGame.component.ts:
import { Component, OnInit,Input } from '#angular/core';
import { gameBuy } from '../shared/buygame.model';
import { gameService } from '../shared/buygame.service';
#Component({
selector: 'app-buy-game',
templateUrl: './buy-game.component.html',
styleUrls: ['./buy-game.component.css'],
})
export class BuyGameComponent implements OnInit {
#Input() buy:gameBuy[];
constructor(private service: gameService) { }
ngOnInit() {
this.buy = this.service.getBuyingList();
}
onAddToCart(){
this.service.addItemToCart(this.buy);
}
}
cart.component.ts:
import { Component, OnInit} from '#angular/core';
import { cartModel } from '../shared/cart.model';
import { cartService } from '../shared/cart.service';
import { gameBuy } from '../shared/buygame.model';
#Component({
selector: 'app-cart',
templateUrl: './cart.component.html',
styleUrls: ['./cart.component.css'],
})
export class CartComponent implements OnInit {
cart:gameBuy[];
constructor(private service: cartService) { }
ngOnInit() {
this.cart = this.service.getCartItem();
}
}
cart.service.ts:
import { cartModel } from "./cart.model";
import { EventEmitter } from "#angular/core";
import { gameBuy } from "./buygame.model";
export class cartService{
cartChanged = new EventEmitter<gameBuy[]>();
private cart: gameBuy[] = [
new gameBuy('Batman','Batman is a cool game','https://images-na.ssl-images-amazon.com/images/I/91lu5KHSm3L._SY445_.jpg'),
new gameBuy('Gta 5','online game of GTA','https://www.rockstargames.com/V/img/global/order/mobile-cover.jpg')
];
getCartItem(){
return this.cart.slice();
}
addItem(cart:gameBuy[]){
this.cart.push(...cart);
this.cartChanged.emit(this.cart.slice());
}
}
cart.model.ts:
export class cartModel{
constructor(public cartName: string,public cartDesc: string,public cartImage:string){}
}
buygame.model.ts:
export class gameBuy{
constructor(public names:string, public desc:string, public getImg:string){}
}
You need to specify exact item you want to be added to the cart in the temlate:
(click)= "onAddToCart(buying)"
And then pass it right to your service as onAddToCart method parameter:
onAddToCart(buying: gameBuy){
this.service.addItemToCart(buying);
}
Also, your buygame service method should accept a single item, not a list:
addItemToCart(game: gameBuy){
this.cartSer.addItem(game);
}
Atl last, cart service should be updated too (just to push a single item)
addItem(cart:gameBuy){
this.cart.push(cart);
this.cartChanged.emit([...this.cart]); // slice() is ok too if you need a copy
}
Try providing the index in your call (click)= "onAddToCart(index)" and get it from your array.
OR provide the single object in (click)= "onAddToCart(buying)"
then receive it on TS
How to update component when route changes. I have this component :
import { Component, OnInit } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
import { ListService } from '../list/list.service';
#Component({
selector: 'view',
template: `
<div *ngIf="!entity">
<p>Select <b (click)="showRow()">row {{entity}}</b>!</p>
</div>
<div *ngIf="entity">
<p >{{entity.id}}</p>
<p >{{entity.name}}</p>
<p >{{entity.weight}}</p>
<p >{{entity.symbol}}</p>
</div>
`,
styles: []
})
export class ViewComponent implements OnInit {
constructor(
private route: ActivatedRoute,
private service: ListService
) {
this.route.params.subscribe(params => {
const id = parseInt(params['id']);
if (id) {
const entity = this.service.getRow(id);
this.entity = entity
}
});
}
entity;
showRow() {
console.log(this.entity);
}
ngOnInit() {
}
}
in this.entity inside constructor i have desired object but when i execute showRow this.entity is undefined, what i'm doing wrong ? I have tried to change property to different name, and it didn't work as expected, if any one knows how to resolve this or point me to right direction.
EDIT:
getRow from service
getRow(id) {
console.log(id, 'test');
return this.datasource.find(row => row.id === id);//returns good row
}
Move your code to ngOnInit() method and check will you get value or not.
ngOnInit() {
this.route.params.subscribe(params => {
const id = parseInt(params['id']);
if (id) {
const entity = this.service.getRow(id);
this.entity = entity
}
});
}
I believe you need to define/initialize the entity that is positioned above showRow, such as:
const entity = Entity;
or something along those lines. apologies I am quite new to angular as well.
I found answer to my problem, i just needed to put router-outlet in template just like that :
....
....
template: `
<router-outlet>
<div *ngIf="row?.id; else elseBlock">
<div>
<p>{{row.id}}</p>
<p>{{row.name}}</p>
<p>{{row.weight}}</p>
<p>{{row.symbol}}</p>
</div>
</div>
</router-outlet>
`,
I'm new to Angular2 and spent a lot of time trying to fix a simple thing.
As you can see, I only want to access the Local Storage (bottom function, ui())and send the contents to the View, Register.components.html. I tried various blog but I failed every-time.
So I can't really post an error, but how do I just access the local storage and display the contents to my view? Also ui() isn't being called. How do I call it?
Register.component.ts
import { Component, OnInit } from '#angular/core';
import {ValidateService} from '../../services/validate.service';
import {AlarmService} from '../../services/alarm.service';
import {FlashMessagesService} from 'angular2-flash-messages';
import {Router} from '#angular/router';
#Component({
selector: 'app-register',
templateUrl: './register.component.html',
styleUrls: ['./register.component.css']
})
export class RegisterComponent implements OnInit {
hours: String;
id: String;
constructor(
private validateService: ValidateService,
private FlashMessage: FlashMessagesService,
private Router: Router,
private AlarmService: AlarmService
){
}
ngOnInit() {
}
onRegisterSubmit(){
var user = {
hours: (new Date(this.hours.replace('T', ' ').replace('-', '/'))).valueOf(),
id: new Date().getTime(),
flag: 0
}
setTimeout(() => {
this.FlashMessage.show('Your alarm has been added.', {cssClass: 'alert-success', timeout: 5000});
}, 10);
var storage = localStorage.getItem('users');
var final = [];
if (storage == null || typeof(storage) == undefined )
{ final.push(user);
localStorage.setItem('users', JSON.stringify(final));
let time = new Date().getTime()
this.AlarmService.setUpAlarms(time);
}else{
var get = JSON.parse(localStorage.getItem('users'));
var size = Object.keys(get).length;
for(var i =0; i< get.length; i++){
final.push(get[i]);
}
final.push(user);
localStorage.setItem('users', JSON.stringify(final));
let time = new Date().getTime()
this.AlarmService.setUpAlarms(time);
}
}
ui(){
var storage = localStorage.getItem('users');
if (storage == null || typeof(storage) == undefined ){
var HERO = localStorage.getItem('users');
}
console.log(HERO);
const HEROES = HERO
}
}
This is my HTML view
<form (submit)="onRegisterSubmit()">
<div class = "container">
<div class="overlay">
<div id="alarm-dialog">
HEKK
<h2>Set alarm at</h2>
<div class="form-group">
<label class="hours">
<input type="datetime-local" pattern="[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}" class="form-control" [(ngModel)]="hours" name="hours" value="0" min="0" required/>
</label>
</div>
<a class="close"></a>
</div>
</div>
<input type="submit" class="btn btn-primary" value="Set Alarm">
</div>
</form>
<tr *ngFor="let hero of heroes">
<td>{{hero.hours}}</td>
</tr>
If you want ui() called when the component loads, you can do it in ngOnInit:
ngOnInit() {
this.ui();
}
Also, declare heroes as a property on your component so it can be bound to the view:
export class RegisterComponent implements OnInit {
heroes: any[]; // Maybe add a type instead of "any"
// etc
}
And fix up your ui method a bit:
ui() {
this.heroes = JSON.parse(localStorage.getItem('users'));
}
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);
});
}
}