Angular 7 add new fields to form - javascript

I have a small form and when the user clicks on an element I want to display some more fields to the form. This action can be done multiple times. So my ideia is to have a separated html file with these fields to be appended to the form so I got this
public showMoreFields(): void {
const wrapper_div = document.getElementById("wrapper");
const template = require("./my-template.html")
container.innerHTML += template
}
The new fields are being appended properly.
My first question is: Is this the best approach to load external html? (I don't have the "text/template" script tag)
Or should I create a new component and append it to the maim form?
....
<input type="text" .... />
<my-new-fields></my-new-fields>
...
<button></button>
If so, how do I append new ones?
Also read about ngTemplateOutlet but didn't figure out how can apply to my case.
I am quite confused about this
Second. Although my new fields are being displayed the click events they are not triggering my functions.
exemple:
<span class="fa fa-remove" (click)="cleanInput()"></span>
// this is not executing the cleanInput function
Thanks

You can use *ngIf or [hidden] attributes on the section which you want to hide.
Example:
<input [(ngModel)] = "model1">
<your-component *ngIf="areExtraFieldsVisible">
</your-component>
<button (click)="showExtraFields()"></button>

1,
you can use flag in your component to show/hide data on button click:
in component:
showData: boolean = false;
in html:
<button (click)="this.showData=!this.showData">show/hide</button>
<div *ngIf="this.showData">
...
</div>

Related

How clone and init Datepicker via vanilla JS correctly in Tailwind Elements?

I use Tailwind Elements library with Tailwind CSS in my project.
Everything works perfectly, but there is one problem appeared... because I'm trying to clone the element and add to the page a copy.
I do the clone of a form with cloneNode method like this:
I have hidden HTML template:
<template id="temp">
<form>
<div class="datepicker relative mb-3" data-mdb-toggle-button="false">
<input type="text" placeholder="Select a date" data-mdb-toggle="datepicker" />
<label class="text-gray-700">Select a date</label>
</div>
</form>
</template>
When user clicks a button somewhere in the page "+ Add new form", then the new form will be added to the page. There can be many forms as user wants. So I want to use this template to clone it and insert new and new and new nodes to the page. JS:
import 'tw-elements'
let newForm = document.querySelector("#temp").content.cloneNode(true); // clone the <form>
newForm.appendChild(document.body); // add to the <body>
After these manipulations the new form added to the page, but the Datepicker does not work.
As I understood, I should call the initialization of the element programmably via JS like this:
let datePicker = newForm.querySelect('.datepicker');
datePicker.tweDatePicker(); // smth like that maybe
Right? But how? I cannot find in the docs how to call and init the Datepicker (or any other component) via vanilla JS programmably?
Maybe you know the better approaches.

How can I validate that dynamically generated fields are not empty?

I am working on a bootstrap environnement with classic asp.
I have a dynamically generated input fields
<input type="number" size="5" name="montantafacturer_<%=irs("INSPECTIONID")%>">
<button onclick="_EVENTSPARAM('events_ajouteralafacturation','<%=irs("INSPECTIONID")%>');">add</button>
There can be up to 100 dynamically generated fields on one page.
The basics are that i should fill the field montantafacturer_<%=irs("INSPECTIONID")%> with a numeric value and click on add to insert value in the database in a postback method
I am wondering if i can insert a javascript code to check if my field is filled rather than getting the field empty error from postback response... to gain time
ie :
<input type="number" size="5" name="montantafacturer_<%=irs("INSPECTIONID")%>">
<button onclick="**IF montantafacturer_<%=irs("INSPECTIONID")%>" IS NOT EMPTY THEN** _EVENTSPARAM('events_ajouteralafacturation','<%=irs("INSPECTIONID")%>');">add</button>
I wonder if this can be done via inline javascript.
Please advise how.
iid = irs("INSPECTIONID")
if iid <> "" then %>
<input type="number" size="5" name="montantafacturer_<%=iid%>">
<button onclick="_EVENTSPARAM('events_ajouteralafacturation','<%=iid%>');">add</button>
<$ end if %>
That way if your recordset is empty, no HTML is output. If you move the IF/THEN to just before the Button tag, then no button will be created for an empty value.
First of all, welcome to StackOverflow
Secondly ... It's been a very long while when I stopped using Classic ASP (more than 15 years ago), it's nice to see that he still has a small place out there :)
Last, but not the least... your question
as you have input and buttons, I'm sure you have a for loop and outside I will assume you have a <form> tag wrapping all inputs and buttons
To accomplish what you're trying to do, and making use of better code techniques, I would most likely end up with something as (and assuming that you can use jQuery to lift a bit of the javascript... let me know if you can't, and I'll re-write without it)
<form action="/yourpage.asp" method="POST">
<table class="table">
<tbody>
<% For ... %>
<tr class="tr-<%=irs("INSPECTIONID")%>">
<td>
<input
type="number"
size="5"
id="montantafacturer_<%=irs("INSPECTIONID")%>"
name="montantafacturer_<%=irs("INSPECTIONID")%>">
</td>
<td>
<button
class="btn"
data-event="events_ajouteralafacturation"
data-input="<%=irs("INSPECTIONID")%>"
>add</button>
</td>
</tr>
<% Next %>
</tbody>
</table>
</form>
<script>
$(function() {
// for every button with class "btn", fire "onButtonClick" fn upon click
$(".btn").on("click", onButtonClick);
});
function onButtonClick(evt) {
evt.preventDefault();
var btn = $(evt.currentTarget); // the clicked button
var btnEvent = btn.data("event");
var btnInput = btn.data("input");
// your logic
var input = $("#montantafacturer_" + btnInput).val();
if(input.length === 0) {
// show the error in any way you prefer
return;
}
// if we reach here, we have data in the input
_EVENTSPARAM(btnEvent, btnInput);
// you can now fire some code to submit the form
// or just this value, or even disable the button while it
// is being used to send the data (prevent double click), etc.
}
</script>
the <tr class="tr-<%=irs("INSPECTIONID")%>"> was a technique that I used back then so I could add a class that would mark that row with another color, to give some feedback to the user that something was happening with that row data, for example
$(".tr-" + btnInput).addClass("updating");
I've also added id to the input to use $("#...") instead of search by name
Small rant on using JS inline
Why would you ever use inline JS? It really isn't practical or readable - highly recommend moving that into an external source.
How to use JS inline (pls don't)
But if there is absolutely no way around for you, you can always just throw all your normal JS code inside an inline event like onclick.
<button onclick="
// this makes me sad
const allInputs = document.querySelectorAll('[name^=montantafacturer_]');
allInputs.forEach(input => {
if (input.value.length > 0 && !isNaN(input.value)) {
// number
} else {
// empty / Not a Number
}
});
">
add
</button>
This is about what you are looking for.
Mentions
Really, don't use inline JS
As pointed out by another user - you may want to use the HTML property required

Reset input fields of child component on parent's form submit [duplicate]

This question already has an answer here:
How to reset data and view of child input on click of parent's button click?
(1 answer)
Closed 5 years ago.
This is not a debugging problem, we're looking for the conceptual guidance on the best possible solution for the problem
In our Angular 2 app, we've a
<parent-component> which has a <form>
The <form> has <level-1-child>
The <level-1-child> has <level-2-child>
The <level-2-child> has <textarea>
What we need to do?
Reset the <form> elements of <parent-component>, <level-1-child> & <level-2-child> on <button> click or submit of <parent-component>'s <form>
Here is the issue re-producible
Without FormBuilder it's harder to do an actual reset, but you can do something like:
Parent component:
<form>
<level-1 [val]="myVal"></level-1>
</form>
Level 1:
<level-2 [val]="val"></level-2>
Level 2:
<textarea [(ngModel)]="val.someProp"></textarea>
Then simply add #Input() val: any to the level-1 and level-2 components.
So when you "reset" you will reset the myVal object to its original values. This will then take effect on the children as well.
Sidenote: I'm unsure if you have to update the object reference as well for it to take effect but you can then do that with this.myVal = Object.assign({}, this.myVal) should you need it.
You should also call resetForm on the <form> tag when you submit. So something like:
<form #form="ngForm" (ngSubmit)="save()"></form>
#ViewChild('form') form;
save(): void {
this.form.resetForm()
}
EDIT:
With FormBuilder you'd do:
Parent component:
public form: FormGroup = this.fb.group({
someProp: ''
});
<form [formGroup]="form">
<level-1 [val]="form"></level-1>
</form>
Level 1:
<level-2 [val]="val"></level-2>
Level 2:
<div [formGroup]="val">
<textarea formControlName="someProp"></textarea>
</div>
And then reset the form with this.form.reset().

React.js Bootstrap Add or Remove Input Fields

I'm building a web app using React.js and react-bootstrap. I have a form page where the user can type in symptoms of a disease that they are experiencing. I want the user to be able to type in text and also have the option to remove previously typed text.
An example of it being done in Javascript is here:
http://bootsnipp.com/snippets/featured/dynamic-form-fields-add-amp-remove-bs3
I would like to have the same feature as the link above but using React. Here's the code I have so far for the section, but I'm unsure of the best way to continue.
var closeButton = <Button onClick={this.removeSymptomField()}>Close</Button>;
<Input type="text" buttonAfter={closeButton} placeholder="Type a symptom here."/>
<Button onClick={this.addSymptomField()}>Click to add a new symptom.</Button>
When this.addSymptomField() is called, I want to add an Input field to the page, and when this.removeSymptomField() is called, I want to remove the existing Input field from the page.
Thank you so much!
You could keep a list of current inputs in state and modify that when calling addSymptomField and removeSymptomField
In your component constructor
this.state = { inputs: [] }
In render
<div className="inputs">
{this.renderInputs()}
</div>
And your renderInputs method could look like this
renderInputs() {
return this.state.inputs.map((input, index) => <Input key={index} type="text" buttonAfter={closeButton} placeholder="Type a symptom here."/>)
}
Then simply add or remove inputs to/from the list when calling the addSymptomField and removeSymptomField methods

ngShow and ngHide when in a ngRepeat loop, button to add element only first time it shows all the elements

I have this issue:
Inside a ngRepeat loop the form elements are hidden and only displayed when an edit button is clicked for each element.
There is also a button at the end that adds an element to the list, and it should show the form for only the new empty elemnt added.
When the add button is clicked first time, all the forms are displayed - it is clearly not intended -. But if I show the form elements and afterwards I hide them (submitting the form), when I click to the add button, it does not display all the forms, only the new one - what I actually intended -.
The code is straightforward:
<div ng-controller='TriggerCtrl' ng-model='triggers'>
<ul>
<li ng-repeat='trigger in triggers'>
<span ng-hide='edit'>{{trigger.operator}}</span>
<button ng-click='edit=!edit' ng-hide='edit'>Edit</button>
<form name='editTrigger' ng-show='edit' ng-submit='submit'>
<select ng-model='trigger.operator' ng-options='op.value as op.name for op in operators' />
<input id='submit' type='submit' value='Submit' ng-click='edit=!edit' />
</form>
</li>
</ul>
<button ng-click='add()'>Add</button>
</div>
And the javascript:
function TriggerCtrl($scope) {
$scope.triggers = [
{operator: 'gt'},
{operator: 'gte'}
];
$scope.operators = [
{name: 'greater than', value: 'gt'},
{name: 'greater than equal', value: 'gte'}
];
$scope.add = function () {
$scope.triggers.push({});
$scope.edit = true;
};
};
You can see the effect in jsfiddle http://jsfiddle.net/gserra/NG76R/1/
To test it, first click to add button and see that all forms are displayed. Then reload, and then first, click on edit and then click on submit. Afterwards, if you click in add button, only the new form will be displayed.
It's a problem with scope inheritance. basically you have a $scope.edit in your main controller and each iteration of your ng-repeat has it's own $scope.edit. If you change the $scope.edit in the parent scope/controller it will change the children $scope.edit's, but changing the children $scope.edit's (e.g. when you click the submit buttons) will not change the global $scope.edit. See my fiddle where I try to demonstrate this:
http://jsfiddle.net/773Pf/
There's probably different ways to achieve what you want so I'll just tell you what's wrong right now. One Hint: look into inheritance in javascript specifically objects vs. primitives.
Adding to NicolasMoise's answer, you may use global scope array to record the state of your individual entries instead of using local $scope.edit's.
The html could be like this:
<li ng-repeat='trigger in triggers'>
<p ng-hide='$parent.editArray[$index]'>{{trigger.type}} - {{trigger.operator}}</p>
<form name='editTrigger' ng-show='$parent.editArray[$index]' ng-submit='submit'>
<select ng-model='trigger.operator' ng-options='op.value as op.name for op in operators' />
<input id='submit' type='submit' value='Submit' ng-click='$parent.editArray[$index]=!$parent.editArray[$index]' />
</form>
</li>
And the global edit button would call a function in the controller like this:
$scope.toggleEdit = function() {
$scope.edit = !$scope.edit;
for (var i=0; i<$scope.editArray.length; i++)
$scope.editArray[i]=$scope.edit;
}
Here's the fiddle that fixes your problem:
http://jsfiddle.net/773Pf/2/
I think this is this should have fixed your problem if I understand it correctly.

Categories