Dynamic Form with Repeating Form

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
}));
};

Edit dynamic-form-returns-the-same-value-for-the-first-and-second-input

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



Leave a reply



Submit