Angular2 how to clear the input value on submit without passing dom element - input

I've read that it is not good practice to pass the dom element so I come up with such view:
<div>
<input #message placeholder="message" (keyup.enter)="add(message.value)" />
<button (click)="add(message.value)">Add+</button>
<p>
the message is {{ message.value }}
</p>
</div>
As you can see I am passing the message.value to my add method
add(message: string) {
this.message = message;
console.log(this.message);
this.messengerService.add(this.message);
}
But how can I clear the so inside add method so input #message won't containg any text? I tried message = null; but it is does not work.

If you extend your response with (blur)= , you can reset the input field as follows:
<div>
<input #message placeholder="message (keyup.enter)="add(message.value)" (blur)="add(message.value); message.value='' " />
<button (click)="add(message.value)">Add+</button>
<p>
the message is {{ message.value }}
</p>
</div>
Note added: (blur)=add(message.value); message.value=''

you can use ngModel instead of #elem.value :
<div>
<input placeholder="message" [(ngModel)]="message" (keyup.enter)="add(message)"/>
<button (click)="add(message)">Add+</button>
<p>
the message is {{ message }}
</p>
</div>
and then clear the input value by adding
this.message = null;
in add function. This will work :
add(message: string) {
this.message = message;
console.log(this.message);
this.messengerService.add(this.message);
this.message = null;
}
your message.value attribute in view was not mapped to this.message in modal

Why to use extra Parameter when angular provides two way data binding in the form of [(ngModel)]. you can use ngModel to get notified both side on view as well as in the class/controller. no need to pass extra parameter along with method call. so you can use this simplified approach here
<div>
<input placeholder="message" [(ngModel)]="message" (keyup.enter)="add()"/>
<button (click)="add(); message = null">Add+</button>
<p>
the message is {{ message }}
</p>
</div>
message: string = null;
add() {
console.log(this.message);
// this.messengerService.add(this.message);
}
here is working demo for the same Working Plunker
The ngModel input property sets the element's value property and the ngModelChange output property listens for changes to the element's value. The details are specific to each kind of element and therefore the NgModel directive only works for elements, such as the input text box.

Related

Blazor problem connecting the UI with the code

I'm using Blazor Server application in Visual Studio 2019. In the .razor page I have:
<div class="container">
<div class="row">
<div class="col-md">
<label for="ConnectionStringEdit" id="Label1">Connection String for destination</label>
</div>
<div class="col-md-7">
<input type="text" id="ConnectionStringEdit" name="ConnectionStringEdit" text=#ConnectDestination spellcheck="false" style="width: 585px; height: 26px;" class="form-control">
</div>
<div class="col-md-auto">
<input type="submit" id="btnConnect" name="btnConnect" value="Connect" class="btn btn-primary" #onclick="Connect1">
</div>
</div>
</div>
now in the code part I have
#code {
private string ConnectDestination { get; set; } = "";
private void Connect1()
{
if (ConnectDestination.Length > 0)
{
// do something
}
}
}
When I insert something in the Input and I press the button, ConnectDestination doesn't take the value of the Input Control. So this last If condition is never true. How do I get the inserted value of the Input control named ConnectionStringEdit?
Thanks
It should be #bind-value="#ConnectDestination"
you could also use the short directive #bind instead:
#bind="#ConnectDestination"
Note: All the input element's types are bound through the value attribute of the element.
Note: Both #bind-value and #bind are compiler directive instructing the compiler to emit code, behind the scene, that enables two way data-binding between a variable and an Html tag. The compiler create a two-way data binding by binding a variable to the value attribute of the element, something equivalent to this:
value="#ConnectDestination", which creates a one direction binding from the variable to the bound element. The compiler also creates an event call back which enables binding from the element to the variable, something equivalent to this:
#onchange="#((args) => ConnectDestination = args.Value?.ToString())"
This means that you could do that yourself, if you wish to have more control over the binding. You'll usually do something like this:
value="#ConnectDestination" #onchange="OnChange"
And define the call back method like this:
private void OnChange(ChangeEventArgs args)
{
// Note that it is your responsibility to update the
// ConnectDestination variable:
ConnectDestination = args.Value?.ToString());
}
Note: This is wrong:
<input type="submit" id="btnConnect" name="btnConnect" value="Connect" class="btn btn-primary" #onclick="Connect1">
The type attribute of the input element should be set to button:
<input type="button"
Blazor App is an SPA... meaning no submit. The only place you use the "submit" button is when you use the EditForm component, and even then the "submit" action is intercepted and canceled by the Blazor.
You can try
<input type="text" id="ConnectionStringEdit" name="ConnectionStringEdit" #bind=#ConnectDestination spellcheck="false" style="width: 585px; height: 26px;" class="form-control">
or
<input type="text" id="ConnectionStringEdit" name="ConnectionStringEdit" value="#ConnectDestination"
#onchange="#((ChangeEventArgs __e) => ConnectDestination = __e?.Value?.ToString())" spellcheck="false" style="width: 585px; height: 26px;" class="form-control">

true / false in input (vuejs), probably without v-model

I have an array with many checkbox.
<li v-for='item in resultQuery' :key='item.id'>
<label class='custom-checkbox'>
<input type='checkbox' :value='item.id' v-model='checkBrands'>
<span #click='loadProducts(item.seoName)>{{ item.title }}</span>
</label>
</li>
I need to get true or false (depends on checkbox). How can I do this without affecting the v-model? (Use it to transfer an array of selected checkbox).
Needed to trigger a specific mutation
.then((response) => {
if(true) {
this.$store.commit(
'showFilteredList',
response.data.items
);
} else {
this.$store.commit(
'deleteCheckboxItems',
response.data.items
);
}
});
You can using #change on checkbox.
Example: https://codepen.io/koei5113/pen/ZEXLLgL
<input type='checkbox' :value='item.id' v-model='checkBrands' #change="changeEvent">
methods: {
...,
changeEvent($event) {
console.log($event.target.checked);
}
}
In this example you can see your v-model still working and you still can check checkbox status by the change event.
v-model ignore the :value in the input. You need to use :checked and #change
For example, and when you emit the change event use your function.
<input type="checkbox" :checked="value" #change="changeArrayNotValue" />

Aurelia repeater: model.bind is not working for radio buttons

I am creating a set of radio buttons in Aurelia with the code like this:
<div repeat.for="option of options">
<input type="radio" id="${option}_id" name="radio_options" model.bind="option" checked.bind="optionValue"/>
<label for="${option}_id" id="${option}_label">${option}</label>
</div>
However, doing it this way I discovered that model.bind is not working - the optionValue in corresponding class is not populated when radio button is checked. Similarly when some value is assigned to optionValue in the class, the appropriate radio button is not checked. I found this happening only with repeater. Options are numbers in my case. Could you please help me to find out what may be wrong here?
The first problem is that model.bind should be used when working with objects. Since you're working with a primitive type, you should use value.bind instead.
The second problem is that input values are always strings, so when setting an initial value, it must be a string. For example:
Html:
<template>
<div repeat.for="option of options">
<input type="radio" id="${option}_id" name="radio_options" value.bind="option" checked.bind="optionValue"/>
<label for="${option}_id" id="${option}_label">${option}</label>
</div>
<p>Selected Option: ${optionValue} </p>
</template>
JS:
export class App {
options = [ 1, 2, 3, 4 ]
optionValue = '3';
}
If you really want to use int in your view-model, you can create a ValueConverter to convert the value to int when passing it to/from the view. For instance:
export class AsIntValueConverter {
fromView(value) {
return Number.parseInt(value);
}
toView(value) {
return value.toString();
}
}
Usage:
<input type="radio" id="${option}_id" name="radio_options" value.bind="option" checked.bind="optionValue | asInt"/>
Running Example https://gist.run/?id=1465151dd5d1afdb7fc7556e17baec35

How can I compose a VM into a view within an Aurelia validation renderer

I'm trying to use the aurelia-validation plugin to perform validation on a form. I'm creating a custom validation renderer that will change the color of the input box as well as place an icon next to the box. When the icon is clicked or hovered, a popup message appears that will display the actual error message.
Currently, I'm rendering all of this in code manually in the renderer, but it seems like it would be nice to have the html for all of this defined in an html file along with the associated js file to handle the click and hover on the icon. IOW, encapsulate all the error stuff (icon with popup) in a View/ViewModel and then in the render() of my validation renderer, somehow just compose a new instance of this just after the element in question.
Is this possible to do? I've seen how to use <compose></compose> element but I really am trying to avoid having to add that to all of my forms' input boxes.
This is what I currently have in my renderer:
import {ValidationError, RenderInstruction} from 'aurelia-validation'
export class IconValidationRenderer {
render(instruction){
//Unrender old errors
for(let {result, elements} of instruction.unrender){
for(let element of elements){
this.remove(element, result);
}
}
//Render new errors
for(let {result, elements} of instruction.render){
for(let element of elements){
this.add(element, result)
}
}
}
add(element, result){
if(result.valid)
return
//See if error element already exists
if(element.className.indexOf("has-error") < 0){
let errorIcon = document.createElement("i")
errorIcon.className = "fa fa-exclamation-circle"
errorIcon.style.color = "darkred"
errorIcon.style.paddingLeft = "5px"
errorIcon.id = `error-icon-${result.id}`
errorIcon.click = ""
element.parentNode.appendChild(errorIcon)
element.classList.add("has-error")
element.parentNode.style.alignItems = "center"
let errorpop = document.createElement("div")
let errorarrow = document.createElement("div")
let errorbody = document.createElement("div")
errorpop.id = `error-pop-${result.id}`
errorpop.className = "flex-row errorpop"
errorarrow.className = "poparrow"
errorbody.className = "flex-col popmessages"
errorbody.innerText = result.message
console.log("Computing position")
let elemRec = errorIcon.getBoundingClientRect()
let elemH = errorIcon.clientHeight
errorpop.style.top = elemRec.top - 10 + "px"
errorpop.style.left = elemRec.right + "px"
errorpop.appendChild(errorarrow)
errorpop.appendChild(errorbody)
element.parentNode.appendChild(errorpop)
}
}
remove(element, result){
if(result.valid)
return
element.classList.remove("has-error")
let errorIcon = element.parentNode
.querySelector(`#error-icon-${result.id}`)
if(errorIcon)
element.parentNode.removeChild(errorIcon)
//Need to remove validation popup element
}
}
Thanks for any help you can offer.
P.S. At this point, I am not implementing a click or hover like I mentioned -- that is something that I would like to do but I'm not even sure how at this point. Would be more straight forward if I can compose a VM.
EDIT
I was pointed to this article by someone on the Aurelia Gitter channel. I've tried implementing the TemplatingEngine but clearly I'm not going about it the right way. Here's what I have.
add-person-dialog.js //VM that has form with validation
import {TemplatingEngine,NewInstance} from 'aurelia-framework'
import {ValidationController} from 'aurelia-validation'
import {IconValidationRenderer} from './resources/validation/icon-validation-renderer'
export class AddPersonDialog {
static inject = [NewInstance.of(ValidationController),TemplatingEngine]
constructor(vc, te){
this.vc = vc
this.vc.addRenderer(new IconValidationRenderer(te))
}
icon-validation-renderer.js
//Plus all the other bits that I posted in the code above
constructor(te){
this.te = te
}
add(element, result){
if(result.valid) return
if(element.className.indexOf("has-error") < 0 {
//replaced there error icon code above with this (as well as a few different variations
let test = document.createElement("field-error-info")
element.parentNode.appendChild(test)
this.te.enhance({element: test})
}
}
field-error-info.html
<template>
<require from="./field-error-info.css" ></require>
<i class="fa fa-exclamation-circle" click.delegate="displayMessage = !displayMessage" mouseenter.delegate="displayMessage = true" mouseleave.delegate="displayMessage = false"></i>
<div show.bind="displayMessage" class="flex-row errorpop" style="left:300px">
<div class="poparrow"></div>
<div class="flexcol popmessages">Message 1</div>
</div>
</template>
Ultimately, <field-error-info></field-error-info> gets added to the DOM but doesn't actually get rendered. (Incidentally, I also tried adding <require from='./elements/field-error-info'></require> in the add-person-dialog.html.
You could create a form control custom element that encapsulates the error icon and tooltip logic. The element could expose two content projection slots to enable passing in a label and input/select/etc:
<template>
<div validation-errors.bind="errors"
class="form-group ${errors.length ? 'has-error' : ''}">
<!-- label slot -->
<slot name="label"></slot>
<!-- input slot -->
<slot name="input"></slot>
<!-- icon/tooltip stuff -->
<span class="control-label glyphicon glyphicon-exclamation-sign tooltips"
show.bind="errors.length">
<span>
<span repeat.for="errorInfo of errors">${errorInfo.error.message}</span>
</span>
</span>
</div>
</template>
Here's how it would be used:
<template>
<require from="./form-control.html"></require>
<form novalidate autofill="off">
<form-control>
<label slot="label" for="firstName" class="control-label">First Name:</label>
<input slot="input" type="text" class="form-control"
value.bind="firstName & validateOnChange">
</form-control>
<form-control>
<label slot="label" for="lastName" class="control-label">Last Name:</label>
<input slot="input" type="text" class="form-control"
value.bind="lastName & validateOnChange">
</form-control>
</form>
</template>
Live example: https://gist.run/?id=874b100da054559929d5761bdeeb651c
please excuse the crappy tooltip css

knockout model - observable empty in IE 11

I have a set of input fields that are used to set search params, passed to the controller via knockout and ajax upon pressing the 'search' button.
Each input is bound to a property in a viewModel - each one is a ko.observable().
I have code bound to the keyup event for these fields that will invoke the same search operation when the return key is pressed.
In Chrome, this works fine, but in IE(11) this is never passed!
I have also noted that if I press the tab key to go to the next field, and THEN press return, the search parameter I expect is now populated.
Any ideas? at my wits end...
How do I do the same as pressing the tab key in code, without pressing the tab key?
I think IE's handling of the .change() event in jquery might be different to everyone else...
edited to include sample code - js:
var searchVm = function () {
var self = this;
self.reference = ko.observable("");
self.postCode = ko.observable("");
self.description = ko.observable("");
self.doSearch = function () {
var date = {
Ref: self.reference(),
PostCode: self.postCode(),
Desc: self.description()
};
// do the ajax call to controller
// ... etc ...
}
}
$('.searchField').keyup(function(e) {
if (e.which===13) {
$('#btnSearch').click();
}
});
the page:
<div class='indexRow'>
<label>Reference:</label>
<input type='text' class='searchField' data-bind="value: reference" />
</div>
<div class='indexRow'>
<label>PostCode:</label>
<input type='text' class='searchField' data-bind="value: postCode" />
</div>
<div class='indexRow'>
<label>Description:</label>
<input type='text' class='searchField' data-bind="value: description" />
</div>
<button data-bind='click: doSearch'>Search</button>