Angular 5, Typescript 2.7.1
I can't seem to get the checkbox to be checked when returning a boolean, I've tried, item.check returns either true or false.
<tr class="even" *ngFor="let item of rows">
<input value="{{item.check}}" type="checkbox" checked="item.check">
The checkbox is always checked when checked is written inside input. And it does not get unchecked when checked="false".
Is there a better way to do it with Angular features instead? like ngModel or ngIf???
Solution
<input type="checkbox" [checked]="item.check == 'true'">
try:
[checked]="item.checked"
check out: How to Deal with Different Form Controls in Angular
You can use this:
<input type="checkbox" [checked]="record.status" (change)="changeStatus(record.id,$event)">
Here, record is the model for current row and status is boolean value.
When you have a copy of an object the [checked] attribute might not work, in that case, you can use (change) in this way:
<input type="checkbox" [checked]="item.selected" (change)="item.selected = !item.selected">
Hope this will help somebody to develop custom checkbox component with custom styles. This solution can use with forms too.
HTML
<label class="lbl">
<input #inputEl type="checkbox" [name]="label" [(ngModel)]="isChecked" (change)="onChange(inputEl.checked)"
*ngIf="isChecked" checked>
<input #inputEl type="checkbox" [name]="label" [(ngModel)]="isChecked" (change)="onChange(inputEl.checked)"
*ngIf="!isChecked" >
<span class="chk-box {{isChecked ? 'chk':''}}"></span>
<span class="lbl-txt" *ngIf="label" >{{label}}</span>
</label>
checkbox.component.ts
import { Component, Input, EventEmitter, Output, forwardRef, HostListener } from '#angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '#angular/forms';
const noop = () => {
};
export const CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CheckboxComponent),
multi: true
};
/** Custom check box */
#Component({
selector: 'app-checkbox',
templateUrl: './checkbox.component.html',
styleUrls: ['./checkbox.component.scss'],
providers: [CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR]
})
export class CheckboxComponent implements ControlValueAccessor {
#Input() label: string;
#Input() isChecked = false;
#Input() disabled = false;
#Output() getChange = new EventEmitter();
#Input() className: string;
// get accessor
get value(): any {
return this.isChecked;
}
// set accessor including call the onchange callback
set value(value: any) {
this.isChecked = value;
}
private onTouchedCallback: () => void = noop;
private onChangeCallback: (_: any) => void = noop;
writeValue(value: any): void {
if (value !== this.isChecked) {
this.isChecked = value;
}
}
onChange(isChecked) {
this.value = isChecked;
this.getChange.emit(this.isChecked);
this.onChangeCallback(this.value);
}
// From ControlValueAccessor interface
registerOnChange(fn: any) {
this.onChangeCallback = fn;
}
// From ControlValueAccessor interface
registerOnTouched(fn: any) {
this.onTouchedCallback = fn;
}
setDisabledState?(isDisabled: boolean): void {
}
}
checkbox.component.scss
#import "../../../assets/scss/_variables";
/* CHECKBOX */
.lbl {
font-size: 12px;
color: #282828;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
cursor: pointer;
&.checked {
font-weight: 600;
}
&.focus {
.chk-box{
border: 1px solid #a8a8a8;
&.chk{
border: none;
}
}
}
input {
display: none;
}
/* checkbox icon */
.chk-box {
display: block;
min-width: 15px;
min-height: 15px;
background: url('/assets/i/checkbox-not-selected.svg');
background-size: 15px 15px;
margin-right: 10px;
}
input:checked+.chk-box {
background: url('/assets/i/checkbox-selected.svg');
background-size: 15px 15px;
}
.lbl-txt {
margin-top: 0px;
}
}
Usage
Outside forms
<app-checkbox [label]="'Example'" [isChecked]="true"></app-checkbox>
Inside forms
<app-checkbox [label]="'Type 0'" formControlName="Type1"></app-checkbox>
Here is my answer,
In row.model.ts
export interface Row {
otherProperty : type;
checked : bool;
otherProperty : type;
...
}
In .html
<tr class="even" *ngFor="let item of rows">
<input [checked]="item.checked" type="checkbox">
</tr>
In .ts
rows : Row[] = [];
update the rows in component.ts
Work with checkboxes using observables
You could even choose to use a behaviourSubject to utilize the power of observables so you can start a certain chain of reaction starting at the isChecked$ observable.
In your component.ts:
public isChecked$ = new BehaviorSubject(false);
toggleChecked() {
this.isChecked$.next(!this.isChecked$.value)
}
In your template
<input type="checkbox" [checked]="isChecked$ | async" (change)="toggleChecked()">
[checked]= "isChecked"
This isChecked will be your varaible of type boolean in component.ts file i.e
if you setb isChecked variable to true in component.ts ts checkbox will be checked and vice versa.
<input type="checkbox" [checked]="item.checked == true">
Related
HTML
<ion-app>
<ion-content>
<div #scrolledToElement class="second-block" [ngClass]="flag ? 'red' : 'green' "></div>
</ion-content>
</ion-app>
CSS
.second-block {
margin-bottom: 500px;
height: 250px;
width: 100%;
}
.red {
background: red;
}
.green {
background: green;
}
TS
import { Component, VERSION, HostListener, ElementRef, ViewChild } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent {
name = 'Ionic 6.2 Angular ' + VERSION.major;
constructor() {}
flag = false;
#ViewChild("scrolledToElement", { static: false })
scrolledToElement: ElementRef;
#HostListener("window:scroll", ["$event"])
onScroll(event) {
if (window.scrollY > this.scrolledToElement.nativeElement.offsetTop) {
this.flag = true;
console.log("flag", this.flag);
}
else
{
this.flag = false;
}
}
}
How to change the class on scroll?
I want a solution for the latest Angular version
stackblitz : https://stackblitz.com/edit/ionic6-angular13-wsjtit?file=src%2Fapp%2Fapp.component.html
Any solution, please?
Working URL :
https://stackblitz.com/edit/angular-ivy-xhb4yw?file=src%2Fapp%2Fapp.component.ts,src%2Fapp%2Fapp.component.html,src%2Fapp%2Fapp.component.css,src%2Findex.html,src%2Fapp%2Fhello.component.ts,src%2Fapp%2Fapp.module.ts
#HostListener('window:scroll', ['$event']) works fine in the web browser. As you are using ionic you need to use their custom events provided in the documentation for ion-content.
<ion-content [scrollEvents]="true"
(ionScrollStart)="logScrollStart($event)"
(ionScroll)="logScrolling($event)"
(ionScrollEnd)="logScrollEnd($event)">
</ion-content>
Implement above methods in app.component.ts file. You can define your change class logic logScrolling method. Instead of window.scrollY use event.detail.scrollTop from the event provided by ionScroll.
logScrollStart(event) {
console.log("logScrollStart : When Scroll Starts", event);
}
logScrolling(event) {
console.log("logScrolling : When Scrolling", window.scrollY);
console.log("Offset", this.scrolledToElement.nativeElement.offsetTop);
if (event.detail.scrollTop > this.scrolledToElement.nativeElement.offsetTop) {
this.flag = true;
console.log('flag', this.flag);
} else {
this.flag = false;
}
}
logScrollEnd(event) {
console.log("logScrollEnd : When Scroll Ends", event);
}
Here is the forked stackblitz repository.
How to change this template driven form to Reactive form? I want to change this template driven form to reactive form. What changes shall I do to change it to Reactive form? The code is complete no other modification is required? Can someone please help me with this, I am attaching all the required codes. What things we should keep in mind while changing to reactive form, also why reactive form is preferred over template driven form?
HTML code
<html>
<body>
<nav>
<div class = "navbar-header">
Image Change on clicking Enter in TextBox
<input class = "col" type = "text" readonly value ="{{imgNumber}}/3 {{cityImageId}}">
</div>
</nav>
<div class = "textarea" contenteditable #scrollDiv [scrollTop]="scrollDiv.scrollHeight" wrap="hard">
<img contenteditable="false" [src]="imagePath" width = "1090px" height = "440"/>
</div>
<form>
<div>
<input type = "text" class="col2" [(ngModel)]="pincode" (keyup.enter)="generatecityDetailArray()" maxlength="6" [ngModelOptions]="{standalone: true}"/>
</div>
</form>
<div>
<input type = "label" class = "col3" value = "Pin Code" readonly />
</div>
</body>
</html>
CSS code
nav {
background-color:black;
border : 0;
}
.navbar-header{
text-align: center;
}
.navbar-header{
color:white;
}
.col{
margin-left: 950px;
text-align: right;
border : 0;
background-color:rgb(160,0,0);
}
.textarea{
overflow: scroll;
height: 400px;
width:1090px;
background-color:white;
margin-left: 100px;
margin-top: 50px;
object-fit: none;
object-position: 1000px 200px;
}
.col2{
width:230px;
height: 30px;
margin-top: 20px;
margin-left: 950px;
}
.col3{
width:230px;
height: 30px;
margin-top: 20px;
margin-left: 950px;
}
TypeScript Code
import { Component, OnInit } from '#angular/core';
import { CityClassificationService } from 'src/app/service/city-classification.service';
#Component({
selector: 'app-city-classification',
templateUrl: './city-classification.component.html',
styleUrls: ['./city-classification.component.css']
})
export class CityClassificationComponent implements OnInit {
imgNumber : number =-1;
cityImageId : number = -1;
imagePath : string ='';
pincode:string='';
curImageNumber:number=0;
imageDetails:{imageName:string,cityImageId:number,imgNumber:number}[]=[];
cityDetailArray :{ pincode : string; cityImageId : number; imgNumber:number;}[] = [];
constructor(private cityService:CityClassificationService) { }
ngOnInit(): void {
this.imageDetails=this.cityService.getImageObject();
this.getNextImage(this.imageDetails[this.curImageNumber]);
}
getNextImage(imageObj:{imageName:string,cityImageId:number,imgNumber:number}):void{
this.imgNumber= imageObj.imgNumber;this.imagePath=`assets/images/${imageObj.imageName}.png`;this.cityImageId=imageObj.cityImageId;
}
generatecityDetailArray():void{
if(this.pincode=='' || this.pincode.toString().length>6){alert('Enter Valid PinCode');return;};
this.cityDetailArray.push(
{pincode:this.pincode, cityImageId:this.cityImageId, imgNumber:this.imgNumber}
);
this.pincode='';
this.curImageNumber++;
if(this.curImageNumber==this.imageDetails.length){this.submitForm();return;}
this.getNextImage(this.imageDetails[this.curImageNumber]);
}
submitForm(){
//TODO here....
this.cityService.getAPi(this.cityDetailArray);
}
}
Model class
export class CityClassification{
pinCode : string;
cityImageId : number;
imgNumber:number;
constructor(pinCode : string, cityImageId : number,imgNumber:number){
this.pinCode = pinCode;
this.cityImageId = cityImageId;
this.imgNumber = imgNumber;
}
}
Service class
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root'
})
export class CityClassificationService {
imageDetails:{imageName:string,cityImageId:number,imgNumber:number}[]=[
{imageName:'Ghazipur1',cityImageId:101,imgNumber:1},
{imageName:'Nashik2',cityImageId:102,imgNumber:2},
{imageName:'Noida3',cityImageId:103,imgNumber:3}
]
constructor() { }
getAPi(formBody:any){
//API TO SUBMIT FORM.....
console.log('INSIDE SERvice.......',formBody);
}
getImageObject():any{
//API HERE TO GET IMAGE
return this.imageDetails;
}
}
here is a simple code snippet for you,
Lets say you have component name as AppComponent,
import { Component } from '#angular/core';
import {
FormBuilder,
FormGroup,
FormControl,
Validators
} from '#angular/forms';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
title = 'stackoverflow-examples';
declare MultipleSelect: any;
textInputForm: FormGroup;
get textInputControl() {
return this.textInputForm.get('textInputControl') as FormControl;
}
constructor(
private fb: FormBuilder
) {
this.textInputForm = new FormGroup(
{
textInputControl: new FormControl('', [Validators.maxLength(6)])
}
);
}
ngOnInit() {
}
onClick(): void{
// Just to show you that form can read the values,
console.log(this.textInputForm.getRawValue());
}
}
Your Html can look like this below,
<form>
<label>
Input Name:
<input type="text" class="col2" [formControl]="textInputControl" maxLength="6"/>
</label>
</form>
<button (click)="onClick()">Click</button>
Having a sample React component for testing input type behavior.
Initially the input type is set to 'text' and it's value is initialized with '1.0'.
Clicking the button the value will be converted to Number. The value displayed on the input box now shows '1' - Ok.
Setting the input type to 'number' and clicking the button, the value displayed on the input box will stay at '1.0' - is this supposed to work so, i mean is this so specified by html standard ? Same behavior on chrome/firefox/iexplorer (for firefox the value '1.0' has to be entered again, as it will show '1' on type change to 'number').
class Sample extends React.Component {
constructor(props) {
super(props);
this.defVal = '1.0';
this.state = {
val: this.defVal,
type: 'text'
};
this.onChange = this.onChange.bind(this);
this.onChangeType = this.onChangeType.bind(this);
this.onClick = this.onClick.bind(this);
}
onChange(e) {
this.setState({
val: e.target.value
});
}
onChangeType(e) {
this.setState({
val: this.defVal,
type: e.target.value
});
}
onClick(e) {
this.setState((state, props) => ({
val: Number(this.state.val)
}));
}
render() {
return <div id='flex-container'>
<input type={this.state.type} onChange={this.onChange} value={this.state.val} step="any" />
<label className='state-val'>state.val: {this.state.val}</label>
<select value={this.state.type} onChange={this.onChangeType}>
<option value="text">Text</option>
<option value="number">Number</option>
</select>
<button onClick={this.onClick}>Convert to Number</button>
</div>
}
}
ReactDOM.render(
<Sample />,
document.getElementById('react')
);
#flex-container {
display: flex;
flex-direction: column;
width: 150px;
}
#flex-container > * {
margin: 10px 0;
}
.state-val {
background-color: #e7e7e7;
color: black;
font-family: "Courier New";
font-size: small;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="react"></div>
I ran into difficulty styling my dropdown : https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select#Styling_with_CSS
The select element is notoriously difficult to style productively with CSS.
in summary none of the hacks you can use on the option and select tags are worth their salt even in combination.
I'd love to continue using reactive forms but I wish to purify my html and css by using only <div> tags to draw up and use my dropdown in a reactive form.
is this possible?
here is my code as it exists today.
// this.statusForm = this.fb.group({
// status: ['Delivered', Validators.required]
// });
<form [formGroup]="statusForm">
<select formControlName="status">
<option value="Delivered">Delivered</option><!---->
<option value="Cancelled">Cancelled</option><!---->
<option value="UndeliveredTechnicalissue">Undelivered/Technical issue</option><!---->
</select>
</form>
the js is just the FormBuilder hydration.
I can gather/ console.log() the value using
this.statusForm.value.status;
You create the "options" part as ul>li or div and then style it accordingly.
The trick is to hide/show this part on mouse click or keyboard interaction, but for this you can use a boolean variable (here expanded).
Here a working Stackblitz.
If you want to look at the code in just one page, have a look to the code below:
template
<div class="select-container">
<input type="text"
[id]="customId"
[placeholder]="placeholder"
[value]= "selectedValue"
[disabled]="disabled"
(click)="showOptions()"/>
<ul class="select-menu box" role="listbox" *ngIf="expanded">
<li role="option"
*ngFor="let option of options; let i = index;"
[id]="customId + '-option-' + i"
[title]="option.label"
class="option-item"
[ngClass]="{ 'selected': activeItemIndex === i }"
(click)="selectItem(option)">
<span> {{option.label}}</span>
</li>
</ul>
</div>
component
#Component({
selector: 'form-select',
templateUrl: './form-select.component.html',
styleUrls: ['./form-select.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
multi: true,
useExisting: forwardRef(() => FormSelectComponent)
}
]
})
export class FormSelectComponent implements ControlValueAccessor{
public selectedValue = '';
public disabled = false;
public value: string;
#Input()
label: string;
#Input()
formCtrl: AbstractControl;
#Input()
pipe: { type?: string; params?: any };
#Input()
options: {key: string, label: string}[] = [];
#Input()
customId: string;
#Input()
placeholder: string;
public expanded = false;
public activeItemIndex: number;
public onChange(newVal: T) {}
public onTouched(_?: any) {}
public registerOnChange(fn: any): void {
this.onChange = fn;
}
public registerOnTouched(fn: any): void {
this.onTouched = fn;
writeValue(value: string) {
if (value && this.options) {
const match = this.options.find(
(item: { type?: string; params?: any }, index: number) => {
if (item.key === value) {
this.activeItemIndex = index;
return true;
}
}
);
this.selectedValue = match ? match.label : '';
}
}
showOptions() {
if (!this.disabled) {
this.expanded = true;
}
}
selectItem(item: {key: string, label: string}) {
this.value = item.key;
this.expanded = false;
this.selectedValue = item.label;
this.onChange(item.key);
}
}
scss styles
.select-container {
position: relative;
.input-container {
i {
position: absolute;
top: 1rem;
right: 1rem;
}
input[type='text'] {
overflow-x: hidden;
white-space: nowrap;
text-overflow: ellipsis;
padding-right: 2rem;
}
}
.select-menu {
width: 100%;
z-index: 100;
max-height: 17.75rem;
overflow: auto;
position: absolute;
top: -5px;
right: 0;
background-color: white;
border: 1px solid gray;
padding: 1rem;
box-sizing: border-box;
.option-item {
padding-left: 1rem;
line-height: 3rem;
color: gray;
overflow-x: hidden;
white-space: nowrap;
text-overflow: ellipsis;
margin: 0 -1rem;
&:last-child {
margin-bottom: 0;
}
&.selected,
&:hover {
background-color: lightgray;
color: black;
}
&:focus {
outline: none;
}
}
}
}
How to use it:
In template:
<form [formGroup]="mainFormGroup">
<form-select formControlName="myControl" [options]="options">
</form-select>
</form>
in component:
const options = [{
key: key1,
label: 'value_1'
}, {
key: key2,
label: 'value_2'
}];
this.mainFormGroup = this.fb.group({
myControl: ['']
});
I am using nextjs to compile my code and antd framework. I am unable to style the positioning of my button, also I want my start button to trigger a set of buttons but for some reason it does not work. Below is my code
import React, { Component } from "react";
import Layout from "./Layout";
import { Radio } from "antd";
export default class PositiveAffirmation extends Component {
state = {
changeButton: false
};
toggleChangeButton = e => {
this.setState({
changeButton: e.target.value
});
};
render() {
const { changeButton } = this.state;
return (
<Layout>
<Radio.Group
defaultValue="false"
buttonStyle="solid"
onChange={this.changeButton}
className="radio-buttons"
>
<Radio.Button value={true}>Start</Radio.Button>
<Radio.Button value={false}>Stop</Radio.Button>
</Radio.Group>
{changeButton && (
<Button.Group size={size}>
<Button type="primary">Happy</Button>
<Button type="primary">Sad</Button>
<Button type="primary">Fullfiled</Button>
</Button.Group>
)}
<style jsx>{`
Radio.Button {
font-size: 100px;
margin-left: 20px;
margin-top: 5px;
margin-bottom: 5px;
display: flex;
justify-content: center;
}
`}</style>
</Layout>
);
}
}
you are calling just wrong fn change onChange={this.changeButton} to onChange={this.toggleChangeButton}