Angular 2 - Getter function to split array into chunks of 3 - javascript

Working on an application and the framework I am using has rows and I would like to iterate over an array of devices and input them into rows, 3 devices per row.
I was pointed in the direction of using a getter function to split the array, I am just not sure how to implmenet this in my component.ts file.
Below is the function I am attempting to implement
get deviceRows() {
let arrRows = [];
let deviceTriple = [];
for (let i = 1; i <= this.devices.length; i++) {
deviceTriple.push(this.devices[i - 1]);
if (i % 3 === 0) {
arrRows.push(triple);
deviceTriple= [];
}
}
return arrRows;
}
And this is the file I would like to implement this.
import { Component } from '#angular/core';
import { DeviceService } from '../../services/device.service';
import { Device } from '../../../Device';
#Component({
moduleId: module.id,
selector: 'devices',
templateUrl: 'devices.component.html'
})
export class DevicesComponent {
devices: Device[];
name: string;
constructor(private deviceService:DeviceService) {
this.deviceService.getDevices()
.subscribe(devices => {
this.devices = devices;
});
}
addDevice(event) {
event.preventDefault();
var newDevice = {
name: this.name,
onStatus: false
}
this.deviceService.addDevice(newDevice)
.subscribe(device => {
this.devices.push(device);
this.name = '';
});
}
deleteDevice(id) {
var devices = this.devices;
this.deviceService.deleteDevice(id)
.subscribe(data => {
if(data.n == 1){
for(var i = 0; i < devices.length; i++) {
if(devices[i]._id == id){
devices.splice(i, 1);
}
}
}
});
}
toggleDevice(device){
var updatedStatus = {
_id: device._id,
name: device.name,
onStatus: !device.onStatus
};
this.deviceService.toggleDevice(updatedStatus)
.subscribe(data => {
device.onStatus = !device.onStatus
updatedStatus = {};
});
}
}
Inside the view I would like to use something like this
<div *ngFor="let DeviceTriple of deviceRows">
<div class="uk-child-width-expand#s uk-text-center" uk-grid>
<div *ngFor="let device of DeviceTriple">
<h2 class="uk-h2">{{ device.name }}</h2>
<button class="uk-button uk-button-default" (click)="toggleDevice(device)" [disabled]="device.onStatus">On</button>
<button class="uk-button uk-button-danger" (click)="toggleDevice(device)" [disabled]="!device.onStatus">Off</button>
</div>
</div>
</div>
Below is how it I currently added devices to the DOM.
<div class="uk-child-width-expand#s uk-text-center uk-grid">
<div *ngFor="let device of devices">
<div class="uk-card uk-card-secondary uk-card-hover uk-card-body uk-transform-origin-bottom-right uk-animation-scale-up">
<h2 class="uk-h2">
<a href="api/device/{{device._id}}">
{{ device.name }}
</a>
</h2>
<button class="uk-button uk-button-default" (click)="toggleDevice(device)" [disabled]="device.onStatus">On</button>
<button class="uk-button uk-button-danger" (click)="toggleDevice(device)" [disabled]="!device.onStatus">Off</button>
</div>
</div>
</div>
I am new to Angular and I am not sure this is the most effient way of doing this, I have seen suggestions about using a custom pipe to do this, but I am lost when looking at examples of implementing this.
Any guidance would be greatly received. If I insert the function anywhere outside of the export class DevicesComponent I get the error 'get is not defined'. If I place it after name: string; I get the error cannot get .length of undefined.

Related

main.js:1 ERROR TypeError: Cannot read property 'querySelectorAll' of null

I am using Chartist with angular to create charts. The problem is that my charts don't display when I run the server and I get this error on the console. When I refresh the page, charts show up. How can I solve this?
I printed data on the console to check if they are coming right and they are.
line.component.ts file
import { Component, OnInit, Input, ViewEncapsulation } from '#angular/core';
import { ChartData } from '../chart.data.namespace';
import * as Chartist from 'chartist';
import { NChartist } from '../NChartist.namespace';
import {
IBarChartOptions,
IChartistAnimationOptions,
IChartistData
} from 'chartist';
#Component({
selector: 'app-line',
templateUrl: './line.component.html',
styleUrls: ['./line.component.css'],
encapsulation:ViewEncapsulation.None,
})
export class LineComponent implements OnInit {
chartline: any;
#Input() data: ChartData.LabelValue[];
constructor() {
}
ngOnInit() {
this.createLineChart();
}
createLineChart() {
let linedata: NChartist.Data = new NChartist.Data();
this.prepareLineData(linedata);
const prepared_l_data = {
series: [linedata.series],
labels: linedata.labels,
}
this.chartline = new Chartist.Line('.ct-chart-line', prepared_l_data,{})
console.log(this.chartline);
this.chartline.update();
this.startAnimationForLineChart(this.chartline);
}
prepareLineData(linedata) {
for (let i = 0; i < this.data.length; i++) {
linedata.series[i] = this.data[i].value;
linedata.labels[i] = this.data[i].label;
}
}
startAnimationForLineChart(chart){
let seq: any, delays: any, durations: any;
seq = 0;
delays = 80;
durations = 500;
chart.on('draw', function(data) {
if(data.type === 'line' || data.type === 'area') {
data.element.animate({
d: {
begin: 600,
dur: 700,
from: data.path.clone().scale(1, 0).translate(0, data.chartRect.height()).stringify(),
to: data.path.clone().stringify(),
easing: Chartist.Svg.Easing.easeOutQuint
}
});
} else if(data.type === 'point') {
seq++;
data.element.animate({
opacity: {
begin: seq * delays,
dur: durations,
from: 0,
to: 1,
easing: 'ease'
}
});
}
});
seq = 0;
};
}
line.component.html
<body>
<div class="card card-chart">
<div class="card-header card-header-warning">
<div class="ct-chart-line" id="chartline"></div>
</div>
<div class="card-body">
<h4 class="card-title">Line Chart</h4>
<p class="card-category">
<span class="text-success"><i class="fa fa-long-arrow-up"></i> 55% </span> increase in today
sales.
</p>
</div>
<div class="card-footer">
<div class="stats">
<i class="material-icons">access_time</i> updated 4 minutes ago
</div>
</div>
</div>
</body>
You have to ensure all html is complete when you call createLineChart().
ngOnInit() isn't the best cycle hook to call a queryselector. Try to call it in ngAfterViewInit() method without forgetting to implement AfterViewInit :
export class LineComponent implements OnInit, AfterViewInit
ngAfterViewInit(){
this.createLineChart();
}

How can I access certain element's key in local storage?

I have an array of objects like this and when I click the Remove Favorite button I want to delete the certain element from local storage. I'm deleting from the page with the removeLocal() function but it only deletes from the page, not from local storage. I want to delete it both. I'm generating random number when assigning local storage key. Is there way to access this key and delete the item?
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 *ngIf="closeDiv">
<div style="background-color: rgb(106, 106, 170);" *ngFor="let item of display">
<p>Username: {{item.login}}</p>
<p>Location: {{item.location}}</p>
<p>ID: {{item.id}}</p>
<button (click)="removeLocal(item.id)" class="viewProfileButton">Remove Favorite</button>
</div>
</div>
<button (click)="consoleLog()" class="viewProfileButton">Console Log</button>
<router-outlet></router-outlet>
ts:
#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.randomNumber = Math.floor(Math.random() * 10000);
this.randomString = this.randomNumber.toString();
localStorage.setItem(this.randomString, JSON.stringify(this.user));
this.display = Object.values(localStorage).map((val: any) => JSON.parse(val));
console.log(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);
}
}
}
detectChange(ev: any) {
ev.length > 0 ? (this.closeDiv = false) : (this.closeDiv = true);
}
}
Add the call localStorage.removeItem(key) to your removeLocal function. Granted, you need to store your random keys somewhere, otherwise you will have to integrate this solution to parse through them.
removeLocal(id: any, key: string) {
for (let i = 0; i < this.display.length; i++) {
if (this.display[i].id === id) {
this.display.splice(i, 1);
localStorage.removeItem(key); // here
}
}
}
EDIT: After a conversation in the comments, this solution can be simplified to remove a variable from the function header by storing a storageKey variable within display.
removeLocal(id: any) {
for (let i = 0; i < this.display.length; i++) {
if (this.display[i].id === id) {
localStorage.removeItem(this.display[i].storageKey);
this.display.splice(i, 1);
}
}
}

Passing custom template to stencil component

I'm working on to create a custom component using Stencil to replicate UI-select.
The component will be used like:
let items = [{name:"Abc", age: 10}, {name:"Xyz", age: 10}];
let itemString = JSON.stringify(items);
<dropdown-search placeholder="Select User" item-string='${itemString}'>
</dropdown-search>
Then the component is defined as
import {
Component,
State,
Prop,
Element,
} from '#stencil/core';
#Component({
tag: 'dropdown-search',
})
export class DropdownSearch {
#Element() dropdownEl: HTMLElement;
#State() isOpen: boolean = false;
#State() items: any = [];
#Prop() itemString: string = '';
#Prop() placeholder: string = '';
componentDidLoad() {
try {
this.items = JSON.parse(this.itemString);
} catch(e) {}
}
onClickDropdownHandler = (e: UIEvent) => {
e.preventDefault();
this.toggleDropdown();
}
toggleDropdown = () => {
this.isOpen = !this.isOpen;
if (this.isOpen) {
window.setTimeout(
() => {
this.dropdownEl.querySelector('input').focus();
},
0,
);
}
}
renderOptions = () => {
if (!Array.isArray(this.items) || !this.items.length) {
return null;
}
return this.items.map((item) => (
<li
>
<a href="javascript:" title="{item.name}">
{item.name}
<small class="d-block">age: {item.age}</small>
</a>
</li>
));
}
render() {
let dropdownClassName = (this.isOpen ? 'open' : '');
return (
<form name="myForm">
<div class="form-group">
<div
class={`btn-group dropdown ${dropdownClassName}`}
>
<button
class="btn btn-default dropdown-toggle"
onClick={this.onClickDropdownHandler}
>
{this.placeholder}
</button>
<ul class="dropdown-menu" role="menu">
<li>
<div class="input-group input-group-search">
<input
class="form-control"
type="search"
/>
</div>
</li>
{this.renderOptions()}
</ul>
</div>
</div>
</form>
);
}
}
The items are rendering fine. As as the user can pass a custom array of objects, so I need to customize the options template. So the user can pass it while using the component.
Right now I'm using a static template for the options within the component, like
<a href="javascript:" title="{item.name}">
{item.name}
<small class="d-block">age: {item.age}</small>
</a>
but I need a way to pass this template from where I'm using the template. I can't use slot there as I'm using the same template within all the options that running in a loop.
I am new with stencil but just tried to utilize an #Prop function for this, and it worked:
Component
#Component({
tag: 'dropdown-search',
styleUrl: 'dropdown-search.css',
shadow: true,
})
export class DropdownSearch {
// allow template function to be passed from outside
#Prop template!: (item: SelectItem) => any;
#State items!: SelectItem[] = [{name: 'dog'}, {name: 'cat'}, {name: 'elephant'}];
render() {
return (
<Host>
<p>Before Template</p>
{/* render the custom template here: */}
{this.items.map(item => this.template(item))}
<p>After Template</p>
</Host>
);
}
}
Providing custom template to this component:
class AppComponent {
render() {
// Here we define our custom template to be rendered in MyComponent
const CustomTemplate = (item: SelectItem) => <p key={item.name}>Hi, I am {item.name}</p>;
return (
<div class="app-home">
<dropdown-search template={CustomTemplate} />
</div>
);
}
}
result looks like this:

Ng Bootstrap - how to call modal Component from another component another component?

I am a newbie and learning angular. I have a background of angularjs. I have structure of my app like this.
// src /
// app /
// app-footer
// app-footer.component.css , app-footer-component.html,app-footer.component.ts
// app-header
// app-header.component.css,app-header.component.html,app-header.ts
// home
// home.component.css,home.component.html,home.component.ts
// shared
// modals
// todo
// update-todo.css,update-todo.html,update-todo.ts
I am using ng-bootstrap and with the help of it i am showing a modal on click. My home.component.html looks like this.
<div class="container home-page">
<div class="jumbotron text-center">
<h1>I'm a Todo-aholic
</h1>
</div>
<div id="todo-form" class="row">
<div class="mx-auto col-sm-8 text-center">
<form action="">
<div class="form-group">
<input type="text" name="something" [(ngModel)]="todo" class="form-control input-lg text-center" placeholder="I want to buy a puppy that will love me forever">
<div class="add-button-todo-app">
<button class="btn btn-primary" (click)="pushInTodoArr(todo) " [disabled]="todo =='' ? true : false">ADD</button>
</div>
</div>
</form>
</div>
</div>
<div class="list-of-todos">
<ul>
<li *ngFor="let item of todoArray;let i=index">
<span class="tick">
<i class="fa fa-check" aria-hidden="true"></i>
</span>
{{item}}
<span class="trash" (click)="removeItemInTodoArr(item);">
<i class="fa fa-trash" aria-hidden="true"> </i>
</span>
<span class="trash" (click)="content.openLg(content)">
<i class="fa fa-pencil" aria-hidden="true"> </i>
</span>
</li>
</ul>
</div>
<update-todo></update-todo>
and app.component.ts looks like this.
import { Component, OnInit } from '#angular/core';
import { NgbdModalOptions } from '../shared/modals/todo/update-todo';
#Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {
/* todo array list */
todoArray: String[] = [];
todo: string = "";
constructor() {
}
ngOnInit() {
}
/* Push data in todo array */
pushInTodoArr(value) {
this.todoArray.push(value);
this.todo = '';
}
/* remove item in array */
removeItemInTodoArr(key) {
for (var i = this.todoArray.length - 1; i >= 0; i--) {
if (this.todoArray[i] === key) {
this.todoArray.splice(i, 1);
}
}
}
/* update item in array */
updateItemInTodoArr(key,updatedValue) {
for (var i = this.todoArray.length - 1; i >= 0; i--) {
if (this.todoArray[i] === key) {
this.todoArray[i] = updatedValue;
}
}
}
}
Its basically a todo app. Which adds delete and updates. I want to update the field in modal.
This is my update-todo.ts.
import {Component, ViewEncapsulation} from '#angular/core';
import {NgbModal, ModalDismissReasons} from '#ng-bootstrap/ng-bootstrap';
#Component({
selector: 'update-todo',
templateUrl: './update-todo.html',
encapsulation: ViewEncapsulation.None,
styleUrls: ['./update-todo.css']
})
export class NgbdModalOptions {
closeResult: string;
constructor(private modalService: NgbModal) {}
openLg(content) {
this.modalService.open(content, { size: 'lg' });
}
}
Now I want to open the pop up and pass the value on click update icon. i am calling this function (click)="content.openLg(content)" but i get Cannot read property 'openLg' of undefined.
Will some one please point me to right direction. I am stumbling between ng-bootstrap,ngx-bootstrap and ng2-bootstrap. But i want to do it with ng-bootstrap.
This is how I did it in my app:
Import:
import { NgbModal, NgbModalOptions } from '#ng-bootstrap/ng-bootstrap';
constructor:
constructor(
private _modalService: NgbModal
) { }
Call:
onEdit(car: ICars) {
const modalRef = this._modalService.open(RentACarDetailComponent, { size: 'lg' });
modalRef.componentInstance.car = car;
modalRef.componentInstance.type = 'edit';
modalRef.componentInstance.pageTitle = 'Edit vehicle';
modalRef.result.then((result) => {
if (result) {
let answer: number;
this._rentACarService.editVehicle(result, 2)
.takeUntil(this.ngUnsubscribe)
.subscribe(
result => { answer = result; },
error => { this.errorMessage = <any>error; },
() => {answer > 0) {
this._helperService.showInfo('You have succesfully editet vehicle.', 5);
this.ngOnInit();
}
});
}
},
(reason) => { });
}
car, type and PageTitle are set as Inputs in modal component

Angular2 how to remove 1 specific element from list with mouseclick

I am having a small issue in my code. Ive created an weather application using angular 2 and it works fine. Though do i have a small problem when clicking "delete this city button" where it delets only the last city in and not the one I wanted, how can i solve this? Here is my code:
clearWeatherItems() {
for(WEATHER_ITEMS, function(i)){
var city = WEATHER_ITEMS[i];
if(city == WEATHER_ITEMS[i]) {
city.splice(i, 1);
return false;
}
}
} }
Ive also tried doing it this way but still the same problem occurs:
clearWeatherItems() {
WEATHER_ITEMS.splice(-1);
}
here is my weather-item.component.ts:
import { Component, Input } from 'angular2/core';
import { WeatherItem } from "./weather-Item";
#Component({
selector: 'weather-item',
template: `
<div id="clear">
</div>
<article class="weather-element">
<div class="col-1">
<h3>{{ weatherItem.cityName }}</h3>
<p class="info">{{ weatherItem.description }}</p>
</div>
<div class="col-2">
<span class="temperature">{{ weatherItem.temprature }}°C</span>
</div>
<button class="delete" (click)="clearWeatherItems($event, weatherItem)">X</button>
</article>
`,
styleUrls: ['src/css/weather-item.css'],
// inputs: ['weatherItem: item']
})
export class WeatherItemComponent {
#Input('item') weatherItem: WeatherItem;
clearWeatherItems() {
// event.stopPropagation();
this.weatherItem.clearWeatherItems();
}
}
my weather.data.ts:
import { WeatherItem } from "./weather-Item";
export const WEATHER_ITEMS: WeatherItem[] = [];
and here is my weather-item.ts:
import { WEATHER_ITEMS } from "./weather.data";
export class WeatherItem {
constructor(public cityName, public description: string, public temprature: number) {
}
clearWeatherItems(item) {
WEATHER_ITEMS.splice(-1);
}
}
Somebody knows what to do?
Best regards from a programming noob :P
Just declare a method on ts file
clearWeatherItem(item:any){
let index: number = this.WEATHER_ITEMS.indexOf(item);
if (index !== -1) {
this.data.splice(index, 1);
}
}
call this method by passing the item to be removed from the HTML template

Categories

Resources