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

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

Related

Hiding UI components when a condition is met

When a user clicks on one of the three button I need to apply the Class active (By default the first button is in 'active' status).
How can I do this ?
<button class="nav-link active" > one </button>
<button class="nav-link " > two </button>
<button class="nav-link " > three</button>
Here is a Stackblitz example about how you could do, and here is the code :
html:
<div *ngFor="let btn of buttonsData">
<button class="nav-link" (click)="setActive(btn.id)">{{btn.label}}</button>
</div>
ts:
import {
AfterViewInit,
Component,
ElementRef
} from "#angular/core";
#Component({
selector: "my-app",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent implements AfterViewInit {
constructor(private _elem: ElementRef) {}
buttonsData = [
{ id: 0, label: "Link 1" },
{ id: 1, label: "Link 2" },
{ id: 2, label: "Link 3" }
];
buttons;
ngAfterViewInit() {
this.buttons = this._elem.nativeElement.querySelectorAll(".nav-link");
this.setActive(0);
}
setActive(id) {
console.log(this.buttons[id]);
this.buttons.forEach(btn => btn.classList.remove('active'));
this.buttons[id].classList.add("active");
}
}
css:
.active{
background-color: red;
}
With this you just need to set a new item in buttonsData and everything is dynamic. Much better than a hard coded example I think.
Yould even remove the id value in buttonsData and simply handle an array of string and use the index to set the active class.
You can use the following simple implementation.
Your html:
<button class="nav-link" [class.active]="isActiveFirst" (click)="isActiveFirst = true; isActiveSecond = false; isActiveThird = false"> one </button>
<button class="nav-link" [class.active]="isActiveSecond" (click)="isActiveFirst = false; isActiveSecond = true; isActiveThird = false"> two </button>
<button class="nav-link" [class.active]="isActiveThird" (click)="isActiveFirst = false; isActiveSecond = false; isActiveThird = true"> three</button>
Your ts:
export class AppComponent {
isActiveFirst:boolean = true;
isActiveSecond:boolean = false;
isActiveThird:boolean = false;
}

Angular display Filtering Incorrectly

When a user selects a tag, I want to display only the blogs containing that tag.
For example when a user selects the '#C#' tag only posts with this tag will be displayed.
My set up is as follows: I have an array of blogs which contain post tags which contain tags:
export interface IBlog
{
id: number
title: string
content: string
subscription: number
time: string
created: Date;
imageUrl: string
coverImage: string
image: string
likes: number
body: string
comments?:{
message: string
handle: string,
city: string,
country: string
},
articleLinkUrl?: string,
sessions: ISession[],
mainComments: IComments[],
postTags: IPostTags[]
}
export interface ISession
{
id: number,
name: string,
presenter: string,
duration: number,
level: string,
abstract: string,
voters: string[]
}
export interface IComments
{
id: number,
message: string
}
export interface IPostTags
{
id: number,
tag: ITag[]
}
export interface ITag
{
id: number,
name: string
}
I have a blog-list.component.ts:
import {Component, OnInit} from '#angular/core'
import { BlogService} from './shared/blog.service';
import { ActivatedRoute } from '#angular/router';
import { IBlog } from './shared';
#Component({
template: `
<div>
<h1>Upcoming Blogs</h1>
<hr/>
<div class= "btn-group btn-group-sm">
<button class="btn btn-default" [class.active] = "filterBy==='all'" (click) = "filterBy='all'">All</button>
<button class="btn btn-default" [class.active] = "filterBy==='c#'" (click) = "filterBy='#C#'">C#</button>
<button class="btn btn-default" [class.active] = "filterBy==='angular'" (click) = "filterBy='#Angular'">Angular</button>
<button class="btn btn-default" [class.active] = "filterBy==='netcore'" (click) = "filterBy='#NetCore'">.NET Core</button>
</div>
<div class ="row">
<blog-thumbnail [filterBy]="filterBy" [blogs] = "blogs"></blog-thumbnail>
<div class='row' style="margin-bottom: 10px;">
<div class="col-md-2">
<h3 style="margin:0">Sort by Tag</h3>
</div>
<div class="col-md-7">
<div class= "btn-group btn-group-sm" style="margin-right: 20px; margin-left: 20px;">
<button class="btn btn-default" [class.active] = "sortBy==='name'" (click) = "sortBy='name'">By Name</button>
<button class="btn btn-default" [class.active] = "sortBy==='votes'" (click) = "sortBy='votes'">By Votes</button>
</div>
</div>
</div>
</div>
</div>`
})
export class BlogListComponent implements OnInit{
blogs:IBlog[]
filterBy: string = 'all';
constructor(private blogService: BlogService, private route: ActivatedRoute){
}
ngOnInit(){
this.blogs = this.route.snapshot.data['blogs']
}
}
This displays a blog-thumbnail component
import {Component, Input, EventEmitter, Output, OnChanges} from '#angular/core'
import { forEach } from '#angular/router/src/utils/collection';
import { IBlog, IPostTags } from './shared';
#Component ({
selector: 'blog-thumbnail',
template: `
<div class="row" *ngFor="let blog of blogsSession">
<div [routerLink] = "['/blogs', blog.id]" class="well hoverwell thumbnail">
<img src={{blog?.coverImage}}/>
<h2>{{blog?.title | uppercase}}</h2>
<div>{{blog?.created | date}}</div>
<div>Content: {{blog?.content}}</div>
<div>Tags: {{blog?.content}}</div>
<div well-title *ngIf="blog?.mainComments">
{{blog?.likes}} Reactions
<i *ngIf="blog?.likes >= 1" class="glyphicon glyphicon-heart-empty" style="color:red"></i>
{{blog.mainComments.length}} Comments
<i *ngIf="blog.mainComments.length >= 1" class="glyphicon glyphicon-comment" ></i>
</div>
<div well-title *ngIf="blog?.postTags">
{{blog.postTags.length}} Post Tags
<i *ngIf="blog.postTags.length >= 1" class="glyphicon glyphicon-comment" ></i>
</div>
</div>
`,
styles:[
`
.thumbnail {min-height: 210px;}
.pad-left {margin-left: 10px;}
.well div { color: #bbb;}
`]
})
export class BlogThumbnailComponent implements OnChanges {
#Input() blog: IBlog
#Input() filterBy: string;
#Input() blogs: IBlog[];
blogsSession: IBlog[] = [];
getStartTimeStyle(): any{
if (this.blog && this.blog.time === '7:30pm')
return{color: '#003300', 'font-weight' : 'bold'}
return{};
}
ngOnChanges(){
if(this.blogs){
this.filterSessions(this.filterBy);
//this.sortBy === 'name' ? null : null;
}
}
filterSessions(filter){
if(filter === 'all'){
this.blogsSession= this.blogs.slice(0);
}
else
{
this.blogsSession = this.blogs.filter(blog => {
return blog.postTags.filter( postTag =>{
postTag.tag.name === filter
})
})
this commented out code works but it is not what i want
// this.blogsSession = this.blogs.filter(blog => {
// return blog.postTags.length < 2;
//})
}
}
}
The following section off code is where I am having my problem. It does not work:
this.blogsSession = this.blogs.filter(blog => {
return blog.postTags.filter( postTag =>{
postTag.tag.name === filter
})
})
I have spent a large amount of time on this. Would anybody be able to explain what I am doing wrong here?
The function used in the filter() method should return true or false (or actually a value that coerces to true or false). Your function returns an array; you are filtering blog.postTags.
Try this instead:
this.blogsSession = this.blogs.filter(blog => {
return blog.postTags.some(postTag => postTag.tag.name === filter)
})
The some() method will return true if at least one of the items in the array meet the conditions used in the function.
Why do you have 2 filter operators? I think this:
this.blogsSession = this.blogs.filterpostTags.filter( postTag =>{
postTag.tag.name.toLocaleLowerCase() === filter.toLocaleLowerCase()
})
should be enough.

How to do a specific auto refresh in angular

I have 3 files, model.ts, modal.html and modal.ts. I would like to do the auto refresh only when the modal is open and stop it when it is closed. my modal shows me information continuously.
modal.htlm :
<button class="btn btn-success" style="padding: 0.1rem 0.5rem; float: left; font-size: 12px;"(click)="viewFile(model)">Voir</button>
<div class="modal" id="mdl" tabindex="-1" role="dialog" aria-hidden="true"
[ngStyle]="{'display': mdlIsOpen ? 'block' : 'none', 'opacity': 1}">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLongTitle">Hello</h5>
<button type="button" class="close" aria-label="Close" (click)="openContent(false)">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<div style="white-space: pre-wrap" innerHTML="{{reponse}}"></div>
</div>
<div class="modal-footer" align="right">
<button type="button" id="btnOK" class="btn btn-success"
(click)="openContent(false)">close</button>
</div>
</div>
</div>
</div>
modal.ts:
export class ModalComponent implements OnInit {
private updateSubscription: Subscription;
reponse: String;
model: Model[];
constructor(private modelService: ModelService) {
}
ngOnInit() {
}
refreshAuto(){
console.log(this.mdlIsOpen);
if(this.mdlIsOpen === true){
this.updateSubscription = interval(10000).subscribe(
(val) => {
this.refresh();
this.refreshAuto();
});
}
}
refreshFile(): void {
this.viewFile(this.model);
}
refresh(): void {
this.view(this.model);
}
openContent(open: boolean): void {
console.log(open);
this.mdlIsOpen = open;
}
viewFile(model: Model): void {
this.modelService.viewFile(model)
.subscribe(res => {
this.openContent(true);
this.reponse = res;
//console.log(res);
this.refreshAuto();
//clearInterval(this.interval);
}, err => {
console.log("here for view");
console.log(err);
alert (JSON.parse(err.error).message);
}
);
}
view(model: Model): void {
this.modelService.viewFile(model)
.subscribe(res => {
this.reponse = res;
//console.log(res);
}, err => {
console.log("here for view");
console.log(err);
alert (JSON.parse(err.error).message);
}
);
}
}
model.ts:
export class Model {
Id: number;
mode: number;
App: string;
name: string;
}
but its not working well, when I close the modal and mdlIsOpen goes to false, the auto refresh continues.
Step 1) Do not continually call this method
refreshAuto(){
console.log(this.mdlIsOpen);
if(this.mdlIsOpen === true){
this.updateSubscription = interval(10000).subscribe(
(val) => {
this.refresh();
this.refreshAuto();
});
}
}
Why? Because interval never stops on it's own. Each time this is called it sets up another interval observable.
Step 2) "I would like to do the auto refresh only when the modal is open" This code is ambiguous
openContent(open: boolean): void {
console.log(open);
this.mdlIsOpen = open;
}
We can't tell from the code who would ever want to openConent and set the open value to false. OpenContent should probably always be to open the dialog.
Step 3) Determine when to change this.mdlIsOpen to false. One good place, for sure is in ngOnDestroy. You may also stop the interval timer there too.
Step 4) Provide a method isOpen(open:boolean) which allows an explicit self explaining entry into your component to open or close the dialog.
Step 5) MatDialog does have a close option
Example
import { MatDialog, MatDialogConfig } from "#angular/material/dialog";
#Component({
selector: "app-root",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"],
})
export class AppComponent {
constructor( private dialog: MatDialog) {}
// The HTML button click handler
onButtonClicked($event) {
let httpClient: HttpClient = new HttpClient(this.httpHandler);
// Causes a CORS error
httpClient.get("http://www.google.com").subscribe(
(response) => {
console.log(response);
},
(error) => {
let dc = new MatDialogConfig();
dc.autoFocus = true;
// Important part
dc.data = error;
this.dialog.open(ErrorHandlerComponent, dc);
}
);
}
And ErrorHandlerComponent
export class ErrorHandlerComponent implements OnInit, AfterViewInit {
error: HttpErrorResponse;
constructor(
// Must inject MAT_DIALOG_DATA
#Inject(MAT_DIALOG_DATA)
public data: any,
private ref: ElementRef,
private HtmlElementService: HtmlElementService
) {
// Html binds to this.error
this.error = data;
}
The Injected Component's HTML
<h1 mat-dialog-title>{{ error.statusText }}</h1>
<div mat-dialog-content>
<p>Message</p>
{{ error.message }}
</div>
<div mat-dialog-actions>
<button class="mat-raised-button" mat-dialog-close>Close</button>
<button class="mat-raised-button mat-primary" (click)="save()">Copy</button>
</div>
The result:
In your modal close method unsubscribe the interval observable.
openContent(open: boolean): void {
console.log(open);
this.mdlIsOpen = open;
this.updateSubscription.unsubscribe();
}

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

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

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.

Categories

Resources