I am working on making custom multi select using text box.if user enter 1,2,3,4 then 1 2 3 4 should be considered as separate value.i am bale to make this using <input type="text"> but instead of text box i want to div so user can click anywhere in the div and able to add item.
following image is my current implementation.
custom-autocomplete.component.html
<div name="test">
<ul class="autocomplete-text base-auto-select-cointainer">
<li class="d-inline-flex selected-item" *ngFor="let item of itemList;let i=index" title="{{item}}">{{item}}
<span class="remove-item pointer" role="presentation" (click)="handelOnItemRemove(i)">×</span></li>
<li class="d-inline-flex">
<input class="item-input" type="text" (keyup)="handelOnItemKeyUp($event)"
tabindex="0" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
role="textbox">
</li>
</ul>
</div>
custom-autocomplete.component.ts
import { Component, OnInit, Output, EventEmitter } from '#angular/core';
#Component({
selector: 'app-custom-autocomplete',
templateUrl: './custom-autocomplete.component.html',
styleUrls: ['./custom-autocomplete.component.css']
})
export class CustomAutocompleteComponent implements OnInit {
itemList: any[] = []; // for collection selcted items
userInputs: any;
model = 'some text';
constructor() { }
// event emiters for passing data to parent componet.
#Output() handelItemSelection: EventEmitter<any> = new EventEmitter<any>();
ngOnInit() {
}
handelOnItemKeyUp(event) {
if (event.target.value) {
let userInput: string = event.target.value;
let splitChar = userInput.charAt(userInput.length - 1);
let tempArray: any[] = userInput.split(',');
if (tempArray.length > 1) {
if (tempArray[0] != "" && tempArray[0] != undefined) {
this.itemList.push(tempArray[0])
this.handelItemSelection.emit(this.itemList); //passing updated items to parent
}
}
console.log('')
splitChar == ',' ?event.target.value = null : '' // setting empty value to text box when user enter ,
}
}
// code block for handel item removal from list.
handelOnItemRemove(index: number) {
this.itemList.splice(index, 1);
this.handelItemSelection.emit(this.itemList); //passing updated items to parent
}
}
can someone suggest me to how to implement this using <div contenteditable="true"></div>
Related
I have an image component formed from two input types :
1- text input
2- file input
image.html
<div>
<mat-card>
<mat-card-content>
<label for="cam">Take picture</label>
<input id="cam" style="display: none;" type="file" accept="image/*" capture="camera" (change)="onSelectFile($event)" /> <br>OR<br>
<label for="gallery">Open gallery</label>
<input id="gallery" style="display: none;" type="file" accept="image/*" multiple (change)="onSelectFile($event)" />
</mat-card-content>
<img *ngFor="let image of imagesList" [src]="imagesMap.get(image)" mat-card-avatar (click)="deleteImage(image)"> <br/>
<br>
<mat-card-header *ngIf="imagesList.length > 0">
<mat-card-subtitle>Click on image to remove it</mat-card-subtitle>
</mat-card-header>
</mat-card>
<input type="text" placeholder="Ex. pat#example.com">
image.ts
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-image',
templateUrl: './image.component.html',
styleUrls: ['./image.component.css']
})
export class ImageComponent implements OnInit {
files!: File[];
imagesMap: Map<string, string> = new Map<string, string>();
imagesList: string[] = new Array<string>();
url = "";
constructor() { }
ngOnInit(): void {
}
onSelectFile(event: any) {
this.files = event.target.files;
if (this.files && this.files.length > 0) {
for (let file of this.files) {
if (!this.imagesMap.has(file.name)) {
let url: string = "";
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = (event) => {
//#ts-ignore
url = event.target.result;
this.imagesMap.set(file.name, url);
this.imagesList.push(file.name);
}
}
}
}
}
deleteImage(imageName: string) {
let indexToRemove = this.imagesList.indexOf(imageName);
if (indexToRemove != -1) {
this.imagesList.splice(indexToRemove, 1);
this.imagesMap.delete(imageName);
indexToRemove = this.files.findIndex(file => file.name == imageName);
if (indexToRemove != -1)
this.files.splice(indexToRemove, 1);
}
}
}
The problem is that when I call this component several times or duplicate it from app components ,
app.component.html
<app-image></app-image>
<app-image></app-image>
the image that had been selected from the second or third … component , it always shows in the first one instead, this problem is with inputs of type File , inputs with type text are working fine . Kindly refer to the image below for a clearer view.
Component screenshot
I want to when I add pictures in the second component (same component but duplicated) , they must shows in the second component . Each duplicated component must be independent from others.
Please can help me to solve this issue.
Thanks
That's because of duplicate Id. You have two input with Id of cam and gallery so when you click on second, one browser finds the first element and opens file chooser. It means you basically clicked on first element. don't use this approach, just simply use a template variable
<label (click)="file.click()">Open gallery</label>
<input #file style="display: none;" type="file"
accept="image/*" multiple (change)="onSelectFile($event)"/>
I have a email subject textbox where I can input combination of dynamic dropdown value and other text entered by user, here I can enter only one dynamic value at start, cant add another dynamic value after that please help.
its an email subject for example there are 3 dynamic value here ["creditcard","accountno","Amount"].
sample output - "Hi jack your creditcard with accoutno has due Amount" here creditcard accoutno and Amount are dynamic value from dropdown and rest are normal text
HTML
<div class="textbox">
<label class="textbox__label" [for]="id">{{label}}</label>
<input class="textbox__input" [type]="type" [id]="id" [placeholder]="placeholder" [value]="value" [name]="name"
autocomplete="off" (input)="onChange($event)" (keyup.Space)="doSomething()" [(ngModel)]="model" />
<ul class="textbox__dropdown" *ngIf="show">
<ng-container *ngFor="let list of listData ; let i = index">
<li (click)="handleSetValue(list)">
<span [style.background-color]="colors[i % colors.length]">{{list.value.charAt(0)}}</span>
{{list.value}}
</li>
</ng-container>
</ul>
</div>
TS
import { Component, VERSION,Output, EventEmitter } from '#angular/core';
import { isEmpty } from 'lodash';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
name = 'Angular ' + VERSION.major;
public dynamicList = ["creditcard","accountno","Amount"]
public value;
public listData: any;
public show: boolean = true;
public model: any;
#Output() selectedValue = new EventEmitter();
onChange(event: any) {
if (event.target.value !== '' && this.dynamicList) {
console.log(event.target.value);
this.show = true;
const item = this.dynamicList.filter((items) => items.toLowerCase().includes(event.target.value));
if (!isEmpty(item)) {
this.listData = item;
console.log(item)
} else {
this.show = false;
this.selectedValue.emit(event.target.value)
}
} else {
this.listData = this.dynamicList;
this.show = false
}
}
Stackblitz-link
You can split the event.target.value that you are making in onChange() method like,
const getLastSearch = event.target.value.split(' ');
Then in the filter method you can use the last recent search into includes to check and display the latest search result like,
const item = this.dynamicList.filter((items) => items.toLowerCase().includes(getLastSearch[getLastSearch.length - 1]));
In app.component.html , for the input box you could use space bar event and call a method to show the dropdown items like,
<input class="textbox__input" type="type" id="id" name="name"
autocomplete="off" (input)="onChange($event)" (keyup.Space)= "spaceEvent($event)" [(ngModel)]="model" />
And the spaceEvent() as follows,
spaceEvent(event: any){
this.listData = this.dynamicList;
this.show = true;
}
Then finally you could split the strings available in the text box and then you can join it by removing the last one and append the clicked list item to as the last item like,
handleSetValue(list) {
let splittedSearch = this.model.split(' '); // Split each string with space
splittedSearch[splittedSearch.length - 1] = ''; // Make the last string empty
splittedSearch = splittedSearch.join(' ') // Join all the splitted string with space
splittedSearch += list; // Concat the splitted search with the selected list item
this.model = splittedSearch; // Assign the splittedsearch to model
this.show = false;
}
Forked Stackblitz here...
Please make the changes as per this
. In TS file
public model: any = "";
onChange(event: any) {
var value = event.target.value;
var inputarray = value.split(" ");
var lastinput = inputarray[inputarray.length - 1]
console.log(lastinput)
if ((value !== '' || inputarray.length> 1) && this.dynamicList) {
console.log(value);
this.show = true;
const item = this.dynamicList.filter((items) => items.toLowerCase().includes(lastinput));
if (!isEmpty(item)) {
this.listData = item;
console.log(item)
} else {
this.show = false;
this.selectedValue.emit(value)
}
} else {
this.listData = this.dynamicList;
this.show = false
}
}
handleSetValue(list) {
debugger
if(this.dynamicList.find(s=>s == list)){
this.model += " "+list;
}
this.show = false;
// this.selectedValue.emit(list.key)
}
in HTML
<div class="textbox">
<input class="textbox__input" type="type" id="id" name="name"
[ngModel]="model" autocomplete="off" (input)="onChange($event)"/>
<ul class="textbox__dropdown" *ngIf="show">
<ng-container *ngFor="let list of listData">
<li (click)="handleSetValue(list)">
{{list}}
</li>
</ng-container>
</ul>
</div>
I have this data coming from another component
on the basis of active tag when row is clicked I am pushing Id to ngModel of checkbox input field.
Row click is working fine and checkbox is adding/removing data
but now when I click on checkbox itself it doesn't do anything like checkbox click function is not working
How can I solve that?
html component
<ngb-panel [disabled]="true" *ngFor="let testPanel of otherTests; let i = index;" id="{{testPanel.Id}}" [title]="testPanel.Name">
<ng-template ngbPanelTitle>
<div class="action-items">
<label class="custom-control custom-checkbox">
<input
type="checkbox"
class="custom-control-input"
[name]="testPanel.Id + '-' + testPanel.Moniker"
[ngModel]="panelIds.indexOf(testPanel.Id) > -1"
(ngModelChange)="onPanelCheckboxUpdate($event, testPanel)"
[id]="testPanel.Id + '-' + testPanel.Moniker">
<span class="custom-control-indicator"></span>
</label>
</div>
</ng-template>
</ngb-panel>
ts component
getting Id from service and push it on basis of row click
this.testOrderService.refreshRequestsObservable().subscribe(
data => {
this.panelActive = data.active;
let testFilteredArray = lodash.filter(this.otherTests, item => item.Id === data.id);
if (this.panelActive) {
// is checked
this.panelIds.push(data.id);
if(testFilteredArray.length > 0){
this.selectedPanels.push(testFilteredArray[0]);
}
}
else {
//is false
this.panelIds = this.panelIds.filter(obj => obj !== data.id);
this.selectedPanels = this.selectedPanels.filter(obj => obj.Id !== data.id);
}
// this.panelIds = lodash.uniq(this.panelIds);
this.selectedPanels = lodash.uniqBy(this.selectedPanels, "Id");
this.updateSession();
}
)
checkbox function
onPanelCheckboxUpdate($event: boolean, panel: TestOrderPanel) {
let testPanelIds = panel.Tests.map(test => test.Id);
// Wipe any duplicates
this.panelIds = this.panelIds.filter(
panelId => panel.Id !== panelId && testPanelIds.indexOf(panelId) === -1
);
this.selectedPanels = this.selectedPanels.filter(
selectedPanel =>
panel.Id !== selectedPanel.Id &&
testPanelIds.indexOf(selectedPanel.Id) === -1
);
if ($event) {
this.panelIds.push(panel.Id);
this.selectedPanels.push(panel);
}
this.updateSession();
}
This checkbox function is not working and wont let me change the value of checkbox.
And also is there any way of adding click function in ngbPanel tag?
Any help?
Thanks
If you want to achieve two way data binding , use below code.
In foo.html
<input [(ngModel)]="checBxFlag" type="checkbox"/>
In App.module.ts
import { FormsModule } from '#angular/forms';
#NgModule({
imports: [
[...]
FormsModule
],
[...]
})
If you want fire event click of check box ,use (onClick)="somemethod()" in your foo.html file and define method in foo.ts file.
I have written a directive for text input, to support int values.
Here is it
import { NgControl } from '#angular/forms';
import { HostListener, Directive } from '#angular/core';
#Directive({
exportAs: 'number-directive',
selector: 'number-directive, [number-directive]'
})
export class NumberDirective {
private el: NgControl;
constructor(ngControl: NgControl) {
this.el = ngControl;
}
// Listen for the input event to also handle copy and paste.
#HostListener('input', ['$event.target.value'])
onInput(value: string) {
// Use NgControl patchValue to prevent the issue on validation
this.el.control.patchValue(value.replace(/[^0-9]/g, ''));
}
}
And HTML
<div class="form-group">
<label>{{ l("RoomWidth") }}</label>
<input
decimal-number-directive
#roomWidthInput="ngModel"
class="form-control nospinner-input"
type="text"
name="roomWidth"
[(ngModel)]="room.roomWidth"
maxlength="32"
/>
</div>
But I need it to support decimal values. For example 99.5
How do I need to modify it?
Try this:
#HostListener('input', ['$event.target.value'])
onInput(value: string) {
// Use NgControl patchValue to prevent the issue on validation
this.el.control.patchValue(value.replace(/[^0-9].[^0-9]/g, ''));
}
Working_Demo
In my case I have need to considerate two consegutive point too and replace caracter. Maybe is writable with regex but this work for me.
Example if you type or paste 45.456.6 it will be repaced with 45.4566
#HostListener('input', ['$event']) onInputChange(event) {
let initalValue: string = this.el.nativeElement.value;
initalValue = initalValue.replace(/[^\.|0-9]/g, '');
// elimina le seconde occorrenze del punto
const count = (initalValue.match(/\./g) || []).length;
for (let i = 1; i < count; i++) {
initalValue = this.repaceSecondDotOccurrence(initalValue);
}
this.el.nativeElement.value = initalValue;
}
repaceSecondDotOccurrence(inputString): string {
let t = 0;
return inputString.replace(/\./g, function (match) {
t++;
return (t === 2) ? '' : match;
});
}
Here is my emp array of object:
emp[
{"id":1001,"name":"robin_delhi"},
{"id":1002,"name":"robin_mumbai"},
{"id":1003,"name":"robin_bang"},
{"id":1004,"name":"sushil_delhi"},
{"id":1005,"name":"sushil_mumbai"},
]
Here is my HTML template, I am displaying emp object in drop down through mat select component of angular material:
<mat-form-field>
<mat-select [(ngModel)]="emp" [(value)]="sel" placeholder="Employee
[formControl]="toppings" multiple required>
<mat-option *ngFor="let p of emp | proDisable:emp:sel; let p = index;" [value]="p">
{{p.name}}
</mat-option>
</mat-select>
</mat-form-field>
I want when particular emp name will selected, it will match with remaining element (first part of special character “_”), if match found it will show as usual else disabled.
Here is my custom pipe:
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'proDisable'
})
export class ProDisablePipe implements PipeTransform {
transform(value:any, data:any, sel:any) : any {
if(!sel)
{
return value;
}
else
{
let nval:any;
for(var i=0; i<data.length; i++)
{
for(var j=0; j<sel.length; j++)
{
if ((i['name'].substring(0, i['name'].indexOf("_")))==(sel['name'].substring(0, sel['name'].indexOf("_"))))
{
nval= "class='disable'"+value;
}
else
{
nval= "class='enable'"+value;
}
}
}
return nval;
}
}
}
Here if I select first element, the last two will be disabled.
Kindly help how to achieve this or any better way to do this.