Get a reference to a element nested inside a modal - ng-bootstrap

I'm trying to get a reference to an element nested inside a modal. While using #ViewChild works for the modal, its not working for any nested elements. eg: the datePicker in the code below. Working demo here: https://stackblitz.com/edit/angular-s8dtmm-8gqgus (The 2nd console for datePicker is undefined)
export class AppComponent {
#ViewChild('content') modal: ElementRef;
#ViewChild('dp') datePicker: ElementRef;
constructor(private modalService: NgbModal) {}
open() {
this.modalService.open(this.modal);
console.log('modal', !!this.modal); // ref to #content
console.log('dp', this.datePicker); // undefined
}
}
Template:
<ng-template #content let-modal>
<input ngbDatepicker #dp="ngbDatepicker">
<button class="calendar" (click)="dp.toggle()">Date picker</button>
</ng-template>
<button(click)="open()">Open modal</button>

If you can modify your example so that the modal content is a separate component (i.e. based on this example rather than this one) then you should be able to access the datePicker component in the open() method. I have created a launch-modal.component which defines the "Open" button and logs out the value of dp when the modal is opened:
launch-modal.component.html
<button class="btn btn-outline-primary" (click)="open()">Open modal</button>
launch-modal.component.ts
import { Component, ElementRef } from '#angular/core';
import { NgbActiveModal, NgbModal } from '#ng-bootstrap/ng-bootstrap';
import { ModalComponent } from './modal.component';
#Component({
selector: 'launch-modal-component',
templateUrl: './launch-modal.component.html'
})
export class LaunchModalComponent {
constructor(private modalService: NgbModal) {}
open() {
const modalRef = this.modalService.open(ModalComponent);
console.log('dp', modalRef.componentInstance.datePicker);
}
}
I've then defined a modal.component.ts that defines the modal content (this is based on the app.module.html in your question, and defines a ViewChild for the datePicker):
modal.component.ts
import { Component, ElementRef, ViewChild } from '#angular/core';
import { NgbActiveModal } from '#ng-bootstrap/ng-bootstrap';
#Component({
selector: 'modal-component',
template: `
<button type="button" class="close" aria-label="Close" (click)="activeModal.dismiss('Cross click')">
<span aria-hidden="true">×</span>
</button>
<input ngbDatepicker #dp="ngbDatepicker">
<button class="btn btn-outline-primary calendar" (click)="dp.toggle()" type="button">Date picker</button>
`
})
export class ModalComponent {
#ViewChild('dp') datePicker: ElementRef;
constructor(public activeModal: NgbActiveModal) {}
}
The output of the console when the modal is opened is:
Please see this Stackblitz for a working demo.

Related

How to customize checkbox style with react-bootstrap?

I'm using react-bootstrap. I'm trying to style a custom checkbox since it seems it makes it possible. Tho, it doesn't work. I'm doing what the documentation tells me.
This is my code.
import * as React from "react";
import { t as typy } from 'typy';
import _ from 'lodash';
import { Form, FormCheck } from "react-bootstrap";
import { ErrorMessage } from "formik";
export type Props = {
isChecked: Boolean,
changeHandler: Function
}
export const Checkbox = ({
isChecked,
changeHandler
}: Props) => {
return (
<Form>
{['checkbox', 'radio'].map((type) => (
<div key={`custom-${type}`} className="mb-3">
<Form.Check
custom
type={type}
id={`custom-${type}`}
label={`Check this custom ${type}`}
/>
<Form.Check
custom
disabled
type={type}
label={`disabled ${type}`}
id={`disabled-custom-${type}`}
/>
</div>
))}
</Form>
);
};
export default Checkbox;
This is my css. I just want to see if it applies the style:
#custom-checkbox {
background-color: red;
width: 10rem;
}
You can use .custom-control-input and .custom-control-label classes to apply custom style to the custom checkbox in react-bootstrap.

Hide toolbar component when it isn't on the top of the page

I have a toolbar like this Its background is transparent. But when i scroll down, this look like
How can i hide this component when the page scroll down and show it when scroll up to the top of the page?
My English is not good. Sorry about this.
EDIT :
I use mat-toolbar
<mat-toolbar color="primary">
<button mat-button routerLink="/" [ngStyle]="{'color': colorStyle === 'WHITE' ? 'white' : 'black'}">
<mat-icon>home</mat-icon>
{{ 'PAGE.HOME' | translate}}</button>
<!-- This fills the remaining space of the current row -->
<span class="fill-remaining-space"></span>
<div fxLayout="row" fxShow="false" fxShow.gt-sm [ngStyle]="{'color': colorStyle === 'WHITE' ? 'white' : 'black'}">
<button mat-button routerLink="['/home']">{{ 'PAGE.HOME' | translate}}</button>
<button mat-button routerLink="['/home']">{{ 'PAGE.D9' | translate}}</button>
<button mat-button routerLink="['/home']">{{ 'PAGE.DThuDuc' | translate}}</button>
<button mat-button routerLink="['/home']">{{ 'PAGE.MORE' | translate}}</button>
<button mat-button [routerLink]="['/add']">{{ 'PAGE.ADD' | translate}}</button>
<button mat-button [routerLink]="['/login']" *ngIf="!loginStatus">{{ 'PAGE.LOGIN' | translate}}</button>
<button mat-button [routerLink]="['/login']" *ngIf="loginStatus">{{ 'PAGE.LOGOUT' | translate}}</button>
<button mat-button [routerLink]="['/show-map']" [queryParams]="{ lat: data.lat, lng: data.lng}">{{ 'PAGE.OVERVIEW' | translate}}</button>
</div>
<button mat-button [mat-menu-trigger-for]="menu" fxHide="false" fxHide.gt-sm>
<mat-icon>menu</mat-icon>
</button>
</mat-toolbar>
.mat-toolbar {
position: fixed;
z-index: 999;
}
Just you use a #HostListener window:scroll
#HostListener("window:scroll", [])
onWindowScroll() {
let number = window.pageYOffset || 0;
console.log(number);
}
Some authors consider a bad practice reference a window directy. Brian Love propouse a "window-provider": see http://brianflove.com/2018/01/11/angular-window-provider/
The Brian Love propouse solution:
****It's a Copy and Paste of the referenced article ********
import { isPlatformBrowser } from "#angular/common";
import { ClassProvider, FactoryProvider, InjectionToken, PLATFORM_ID } from '#angular/core';
/* Create a new injection token for injecting the window into a component. */
export const WINDOW = new InjectionToken('WindowToken');
/* Define abstract class for obtaining reference to the global window object. */
export abstract class WindowRef {
get nativeWindow(): Window | Object {
throw new Error('Not implemented.');
}
}
/* Define class that implements the abstract class and returns the native window object. */
export class BrowserWindowRef extends WindowRef {
constructor() {
super();
}
get nativeWindow(): Window | Object {
return window;
}
}
/* Create an factory function that returns the native window object. */
export function windowFactory(browserWindowRef: BrowserWindowRef, platformId: Object): Window | Object {
if (isPlatformBrowser(platformId)) {
return browserWindowRef.nativeWindow;
}
return new Object();
}
/* Create a injectable provider for the WindowRef token that uses the BrowserWindowRef class. */
const browserWindowProvider: ClassProvider = {
provide: WindowRef,
useClass: BrowserWindowRef
};
/* Create an injectable provider that uses the windowFactory function for returning the native window object. */
const windowProvider: FactoryProvider = {
provide: WINDOW,
useFactory: windowFactory,
deps: [ WindowRef, PLATFORM_ID ]
};
/* Create an array of providers. */
export const WINDOW_PROVIDERS = [
browserWindowProvider,
windowProvider
];
In constructor of the component
constructor(#Inject(WINDOW) private window: Window)
In Module
#NgModule({
declarations: [..]
imports: [..]
providers: [WINDOW_PROVIDERS,...],
})
In style add
Position:fixed;
z-index:1000;
give the style to main div of the header.
use (window:scroll)="eventToBeCalled()" in the body tag
assign an id to toolbar:
<mat-toolbar color="primary" id="matTB">
and in typescript
eventToBeCalled(): void {
const matToolBar = document.getElementById('matTB');
if(window.scrollY > 104)
{
matToolBar.setAttribute('style', 'display:none');
} else {
matToolBar.removeAttribute('style');
}
}

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 Inheritance components

I have a basewindow.component which will be the base component for all my components. This basewindow.component will be having buttons like save, delete etc and while clicking "New" button I would like to call basewindow function ufbNew() after executing it should execute parent window function ufNew(). Please check my code and help me whether I'm doing it correctly. I'm able to call base function but parent not
//basewindow.component///
import { Component } from '#angular/core';
#Component({
selector: 'basewindow',
templateUrl: './Basewindow.component.html',
styleUrls: ['./Basewindow.component.css']
})
export class BasewindowComponent {
/////////////////////// User Base Functions ///////////////////
ufbNew() {
this.ufNew();
}
ufbSave() {
this.ufSave();
}
/////////////////////// User Functions for parent ///////////////////
ufNew() {
alert("I m In Base ufbNew")
}
ufSave() {
}
}
//// Basewindow.component.html
<div class="container">
<h1>Hero Form</h1>
<form>
<div class="form-group">
<button (click)="ufbNew()">New</button>
<button (click)="ufbSave()">Save</button>
</div>
</form>
</div>
/////////////////////////// AccountsCategory.Component (Parent 1) ////
import { Component } from '#angular/core';
import { BasewindowComponent } from '../base/basewindow.component';
#Component({
selector: 'app',
templateUrl: './AccountsCategory.component.html'
})
export class AccountsCategory extends BasewindowComponent {
/////////////////////// User Functions for parent ///////////////////
ufNew() {
alert("I m in Acounts category (parent)")
}
ufSave() {
}
}
//////////// AccountsCategory.component.html /////////////
<basewindow> </basewindow>
my purpose is to reuse base component objects , functions and override from child if requires.
please see test application in plnkr
https://plnkr.co/edit/bVxt4GjNXwIg7pDbR4sE?p=preview
Use this
abstract class Animal {
//OTHER STUFF
abstract ufSave(): void;
//OTHER STUFF
}

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