Dynamic form with repeating form
Here form username, password, mobile and address fields are repeated based on number user enters in the the page before this. In this picture the form is repeated twice but it could be any number of times which will be dynamic
ANS : you should go with recyclerview
How can I achieve something like this?
Create a layout like that for recyclerview
single item
how will I have to retrieve the typed in values?
You can get values from RecyclerView.Adapter
class
HERE IS THE SAMPLE CODE
Activity.java
public class AddmoreActivity extends AppCompatActivity {
RecyclerView myRc;
ArrayList<AddMorePojo> arrayList = new ArrayList<>();
Button btnGetData;
AddMoreAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_addmore);
myRc = (RecyclerView) findViewById(R.id.myRc);
btnGetData = (Button) findViewById(R.id.btnGetData);
myRc.setHasFixedSize(true);
myRc.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
AddMorePojo addMorePojo = new AddMorePojo();
addMorePojo.setAddress("");
addMorePojo.setUserName("");
arrayList.add(addMorePojo);
adapter = new AddMoreAdapter(this, arrayList);
myRc.setAdapter(adapter);
btnGetData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
ArrayList<AddMorePojo> pojoArrayList = adapter.getArrayList();
for (int i = 0; i < pojoArrayList.size(); i++) {
Log.e("Name " + i, pojoArrayList.get(i).getUserName() + "");
Log.e("Pass " + i, pojoArrayList.get(i).getPass() + "");
Log.e("PHONE " + i, pojoArrayList.get(i).getPhone() + "");
Log.e("Address " + i, pojoArrayList.get(i).getAddress() + "");
}
}
});
}
}
Activity.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.user33.workingtestapp.AddmoreActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/myRc"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:id="@+id/btnGetData"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:text="get Data" />
</RelativeLayout>
pojo class
public class AddMorePojo {
String userName, phone, pass, Address;
public AddMorePojo() {
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getPass() {
return pass;
}
public void setPass(String pass) {
this.pass = pass;
}
public String getAddress() {
return Address;
}
public void setAddress(String address) {
Address = address;
}
}
adapter class
public class AddMoreAdapter extends RecyclerView.Adapter<AddMoreAdapter.ViewHolder> {
Context context;
ArrayList<AddMorePojo> arrayList;
public AddMoreAdapter(Context context, ArrayList<AddMorePojo> arrayList) {
this.context = context;
this.arrayList = arrayList;
}
@Override
public AddMoreAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.addmorelayout, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(AddMoreAdapter.ViewHolder holder, int position) {
}
@Override
public int getItemCount() {
return arrayList.size();
}
public ArrayList<AddMorePojo> getArrayList()
{
return arrayList;
}
public class ViewHolder extends RecyclerView.ViewHolder {
EditText edtName, edtPhone, edtPass, edtAdrress;
public ViewHolder(View itemView) {
super(itemView);
edtName = itemView.findViewById(R.id.edtUname);
edtPhone = itemView.findViewById(R.id.edtPhone);
edtPass = itemView.findViewById(R.id.edtPass);
edtAdrress = itemView.findViewById(R.id.edtAddress);
edtName.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
AddMorePojo addMorePOJO = arrayList.get(getAdapterPosition());
addMorePOJO.setUserName(charSequence + "");
arrayList.set(getAdapterPosition(), addMorePOJO);
}
@Override
public void afterTextChanged(Editable editable) {
}
});
edtPhone.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
AddMorePojo addMorePOJO = arrayList.get(getAdapterPosition());
addMorePOJO.setPhone(charSequence + "");
arrayList.set(getAdapterPosition(), addMorePOJO);
}
@Override
public void afterTextChanged(Editable editable) {
}
});
edtPass.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
AddMorePojo addMorePOJO = arrayList.get(getAdapterPosition());
addMorePOJO.setPass(charSequence + "");
arrayList.set(getAdapterPosition(), addMorePOJO);
}
@Override
public void afterTextChanged(Editable editable) {
}
});
edtAdrress.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
AddMorePojo addMorePOJO = arrayList.get(getAdapterPosition());
addMorePOJO.setAddress(charSequence + "");
arrayList.set(getAdapterPosition(), addMorePOJO);
}
@Override
public void afterTextChanged(Editable editable) {
}
});
}
}
}
adapter custom layout
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardElevation="10dp"
app:cardUseCompatPadding="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<EditText
android:id="@+id/edtUname"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Enter User Name" />
<EditText
android:id="@+id/edtPass"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="********"
android:imeOptions="actionNext"
android:inputType="textPassword" />
<EditText
android:id="@+id/edtPhone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Enter Phone Number"
android:imeOptions="actionNext"
android:inputType="numberDecimal" />
<EditText
android:id="@+id/edtAddress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Enter Address"
android:imeOptions="actionNext"
android:inputType="text" />
</LinearLayout>
</android.support.v7.widget.CardView>
How to get values from dynamically repeated forms in FormGroup on Typescript?
You’re mixing reactive forms (eg. formControlName
) and template driven forms (eg. ngModel
). I’d suggest reading through the Angular docs on those before going further.
If you choose reactive forms, you’ll want to use a formArray
as they’re meant for exactly this. ngFor
exposes the index to the template, and your formControlName
will simply be the index.
You can set the value of reactive forms with patchValue
and setValue
from your ts.
I don't mean to suggest that you should use reactive instead of template driven, just that you shouldn’t mix them. If you’re more comfortable with template driven, it’s totally fine. The docs go into the pros and cons of each.
Good luck!
Dynamic form returns the same value for the first and second input
Issue
You are mutating the state in your handleFormObjectChange
and handleDatePickerChange
handlers.
const handleFormObjectChange = (inputEvent, inputIndex) => {
let deliveryPointListData = [...formObject.deliveryPointList];
// Mutates the nested property!!
deliveryPointListData[inputIndex][inputEvent.target.name] =
inputEvent.target.value;
setFormObject((current) => ({
...current,
deliveryPointList: deliveryPointListData
}));
};
const handleDatePickerChange = (inputNewValue, inputIndex) => {
let deliveryPointListData = [...formObject.deliveryPointList];
// Mutates the nested property!!
deliveryPointListData[inputIndex].deliveryDate = inputNewValue;
setFormObject((current) => ({
...current,
deliveryPointList: deliveryPointListData
}));
};
Solution
Ensure you are shallow copying all properties and nested properties that are being updated. This ensures all updates create new object references.
const handleFormObjectChange = (inputEvent, inputIndex) => {
let deliveryPointListData = [...formObject.deliveryPointList];
deliveryPointListData[inputIndex] = {
...deliveryPointListData[inputIndex], // <-- shallow copy
[inputEvent.target.name]: inputEvent.target.value
};
setFormObject((current) => ({
...current,
deliveryPointList: deliveryPointListData
}));
};
const handleDatePickerChange = (inputNewValue, inputIndex) => {
let deliveryPointListData = [...formObject.deliveryPointList];
deliveryPointListData[inputIndex] = {
...deliveryPointListData[inputIndex], // <-- shallow copy
deliveryDate: inputNewValue,
};
setFormObject((current) => ({
...current,
deliveryPointList: deliveryPointListData
}));
};
Dynamically repeat and remove form elements using JQuery
It is because you need to clone the elements, if not you will keep the same pointer and thus deleting one will result in deleting the whole list.
This solution mantains your style:
function addFormElements(current) {
$(current).parents('.form-list').append($(current).parents('.form-row').clone())
}
function removeFormElements(current) {
$(current).parents('.form-row').remove();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container">
<div class="row justify-content-center align-items-center">
<div class="col-10 col-md-10 col-lg-8">
<form>
<div class="form-list">
<div class="form-row" style="padding-top: 50px;" id="someId">
<div class="form-group col-md-4">
<select class="selectpicker form-control single-select" placeholder="Select an Option..." required>
<option></option>
<option>Mustard</option>
<option>Ketchup</option>
<option>Barbecue</option>
</select>
</div>
<div class="form-group col-md-6">
<input type="password" class="form-control" placeholder="">
</div>
<div class="form-group col-md-1">
<button type="button" class="btn btn-success" onclick="addFormElements(this)">Add</button>
</div>
<div class="form-group col-md-1">
<button type="button" class="btn btn-danger" onclick="removeFormElements(this)">Remove</button>
</div>
</div>
</div>
<div class="form-row" style="padding-top: 50px;">
<button type="button" class="btn btn-primary">
Submit
</button>
</div>
</form>
</div>
</div>
</div>
Dynamic multiple form insert with ng-repeat and each form has dynamic text box to get values
You are using a single object as ng-model
in input type instead use an object with several keys and values like ng-model="itemToAdd.ingredientName[formToAdd.ingredientId]"
this will create a unique key wrt to id and assign input text value to this key which will avoid recurring of same values in next form fields.
Add track by
to keep track of objects in the list which will help us creating new ng-model
object keys while adding new field.
<div ng-repeat="formToAdd in formsToAdd track by formToAdd.ingredientId">
<div ng-repeat="itemToAdd in itemsToAdd track by $index">
and the input fields change to
<label>Option Name</label>
<input type="text" placeholder="Option Name" name="ingredientName" ng-model="itemToAdd.ingredientName[formToAdd.ingredientId]" ng-pattern="/^[a-zA-Z ]{3,25}$/" class="form-control" autocomplete="off" ng-required="true" ng-change="verifyDuplicate()" />
<div class="validation_messages" ng-show="userFieldForm.$submitted || userFieldForm.ingredientName[formToAdd.ingredientId].$touched">
<span ng-show="userFieldForm.ingredientName[formToAdd.ingredientId].$error.required">Option cannot be empty</span>
<span ng-show="!userFieldForm.ingredientName[formToAdd.ingredientId].$error.required && userFieldForm.ingredientName[formToAdd.ingredientId].$error.pattern && userFieldForm.ingredientName[formToAdd.ingredientId].$dirty">Option Name Samples Cookie,Saz Mora</span>
</div>
Below are the simple changes in your JS
$scope.itemsToAdd = [{
ingredientName: {},//assign a new object everytime
ingredientPrice: ''
}];
In your HTML
<body ng-controller="MainCtrl">
Want to add Ingredients :
<button ng-click="showIngredientForm = !showIngredientForm" ng-class="{'red' : showIngredientForm}">{{toggleText}}</button>
<div ng-show="showIngredientForm">
<div ng-repeat="formToAdd in formsToAdd track by formToAdd.ingredientId">
<ng-form name="productFieldForm">
<div class="form-group">
<label>Option Type</label>
<select name="ingredientName" ng-model="formToAdd.ingredientId" class="form-control">
<option value="">Select Option Type Name</option>
<option ng-repeat="option in ingredientTypeList" value="{{option._id}}">{{option.ingredientTypeName}}</option>
</select>
<button ng-click="removeForm(formToAdd)" ng-if="formsToAdd.length > 1">Remove</button>
<!-- <div class="validation_messages">
<span ng-show="validCityingredientTypeName">ingredientType Name is required</span>
</div> -->
</div>
</ng-form>
<div ng-if="formToAdd.ingredientId">
<div ng-repeat="itemToAdd in itemsToAdd track by $index">
<div class="form-group">
<ng-form name="userFieldForm">
<table>
<tr>
<td>
<label>Option Name</label>
<input type="text" placeholder="Option Name" name="ingredientName" ng-model="itemToAdd.ingredientName[formToAdd.ingredientId]" ng-pattern="/^[a-zA-Z ]{3,25}$/" class="form-control" autocomplete="off" ng-required="true" ng-change="verifyDuplicate()" />
<div class="validation_messages" ng-show="userFieldForm.$submitted || userFieldForm.ingredientName[formToAdd.ingredientId].$touched">
<span ng-show="userFieldForm.ingredientName[formToAdd.ingredientId].$error.required">Option cannot be empty</span>
<span ng-show="!userFieldForm.ingredientName[formToAdd.ingredientId].$error.required && userFieldForm.ingredientName[formToAdd.ingredientId].$error.pattern && userFieldForm.ingredientName[formToAdd.ingredientId].$dirty">Option Name Samples Cookie,Saz Mora</span>
</div>
<div class='validation_messages' ng-if='itemToAdd.isDuplicate'>
<span>Duplicate Not Allowed</span>
</div>
</td>
<td>
<button ng-click="remove(itemToAdd)" ng-if="itemsToAdd.length > 1">Remove</button>
</td>
</tr>
</table>
</ng-form>
</div>
</div>
<div>
<button class="validation_messages" ng-click="addNew()">Add new</button>
</div>
</div>
</div>
<div>
<button class="validation_messages" ng-click="addForm()">Add new Form</button>
</div>
</div>
</body>
Here is the updated code
UPDATE
To remove extra boxex in every from on click of add new in another form add below code in JS
$scope.addNew = function(key) {
if ($scope.itemsToAdd[key]== null) {
$scope.itemsToAdd[key] = [];
}
$scope.itemsToAdd[key].push({
ingredientName: {},
ingredientPrice: ''
});
};
$scope.remove = function(itemToAdd,key) {
var index = $scope.itemsToAdd[key].indexOf(itemToAdd);
$scope.itemsToAdd[key].splice(index, 1);
};
In HTML for the inner ng-repeat
to itemsToAdd
<div ng-repeat="itemToAdd in itemsToAdd[formToAdd.ingredientId] track by $index">
Instead of a list we are basically creating itemsToAdd
object {}
and assigning it a key with a list object which holds new items for each and every form individually.
Related Topics
Bitmap Recycle with Largeheap Enabled
Android Fragment Lifecycle Over Orientation Changes
Multiline Edittext with Done Softinput Action Label on 2.3
How to Find Older Versions of Eclipse Adt
Scan_Results_Available_Action Return Empty List in Android 6.0
How to Split Comma-Separated Value in SQLite
Android Eclipse Ddms - Can't Access Data/Data/ on Phone to Pull Files
Installation Failed Due To: 'Null' - Android Studio 3.5
Getting "Socketexception:Connection Reset by Peer" in Android
Picasso Load Image from Filesystem
How to Implement a View Holder
How to Catch Sigsegv (Segmentation Fault) and Get a Stack Trace Under Jni on Android
Sending Text Messages Programmatically in Android
How to Use Apply a Custom Drawable to Radiobutton
How to Use Custom Font with Webview
Error Java.Lang.Classnotfoundexception: Com.Google.Android.Gms.Maps.Mapfragment in Google Map V2
Programmatically Change the Value of a Color Resource Obtained from API Response