Angular 2 date pipe inside a FormControl input - forms

I have a dynamically generated Angular 2 FormGroup with multiple FormControl input fields. Some of the inputs are Dates, which are fetched from the server as unix timestamps.
What i would like to do is :
to be able to translate the unix timestamp to a human readable form,
when my FormGroup is populated, and also
translate the human
representation of the date to a unix timestamp when the form is
submitted.
Part 1 is somewhat simple, using Angular's date pipe like this :
<input class="form-control" [formControlName]="question.key"
[value]="this.form.controls[this.question.key].value | date:'dd/MM/yyyy'">
Where this.form is a reference to the FormGroup and this.question is a custom wrapper class based on the official tutorial about dynamic forms :
https://angular.io/docs/ts/latest/cookbook/dynamic-form.html
Trying to change the date input that way won't work because the pipe will constantly try to transform the input value, thus making the input unusable if not throwing an Invalid argument for pipe 'DatePipe' exception.
To clarify, i fill my form using the FormGroup.patchValue() api, and submit the form data using the FormGroup.getRawValue() api.
I have tried to use an Angular 2 date picker component, but they made my huge forms pretty slow, so i would like to do it without custom date pickers or any jQuery dependent widgets.
Thanks in advance.

One way to do such a thing would be to create a component for your input that implements ControlValueAccessor
A bridge between a control and a native element.
A ControlValueAccessor abstracts the operations of writing a new value
to a DOM element representing an input control.
Please see DefaultValueAccessor for more information.
Something like this should do the trick (not tested):
export const DATE_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => MyDateInput),
multi: true
};
#Component({
template:`<input #input (input)="onChange($event)" (blur)="touchCallback()" type="date" [attr.disabled]="disabled?true:null">`
selector:"my-input",
styles:[],
providers:[DATE_VALUE_ACCESSOR]
})
export class MyDateInput implements ControlValueAccessor{
#ViewChild("input")
input:ElementRef;
disabled=false;
changeCallback=(data:any)=>{};
touchCallback=()=>{};
onChange(event){
let timestamp=this.convertToTimestamp(event.target.value);
this.changeCallback(timestamp);
}
convertToTimestamp(formatedDate){
//TODO:implement
}
convertFromTimestamp(timestamp){
//TODO:implement
}
writeValue(obj: any){
let formatedDate=this.convertFromTimestamp(obj);
this.input.nativeElement.value=formatedDate;
}
registerOnChange(fn: any){
this.changeCallback=fn;
}
registerOnTouched(fn: any){
this.touchCallback=fn;
}
setDisabledState(isDisabled: boolean){
this.disabled=isDisabled;
}
}
then you should be able to use it like this:
<my-input class="form-control" [formControlName]="question.key"></my-input>
or
<my-input [(ngModel)]="myModel"></my-input>

Related

Why is there an "." In the option in the lit sample?

lit introduces an example of "Change detection" at the following URL.
https://lit.dev/playground/#sample=examples/properties-has-changed
Why is there a "." at the beginning of "date" when specifying the "date-display" option in line 16 of my-element.ts?
import { LitElement, html} from "lit";
import {customElement, property} from 'lit/decorators.js';
import {localDateFromUTC} from './date-utils.js';
import './date-display.js';
#customElement('my-element')
class MyElement extends LitElement {
#property() date?: Date;
render() {
return html`
<p>Choose a date:
<input type="date" #change=${this._dateChanged}></p>
<p><button #click=${this._chooseToday}>Choose Today</button></p>
<p>Date chosen: <date-display .date=${this.date}></date-display></p>
`;
}
_dateChanged(e: Event) {
const utcDate = (e.target as HTMLInputElement).valueAsDate;
if (utcDate) {
this.date = localDateFromUTC(utcDate);
}
}
_chooseToday() {
this.date = new Date();
}
}
Lit uses prefixes to indicate the type of expression in a component's template. The . prefix denotes a property expression; without the prefix it would be an attribute expression. Using a property expression makes it very easy and convenient to pass any JS object to a child element (in this case a Date object).
When using HTML attributes you need to be aware that they are always strings. JS data must be converted to a string on the parent element, and then possibly converted back to the corresponding JS type on the child element.
No such conversion is performed with property expressions, because the data stays in "JS land".
So, why not always use property expressions? Two examples come to my mind right away:
For a property expression to work you need to know an implementation detail of the child element, i.e. that it has a corresponding JS property. (If you're dealing with your own Lit based elements inside a single project that is not a problem.)
If you want to apply selectors based on attributes (e.g. for styling my-button[disabled] { /* CSS ... /* } or using querySelector).

Angular 2++ | NgForm: Form.Dirty is Always Dirty

Determine if NgForm Looks Exactly As It Did Before Any User-Input
It seems that form.dirty doesn't redact its value after it has been changed, and form.touched seems to always be false no matter what: dirty is touched, and touched is tetched.
template.html
<form #form="ngForm" (ngSubmit)="handleSubmission($event, {}, form)">
...
<input
#input
type="text"
[name]="item.title"
[(ngModel)]="item.estimate"
(ngModelChange)="handleEstimateChange(item, item.estimate, input, form)"
/>
...
</form>
component.ts
export class LeComponent {
#Input('data') public data: any;
public handleEstimateChange: Function;
constructor(private $: Sandbox) {
this.handleEstimateChange = $.debounce(this.handleEstimate.bind(this), (1000*0.2));
}
handleEstimate(item: any, estimate: number, input: HTMLInputElement, form: NgForm) {
if (!estimate) delete item.esitmate;
(this, item, estimate, input, form);
// Why does form.dirty never change back to pristine again???
}
}
In the TypeScript, I'm debouncing the ngModelChange handler to give Angular a chance to change the form.dirty value before I check it. This is because ngModelChange gets triggered before the NgForm object has been modified.
If !estimate, because estimate === "", then set it back to its original value of undefined. In this case, the form should look exactly like it did before any user-input had occurred.
However, when I put a breakpoint on the line right above the comment and I output form.dirty to the console, the NgForm never changes dirty back to false.
Is it possible to determine if the form looks exactly like it did before any user-input?
Obviously, I can write my own dirty logic, but wouldn't that mean that NgForm is kind of useless? There's got to be something I'm missing, right? How could dirty not mean dirty?
I've taken a look at some other SO questions -- the first one being similar but definitely not the question I am asking. They are asking if this is intentional -- I don't care; I'd like to know how to accomplish the goal above.
Close, but no cigar:
angular2 formcontrol stays dirty even if set to original value
Block routing if form is dirty [ Angular 2 ]
Angular 2 getting only the dirty values in a controlgroup
How do I programmatically set an Angular 2 form control to dirty?
Angular 2.x/4.x & bootstrap: patchValue does not alter dirty flag. Possible bug?
With template-driven forms and a very flat data model, I implemented it like this:
private currentProduct: IProduct;
private originalProduct: IProduct;
get isDirty(): boolean {
return JSON.stringify(this.originalProduct) !== JSON.stringify(this.currentProduct);
}
get product(): IProduct {
return this.currentProduct;
}
set product(value: IProduct) {
this.currentProduct = value;
// Clone the object to retain a copy
this.originalProduct = Object.assign({}, value);
}
But this only works for very simple cases.
As I mentioned in the comments, using reactive forms gives you more flexibility in managing your data model separate from your user entries.
What Was Most Useful
template.html
<form #form="ngForm" (ngSubmit)="handleSubmission($event, {}, form)">
...
<input
#input
type="text"
[name]="item.title"
[attr.name]="item.title"
[(ngModel)]="item.estimate"
(ngModelChange)="handleEstimateChange(item, item.estimate, input, form)"
/>
...
</form>
component.ts
export class LeComponent {
#Input('data') public section: any;
public handleEstimateChange: Function;
private resetFormControl = (input: HTMLInputElement, form: NgForm) => {
var name = input.name, control = form.controls[name];
control.reset();
// control.markAsPristine();
// control.setValue(undefined);
// control.updateValueAndValidity();
};
constructor(private $: Sandbox) {
this.handleEstimateChange = $.debounce(this.handleEstimate.bind(this), (1000*0.2));
}
handleEstimate(item: any, estimate: number, input: HTMLInputElement, form: NgForm) {
if (!estimate) this.resetFormControl(input, form);
(this, item, estimate, input, form);
// Why does form.dirty never change back to pristine again???
}
}
Note
[attr.name]="..." (template.html)
resetFormControl
Basically, simply deleteing the value was not enough because it was still present on the FormControl object (form.controls). To clear it properly, invoke control.reset() for the individual control -- this in-turn invokes .markAsPristine() which communicates to the parent NgForm. Also, input.name was empty as it was only represented by ng-reflect-name unless [attr.name] elucidated the same value -- [name] is really just there because its required by Angular.
Now, anytime an <input /> value changes -- and its falsey -- we reset the input ensuring that if all are falsey, Angular will automatically handle the NgForm's dirty-state correctly.

How to add ID and Class attribute on Input Type in Contact Form 7

I want to add Id and Class attribute on specific input type in our contact form 7.
When I am adding with below example, it apply Id and Class on SPAN tag just above the input Type
[radio amount id:amount class:amount-select default:1 "50" "100" "200" "500" "Other"]
Here is the screenshot of source which is generated from above code:
Thanks in advance
There is no way to do so through the contact form 7 plugin. You'll need to add some custom javascript to do so, for example for your specific [radio] tag you would do something like this inside the cf7 edit page,
<label> My radio button
[radio amount id:amount class:amount-select default:1 "50" "100" "200" "500" "Other"]</label>
[submit]
<script>
(function( $ ) {
$(document).ready( function(){
$('form.wpcf7-form input').each(function(){
var span = $(this).parent('span');
if(span){
var idAttr = span.attr('id');
$(this).attr('id',idAttr);
span.attr('id','');
}
//or you could also do this which is even less maintenance
var name = $(this).attr('name');
var type = $(this).attr('type');
switch(type){
case 'radio':
case 'checkbox':
name += '-'+$(this).attr('value');
}
$(this).attr('id',name);
});
});
})( jQuery );
</script>
this would move all the span ids to the input elements when the page loads, you could use the same logic to move non-wpcf7 classes to the input element too.
[Edit] I added an additional method which needs less maintenance, although keep in mind that for radio/checkbox elements you'll need to append the value to the id to make it unique.
If you are using SASS style rules why not extend the Contact form 7 classes to your own classes? With this approach there is need to write JavaScript.
See my example below:
.wpcf7-form label {
#extend .control-group__label;
}
.wpcf7-form-control {
#extend .control-group__control;
}
.wpcf7-submit {
#extend .btn;
#extend .btn-submit;
}

ReactJS dynamically call custom class

I am trying to setup a way to create form objects dynamically from some json values. Essentially, I have in my json the form object type and properties. I pass that type to a FormInput class that then calls the custom class containing the actual form object. My problem right now is that when I pass in the custom class name "TextInput" (this.props.formElementType) React just creates an element called 'textinput' instead of calling the class. It doesn't appear to like passing in a string, but wants just the classname. Essentially,...
TextInput = React.createClass({...})
...
FormItem = React.createElement(<TextInputPassedAsAString>, {...})
I am not sure if I can call a custom class this way or not, by passing a string. I need help with this implementation or a better idea as I am relatively new to React.
Below is all the relevant code starting with the children ending with the final render block. Please excuse the pseudo coffeescript.
TextInput
TextInput = React.createClass
handleChange: (event)->
this.setState
value: event.target.value
render: ->
React.createElement('label', {
value: this.props.formElementLabel
})
React.createElement('input', {
id: this.props.formElementID,
type: 'text'
})
module.exports = TextInput
FormElement
FormElement = React.createClass
render: ->
R.div(null,
React.createElement(this.props.formElementType, {
formElementID: this.props.formElementID,
formElementLabel: this.props.formElementLabel
})
module.exports = FormElement
The initial call/final render
React.createElement(FormElement, {
formElementType: 'TextInput',
formElementID: 'firstFormElement',
formElementLabel: 'First text input'
})
Well, the best way, easiest to reason about, etc. is probably to require TextInput in the module that's doing your final render. Create it there, passing in the other props to it, and then pass this created component to FormElement rather than passing the string representation of the component.
One way to do this more dynamically would be to have have a module that exports each dynamic component as a property/method on the export. Something like:
module.exports = {
TextInput: ...
}
Then when you're rendering you could pass in something like this:
myImports[json.formElementType]

Bind a form field in Play 2.0 with a constant value?

I have a scala form with several fields.The fields in the form map to the member variables of a Java class. I want to bind one of the fields(say userId) with a value (I dont want the user to enter values for this field. Instead i want to pass this as a parameter to the scala template). However, i was unable to manually bind a form field. Any help is highly appreciated.
See the sample below for easier understanding :
`#(itemForm: Form[Item], user: User)
#import helper._
#main("Item list") {
#if(user != null) {
#form(routes.Application.newItem()) {
#itemForm("userId") = #user.id /**I want to bind the userId form field */
#inputText(itemForm("title"))
#inputText(itemForm("description"))
#inputText(itemForm("price"))
<input type="submit" value="Create">
}
}
}`
In this case it would be better to pass it as action's argument (remember to modify routes declaration)
#form(routes.Application.newItem(user.id)){
....
you can also just use common html
<input type="hidden" name="userId" value="#user.id" />
edit:
Validation in action.Note: it doesn't make sense to display errors on the page next to hidden field, so you do not need placeholders for error messages. It's up to you to pass VALID value into the hidden field. Displaying validation errors to user who can not change the value of hidden field is bad conception.
public static Result newItem(){
Form<ItemModel> itemForm = form(ItemModel.class).bindFromRequest();
if (itemForm.hasErrors(){
return badRequest(newItemView.render(itemForm));
}
itemForm.get().save();
return ok("Your new item is saved...");
}