ng-select not displaying search values properly after pasting in values - javascript

When trying to update array of strings acting as the model for ng-select, the values do not display properly in the search box itself.
the values that are showing up properly, are the ones that are selected from the dropdown, the ones that do not display properly are numbers I am trying to add on to the list manually
select box in template:
<ng-select
id="oNumberSelect"
[items]="ownerNumberResults | async"
[typeahead]="typeAhead$"
bindLabel="desc"
bindValue="code"
dropdownPosition="bottom"
[(ngModel)]="selectedOwnerNumbers"
(ngModelChange)="handleSelectionChange()"
[multiple]="true"
[searchable]="true"
multiple="true"
style="overflow-y: auto; width: 100%"
appendTo="body"
(paste)="handlePaste($event)"
minTermLength="3"
[addTag]="true"
>
</ng-select>
methods referenced:
handlePaste(pasteEvent: ClipboardEvent): void {
pasteEvent.stopPropagation();
pasteEvent.preventDefault();
const clipStrings:string[] = [...pasteEvent.clipboardData.getData('Text').trim().split(/[\s,)]+/)]
this.selectedOwnerNumbers = [...this.selectedOwnerNumbers, ...clipStrings];
}
searchOwnerNumbers(): void {
this.ownerNumberResults = this.typeAhead$.pipe(
distinctUntilChanged(),
debounceTime(500),
switchMap(term => {
return this.ownerHttpService.searchOwnerProperty('ownerNumber', term);
}
)
);
}
handleSelectionChange(): void {
console.log(this.selectedOwnerNumbers)
}
select variables:
selectedOwnerNumbers: string[];
typeAhead$ = new Subject<string>();
ownerNumberResults: Observable<Option[]>;
I have tried using sets, arranging arrays differently, concatenating the pasted values, but i cant get them to show up in the UI correctly

this usually happens if the bindValue and bindLabel are different.
try changing the handle paste logic like this
handlePaste(pasteEvent: ClipboardEvent) {
pasteEvent.stopPropagation();
pasteEvent.preventDefault();
const clipStrings: string[] = [
...pasteEvent.clipboardData
.getData('Text')
.trim()
.split(/[\s,)]+/),
];
this.selectedOwnerNumbers.push(this.ownerNumberResults.find(result => result.desc === clipStrings[0]).code);
this.selectedOwnerNumbers = [...this.selectedOwnerNumbers]
}

Related

Saving Values to Backend from TextBoxes using React Flux Pattern

I have several text boxes and a save button
Each text box value is loaded using the following approach
{
this.getElement('test3lowerrangethreshold', 'iaSampling.iaGlobalConfiguration.test3lowerrangethreshold',
enums.IASamplingGlobalParameters.ModerationTest3LowerThreshold)
}
private getElement(elementid: string, label: string, globalparameter: enums.IASamplingGlobalParameters): JSX.Element {
let globalParameterElement =
<div className='row setting-field-row' id={elementid}><
span className='label'>{localeHelper.translate(label)}</span>
<div className="input-wrapper small">
<input className='input-field' placeholder='text' value={this.globalparameterhelper.getDataCellContent(globalparameter, this.state.globalParameterData)} />
</div>
</div>;
return globalParameterElement;
}
Helper Class
class IAGlobalParametesrHelper {
public getDataCellContent = (globalparameter: enums.IASamplingGlobalParameters, configdata: Immutable.List<ConfigurationConstant>) => {
return configdata?.find(x => x.key === globalparameter)?.value;
}
}
This works fine. Now the user is allowed to update these text values.And on click of save the changes should be reflected by calling a web api .
I have added an onlick event like this
<a href='#' className='button primary default-size' onClick={this.saveGlobalParameterData}>Save</a>
Now inorder to save the data i need a way to identify the text element which has changed.For that i have added an update method within the Helper class
public updateCellValue = (globalparameter: enums.IASamplingGlobalParameters, configdata: Immutable.List<ConfigurationConstant>,updatedvalue:string) => {
let itemIndex = configdata.findIndex(x => x.key === globalparameter);
configdata[itemIndex] = updatedvalue;
return configdata;
}
and return the updated configdata ,and i plan to call this method in the onchange event of every text box like this
<input className='input-field' placeholder='text' onchange={this.setState({ globalParameterData: this.globalparameterhelper.updateCellValue(globalparameter, this.state.globalParameterData, (document.getElementById(elementid) as HTMLInputElement).value})}
But this does not seem like a correct approach as there are number of syntactical errors. I initially got the data using an actioncreator like this.Please advice.
samplingModerationActionCreator.getGlobalParameters();
samplingModerationStore.instance.addListener(samplingModerationStore.SamplingModerationStore
.IA_GLOBAL_PARAMETER_DATA_GET_EVENT,
this.getGlobalParameterData);
}

Convert into HTML checkboxes using JSON values in react

This JSON is dynamic, coming from API and I can't change it. I want to get table.cc value and convert it into HTML checkbox.
let table = {
id: 1,
value: "abc",
height: 1080,
width: 1920,
cc: "{"c08":false,"c07":true}"
}
let headers = Object.keys(table);
let rows = Object.values(table);
let cc = table.cc ? JSON.parse(table.cc) : null;
if(cc) {
let output = Object.entries(cc).map(([key, value]) => {
return `<input type="checkbox" checked=${value}>
<label>${key}</label>`;
}).join('');
console.log(output);
rows[4] = `${output}`; // I am getting a string. I am unable to convert it into HTML markup.
}
Since I am looping over Object.values(table), I want to change only the table.cc to get HTML checkboxes. So, in the case of the above example table.cc should have 2 checkboxes in HTML and the second one should be checked since the value is true. The label should be the key.
Any ideas?
I have put a link in stackblitz to edit the code.
https://stackblitz.com/edit/react-5dtdjt
Your code is working after some fixes. You should not add html tags as strings in JSX. And a Fragment needs to be used as parent element.
import React, { Component, Fragment } from 'react';
--------
...
if (cc) {
let output = Object.entries(cc).map(([key, value]) => {
return (
<Fragment>
<input type="checkbox" checked={value} />
<label>{key}</label>
</Fragment>
)
});
rows[4] = output

why i am not able to make the text area partially non editable in angular 5 and typescript?

I am having an angular project developed using angular 5 and typescript . In my component's html template I have an text area box. I want to make the first few characters non editable in this text area.
So for example from my components ts file i can set the initial value, for example : "RMO " to my text area .
The user cannot remove the text "RMO " which is set in the text area.
i have got some jquery code to achieve this (http://jsfiddle.net/zq4c873L/1/) and i convert it to typescript code. however it is not working as well
so this is my text area defined in the components html template.
<textarea id="messageTxt" formControlName="message" rows="6" [placeholder]="'PLACEHOLDERS.MESSAGE' | translate" (keydown)="ensureMessagePrefixNonEditable(messageTxt.value)" (keyup)="calculateMessagingSegmentCount(messageTxt.value)" #messageTxt></textarea>
there is a function that is triggered whenever the user press a key down. ie ensureMessagePrefixNonEditable(messageTxt.value). this function tries to replace the old value if it doesn't match the text area content with a specific search string. The following is my function .
ensureMessagePrefixNonEditable(inputTxtMsg: string){
console.log(inputTxtMsg);
let originalValue: string = inputTxtMsg;
if( !inputTxtMsg.startsWith(this.messagePrefix.concat(' ')) ) {
this.messageControl.setValue(originalValue);
}
}
however the problem is i am able to remove the predefined value from the text area. any idea what am is wrong in the function ensureMessagePrefixNonEditable. really appreciate any help thank you
i also rewrite my funtion as follows but still the problem
ensureMessagePrefixNonEditable(inputTxtMsg: string){
let originalValue: string = inputTxtMsg;
let messagePrefixSearchWithSpace: string = this.messagePrefix.concat(' ');
let regex: RegExp = new RegExp("^" + originalValue+ "$");
if(!regex.test(messagePrefixSearchWithSpace)){
this.messageControl.setValue(originalValue);
this.formGroup.patchValue( {message: originalValue });
}
}
i can see it enters inside the if block, however this.formGroup.patchValue( {message: originalValue }); didnt set the message text area with the original string in the UI.
Thank you
angular reactive forms version using ngModelChange event handler
private currentValue = "";
constructor(
private formBuilder: FormBuilder,
) {
this.loginForm = this.formBuilder.group({
messageTxt: ["", Validators.required]
});
}
public async ngOnInit() {
this.loginForm.controls["messageTxt"].setValue("RMO");
this.currentValue = "RMO";
}
public keepRMO($event) {
let prefix = "RMO";
if ($event.substring(0, 3) !== prefix) {
alert("You are not allowed to remove the first three characters('RMO')");
this.loginForm.controls["messageTxt"].setValue(this.currentValue);
} else {
this.currentValue = $event;
}
}
html:
<textarea
class="form-control"
name="messageTxt"
id="messageTxt"
formControlName="messageTxt"
rows="6"
(ngModelChange)="keepRMO($event)"
></textarea>
This is the directive I would use, you might need to adapt it. It check if the text contained in the input match your regex otherwise prevent the keydown.
HTML:
<textarea regexDirective="your regex"></textarea>
Directive:
#Directive({
selector: '[regexDirective]'
})
export class RestrictToPatternDirective {
#Input() appRestrictToPattern = ''; // should be your regex passed as an input in case it needs to be reusable
constructor(private elementRef: ElementRef) { }
#HostListener('keydown', ['$event']) onKeyDown(e: KeyboardEvent): void {
if (new RegExp(this.appRestrictToPattern).test(this.elementRef.nativeElement.value + e.key)) {
// let it happen, don't do anything
return;
} else {
e.preventDefault();
}
}
}

ForkJoin() issue on Angular

Hello I am currently working on an UI and I need to print for each 'digId' a value that I retrieved in an unique JSON response.
In the case below, I have 3 incidents and I did a fork to have access to the 3 JSON response.
digId='4149';
digId2='4719';
digId3='4309';
ngOnInit(): void {
const parallel$ = Observable.forkJoin(
this.http.get('http://ninjaopsapi?tablename=REF_OPS_ALARM&babylonref=' + this.digId),
this.http.get('http://ninjaopsapi?tablename=REF_OPS_ALARM&babylonref=' + this.digId2),
this.http.get('http://ninjaopsapi?tablename=REF_OPS_ALARM&babylonref=' + this.digId3)
);
parallel$.subscribe( data => {
this.alarms = data, console.log(data);
})
}
My goal is to print the element circled in blue for example: Capture
But with this code below in my html, I retrieve the element [0] for the three incidents and I can't put an additionnal [0] to select only one.
<div *ngFor= "let alarm of alarms">
<div [(ngModel)]="digId" ngDefaultControl>
<div>{{alarm[0].alertMessage}}</div>
</div>
</div>
Is it possible to print the first element of the first element in an array when the .subscribe is done with a "forkJoin()" ?
Thank you
UPDATE
I only changed {{alarm[0][0].alertMessage}} by {{alarms[0][0].alertMessage}} and delete the loop *ngFor="let alarm of alarms
and it works well ! Thank you
You could simply do
parallel$.subscribe( data => {
this.alarms = data.map(x => x[0]);
});
<div>{{alarm.alertMessage}}</div>

How do I populate a list field in a model from javascript?

I have a Kendo.MVC project. The view has a model with a field of type List<>. I want to populate the List from a Javascript function. I've tried several ways, but can't get it working. Can someone explain what I'm doing wrong?
So here is my model:
public class Dashboard
{
public List<Note> ListNotes { get; set; }
}
I use the ListNotes on the view like this:
foreach (Note note in Model.ListNotes)
{
#Html.Raw(note.NoteText)
}
This works if I populate Model.ListNotes in the controller when the view starts...
public ActionResult DashBoard(string xsr, string vst)
{
var notes = rep.GetNotesByCompanyID(user.ResID, 7, 7);
List<Koorsen.Models.Note> listNotes = new List<Koorsen.Models.Note>();
Dashboard employee = new Dashboard
{
ResID = intUser,
Type = intType,
FirstName = user.FirstName,
LastName = user.LastName,
ListNotes = listNotes
};
return View(employee);
}
... but I need to populate ListNotes in a Javascript after a user action.
Here is my javascript to make an ajax call to populate ListNotes:
function getReminders(e)
{
var userID = '#ViewBag.CurrUser';
$.ajax({
url: "/api/WoApi/GetReminders/" + userID,
dataType: "json",
type: "GET",
success: function (notes)
{
// Need to assign notes to Model.ListNotes here
}
});
}
Here's the method it calls with the ajax call. I've confirmed ListNotes does have the values I want; it is not empty.
public List<Koorsen.Models.Note> GetReminders(int id)
{
var notes = rep.GetNotesByCompanyID(id, 7, 7);
List<Koorsen.Models.Note> listNotes = new List<Koorsen.Models.Note>();
foreach (Koorsen.OpenAccess.Note note in notes)
{
Koorsen.Models.Note newNote = new Koorsen.Models.Note()
{
NoteID = note.NoteID,
CompanyID = note.CompanyID,
LocationID = note.LocationID,
NoteText = note.NoteText,
NoteType = note.NoteType,
InternalNote = note.InternalNote,
NoteDate = note.NoteDate,
Active = note.Active,
AddBy = note.AddBy,
AddDate = note.AddDate,
ModBy = note.ModBy,
ModDate = note.ModDate
};
listNotes.Add(newNote);
}
return listNotes;
}
If ListNotes was a string, I would have added a hidden field and populated it in Javascript. But that didn't work for ListNotes. I didn't get an error, but the text on the screen didn't change.
#Html.HiddenFor(x => x.ListNotes)
...
...
$("#ListNotes").val(notes);
I also tried
#Model.ListNotes = notes; // This threw an unterminated template literal error
document.getElementById('ListNotes').value = notes;
I've even tried refreshing the page after assigning the value:
window.location.reload();
and refreshing the panel bar the code is in
var panelBar = $("#IntroPanelBar").data("kendoPanelBar");
panelBar.reload();
Can someone explain how to get this to work?
I don't know if this will cloud the issue, but the reason I need to populate the model in javascript with an ajax call is because Model.ListNotes is being used in a Kendo Panel Bar control and I don't want Model.ListNotes to have a value until the user expands the panel bar.
Here's the code for the panel bar:
#{
#(Html.Kendo().PanelBar().Name("IntroPanelBar")
.Items(items =>
{
items
.Add()
.Text("View Important Notes and Messages")
.Expanded(false)
.Content(
#<text>
#RenderReminders()
</text>
);
}
)
.Events(e => e
.Expand("getReminders")
)
)
}
Here's the helper than renders the contents:
#helper RenderReminders()
{
if (Model.ListNotes.Count <= 0)
{
#Html.Raw("No Current Messages");
}
else
{
foreach (Note note in Model.ListNotes)
{
#Html.Raw(note.NoteText)
<br />
}
}
}
The panel bar and the helpers work fine if I populate Model.ListNotes in the controller and pass Model to the view. I just can't get it to populate in the javascript after the user expands the panel bar.
Perhaps this will do it for you. I will provide a small working example I believe you can easily extend to meet your needs. I would recommend writing the html by hand instead of using the helper methods such as #html.raw since #html.raw is just a tool to generate html in the end anyways. You can write html manually accomplish what the helper methods do anyway and I think it will be easier for you in this situation. If you write the html correctly it should bind to the model correctly (which means it won't be empty on your post request model) So if you modify that html using javascript correctly, it will bind to your model correctly as well.
Take a look at some of these examples to get a better idea of what I am talking about:
http://www.hanselman.com/blog/ASPNETWireFormatForModelBindingToArraysListsCollectionsDictionaries.aspx
http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx
http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/
So to answer your question...
You could build a hidden container to hold your list values like this (make sure this container is inside the form):
<div id="ListValues" style="display:none">
</div>
Then put the results your ajax post into a javascript variable (not shown).
Then in javascript do something like this:
$('form').off('submit'); //i do this to prevent duplicate bindings depending on how this page may be rendered futuristically as a safety precaution.
$('form').on('submit', function (e) { //on submit, modify the form data to include the information you want inside of your ListNotes
var data = getAjaxResults(); //data represents your ajax results. You can acquire and format that how you'd like I will use the following as an example format for how you could save the results as JSON data: [{NoteID ="1",CompanyID ="2"}]
let listLength = data.length;
for (let i = 0; i < listLength; i++) {
$('#ListValues').append('<input type="text" name="ListNotes['+i+'].NoteID " value="' + data.NoteID +'" />')
$('#ListValues').append('<input type="text" name="ListNotes['+i+'].CompanyID " value="' + data.CompanyID +'" />')
//for your ajax results, do this for each field on the note object
}
})
That should do it! After you submit your form, it should automatically model bind to you ListNotes! You will be able to inpsect this in your debugger on your post controller action.

Categories