I'm having trouble in wanting to create multiples of the same html that I render in a certain class. For example, I have a div that might look like this
[Header goes here, is a input field] [Dropdown]
[TextArea]
[Submit]
[Add another field]
On add another field, I would like to clone this view and be able to add as many again.
Here's what I have so far:
var UpdateForm = React.createClass({
handleSubmit : function(e) {
e.preventDefault();
var title = React.findDOMNode(this.refs.title).value.trim();
var date = React.findDOMNode(this.refs.date).value.trim();
var body = React.findDOMNode(this.refs.body).value.trim();
if(!title||!date || !body ) {
return;
}
this.props.onSubmit({title:title, date : date, body : body});
React.findDOMNode(this.refs.title).value = '';
React.findDOMNode(this.refs.date).value = '';
React.findDOMNode(this.refs.body).value = '';
//React.findDOMNode(this.refs.sub).value = '';
},
render: function() {
return(
<div id = "This is what I want to duplicate on each button click">
<form className="form-horizontal" onSubmit={this.handleSubmit}>
<div class="form-control">
<label className="col-sm-0 control-label ">Title:</label>
<div class="col-sm-5">
<input className = "form-control" type = "text" placeholder="Title...." ref = "title"/>
</div>
</div>
<div class="form-control">
<label className="col-sm-0 control-label ">Date:</label>
<div class="col-sm-5">
<input className = "form-control" type = "text" placeholder="Date...." ref = "date"/>
</div>
</div>
<div className="form">
<label htmlFor="body" className="col-sm-0 control-label">Report Body:</label>
<div className="column">
<textarea className = "ckeditor" id = "ckedit" ref = "body"></textarea>
</div>
</div>
<div class="form-group">
<label className="col-sm-0 control-label"></label>
<div class="col-sm-5">
<input type = "submit" value="Save Changes" className = "btn btn-primary" />
</div>
</div>
</form>
<div className="btn-group">
<button type="button" className="btn btn-danger">Assign to</button>
<button type="button" className="btn btn-danger dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span className="caret"></span>
<span className="sr-only">Toggle Dropdown</span>
</button>
<ul className="dropdown-menu">
{
this.props.forms.map(function(form){
return (
<li>{form}</li>
)})}
</ul>
<button className="btn btn-success btn-block" onClick={this.addInputField}>Add Subform</button>
</div>
</div>
);
}
});
What I think I need to add:
addDiv: function(e) {
e.preventDefault();
//have a array of divs?
//push a the same div into it?
//then set state of that array?
}
I know in jquery I could just write a function that appends this markup whenever I hit a button, but i don't know how to think about it here at all.
I think what you want is to have a button which add another header-date-body-Component to the form, which should then also be submitted, right?
If so then you need to think more in components. Have one Component which handles the form. Have one around that which handles adding other forms.
<ReportDialog>
<ReportForm>
<ReportForm>
<button onClick={this.addReport}>Add another</button>
</ReportDialog>
To accomplish the multiple ReportForms you need to think about the data in your component, which are reports (I assume). So you need a state in ReportDialog which keeps track of your reports. so at the start of the app you have one report:
getInitialState: function () {
return {
reports: [{ title: '', body: '', date: new Date() }]
};
}
So in addReport you then need to change the state and add another report. To have these reports rendered you already used map, but this time you need to loop over the reports in your component and return a ReportForm for each report.
Related
i am building a project where we have one url input tag, which have its respective url mappings which needs to accept only URL and a add button to repeat two input tags
from below scenerio: url1 have urla -> urlb, urlc->urld, urle->urlf,....it has add button add as many as mappings. On save we need to send this json format to post API.
required json format :
{
url1: {
urla : urlb,
urlc : urld,
urle : urlf,...
},
url2:{
urlg : urlh,
urli: url,.....
},......
}
ISSUE : i m unable to get the required json format from my code.
`
HTML
<div>
<input id="url" type="text" ng-model="">
</div>
<div id="maprow1" class="row mt-5" ng-repeat="(privateUrl, publicUrl) in urlMappings track by $index">
<input id="privateurl1" type="text" class="form-control input-text http-input" ng-model="urlMappings.privateUrl">
<input id="publicurl1" type="text" class="form-control input-text http-input" ng-model="urlMappings.publicUrl">
<div id="mappingmain1" class="col-md-1 pr-0 text-right">
<button id="btnaddmapping1" class="button-default button-m panel-heading-margin" type="button" ng-click="addUrlMapping();" ng-if="$last">
<i class="di-block icon-add icon20 mr-0 v-sub"></i>
</button>
<button class="button-default button-m panel-heading-margin" type="button" ng-if="!$last" ng-click="slideUpModal();">
<i class="icon16 icon-delete v-sub di-block mr-0"></i>
</button>
<div id="addview" class=" pull-right p-20" ng-show="!displayModal">
<span class="button-cancel" data-dismiss="modal">Cancel</span>
<button id="addmapping" class="button-m button-primary" type="button" ng-click="addDomain();">Save</button>
</div>
</div>
controller:
$scope.urlMappings = [];
$scope.addUrlMapping = function() {
{
$scope.domainUrl ='';
$scope.privateUrl = '';
$scope.publicUrl = '';
const privateUrl = $scope.privateUrl;
const domainUrl = $scope.domainUrl;
var newUrlMappingField = {
[privateUrl]: $scope.publicUrl
};
$scope.urlMappings = {
[domainUrl] : newUrlMappingField
} };
`
So idea is Id like the user to click a button, have the page submit through the post method which will populate fields for a form, then return the page with a modal displayed. I have this working, except the issue is that the page is at the top even if the user clicked halfway down. Id like the page to redisplay where it was, with the modal displaying.
I know that there is a fragment parameter, but only for redirecttopage and not page()? Sending it back to get resets everything, so thats not realy ideal. Ive also tried altering the url with javascript which works with setting the page position, but somehow it will not display the modal with that?
Does anyone know how to easily return the page in the post method to a specific spot on the page?
Javascript: (Modal shows up for one second then disappers when it goes to the page position)
function OnLoad() { //Used for redisplaying on error purposes
if ("#TempData["EditingParty"]" == "Yes") {
var loadedURL = window.location.href;
var fragmentPieceOfURL = "#TempData["EndOfURL"]"
location.href = loadedURL + fragmentPieceOfURL;
document.getElementById("EditUsersForm").style.display = "inline-block";
}
else {
document.getElementById("EditUsersForm").style.display = "none";
}
}
Cshtml
Button that goes to post (i is counter to keep track of anchor tag for redisplay location):
<a id="#i"></a>
<button type="submit" style="white-space:nowrap" class="btn btn-secondary btn-sm" formnovalidate asp-page-handler="DisplayEdit" asp-route-id="#Model.UsersOnCaseList.ElementAtOrDefault(i).UserID" asp-route-AttorneyRole="#Model.UsersOnCaseList.ElementAtOrDefault(i).AttorneyRole" asp-route-email="#Model.UsersOnCaseList.ElementAtOrDefault(i).EmailAddress" asp-route-pAttorneyRoleID="#Model.UsersOnCaseList.ElementAtOrDefault(i).AttorneyRoleID" asp-route-PageFragment="#i" asp-route-pEditAttorneyUserID="#Model.UsersOnCaseList.ElementAtOrDefault(i).UserID">Edit <i class="fas fa-ribbon"></i></button>
Modal:
<div class="modal" tabindex="-1" role="dialog" id="EditUsersForm" style="display:none">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Edit User</h5>
<button type="button" onclick="closeEditUsersForm()" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="form-group" style="margin-top:15px;padding-left:15px">
<label asp-for="EditUserEmailInput"></label> <span class="red">*</span>
<input asp-for="EditUserEmailInput" class="form-control" id="EmailInputBox" value="#Model.EditUserEmailInput" style="display:block;width:25em" />
<span style="display:block" asp-validation-for="EditUserEmailInput" class="text-danger" id="AddUserInputValidation"></span>
</div>
<div class="form-group" style="margin-top:15px;padding-left:15px">
<label class="control-label">Role</label> <span class="red">*</span>
<select asp-for="AttorneyRoleIDEditForm" asp-items="#(new SelectList(Model.AttorneyRolesList, "AttorneyRoleID","AttorneyRole"))" name="AttorneyRoleIDEditForm" id="SelectAttorneyRoleIDEditForm" class="form-control" style="width:25em" onchange="DidSelectPlaintiffEdit()">
<option class="form-control" value=-1>Select a Role</option>
</select>
<span asp-validation-for="AttorneyRoleIDEditForm" class="text-danger"></span>
</div>
<div class="form-group" style="margin-top:15px;padding-left:15px">
<label asp-for="EditUserPartyInput"></label> <span class="red">*</span><br />
<input asp-for="EditUserPartyInput" value="#Model.EditUserPartyInput" class="form-control" id="PartyNameInputEdit" style="display:inline-block;width:25em" />
<span style="display:block" asp-validation-for="EditUserPartyInput" class="text-danger" id="PartyNameInputValidation"></span>
</div>
<div class="modal-footer">
<button class="btn btn-primary" style="margin:0 auto" asp-page-handler="SaveUserEdits" asp-route-UserBeingEdited="" id="EditSubmitButton" name="EditUserSubmit" value="Yes">Submit</button>
<button class="btn btn-primary" style="margin:0 auto; display:none" asp-page-handler="SaveUserEdits" disabled id="SubmitButtonDisabled" name="AddUserSubmit" value="Yes">Submit</button>
</div>
</div>
</div>
</div>
CS file:
public IActionResult OnPostDisplayEdit(int id, string email,string AttorneyRole,int pAttorneyRoleID,int pAttorney,int pEditAttorneyUserID,int PageFragment)
{
EditUserEmailInput = email;
string curDictionaryAttorneyRole;
if (AttorneyRole.Contains('('))//Just because in development user didnt always have ability to enter a party
{
curDictionaryAttorneyRole = AttorneyRole.Split(new[] { '(' }, 2)[0].Trim();
EditUserPartyInput = AttorneyRole.Split(new[] { '(' }, 2)[1];
var LastRightParenIndex = EditUserPartyInput.LastIndexOf(')');
EditUserPartyInput = EditUserPartyInput.Substring(0, LastRightParenIndex); //Pulling the right parenthesis out
}
else //Its just the dictionary role
{
curDictionaryAttorneyRole = AttorneyRole;
EditUserPartyInput = null;
}
TempData["EditingParty"] = "Yes";
TempData["EndOfURL"] = PageFragment.ToString() + "&#" + PageFragment.ToString();
SetFileSequenceField();
ModelState.Clear(); //Used to remove the validations
SetAttorneyRoleList();
var selectedIndex = AttorneyRolesList.Where(arl => arl.AttorneyRole == curDictionaryAttorneyRole).Select(arl => arl.AttorneyRoleID).FirstOrDefault();
AttorneyRoleIDEditForm = selectedIndex;
EditAttorneyUserID = pEditAttorneyUserID;
UneditedEmail = email;
UneditedAttorneyRoleID = pAttorneyRoleID;
PopulateUserList();
//return RedirectToPage("AddUsersToCase","GET",fragment:PageFragment.ToString());
return Page();
//var PageURL = Page();
//PageURL = PageURL. + "&#" + PageFragment.ToString();
//return PageURL;
}
You can try to replace
location.href = loadedURL + fragmentPieceOfURL;
to
history.pushState({}, null, loadedURL + fragmentPieceOfURL);
So that the page will not be refreshed with location.href.
I am using vue js and html for my project and i am doing a registration. So, I have few tabs. For going to next-tab "btn-next" is the class needed. But, i need to move to next tab only if the json response i receive is true.
So, i modified my html as
<div class="wizard-footer">
<div class="pull-right">
<div v-if="response.status == false">
<button type="submit" class='btn btn-primary'>Submit Again</button></div>
<div v-if="response.status == true">
<button type="submit" class='btn btn-next btn-primary'>Next</button></div>
<div v-else>
<button type="submit" class='btn btn-primary'>Submit</button>
</div>
</div>
</div>
But when I try this way.. i am not able to move to the next tab? can anybody please help me to have a solution..
I am able to get response and move through the different conditions but i am not able to move to next tab. it means btn-next is not working when i give inside div. So, please help me to find out a solution.
My detailed code is
<div class="tab-pane" id="step2">
<form method="POST" id="form1" v-on:submit.prevent="handleSubmit($event);">
<div class="row p-b-15 ">
<div class="row">
<div class="col-sm-12">
<div class="col-sm-6">
<div class="form-group">
<label>Contact Name :</label>
<input name="cname" type="text" class="form-control" id="cname" placeholder="Contact Name" required="required" v-model="cname" />
</div>
</div>
</div>
</div>
<div class="wizard-footer">
<div class="pull-right">
<div v-if="response.status == false"><button type="submit" class='btn btn-next btn-primary'>Next</button></div>
<div v-if="response.status == true"><button type="submit" class='btn btn-next btn-primary'>Next</button></div>
<div v-else>
<button type="submit" class='btn btn-primary'>Next</button>
</div>
</div>
</div>
</div>
</form>
</div>
My vue js code
submitBox = new Vue({
el: "#submitBox",
handleSubmit: function(e) {
var vm = this;
data = {};
data['name'] = this.cname;
data['pid'] = this.pid;
$.ajax({
url: 'hpost/contact/',
data: data,
type: "POST",
dataType: 'json',
success: function(e) {
if (e.status) {
vm.response = e;
console.log(vm.response);
alert("Registration Success")
} else {
vm.response = e;
console.log(vm.response);
alert("Registration Failed")
}
}
});
return false;
},
Case 1:
If the buttons are rendered correctly, please check your web server's backend code. I find that you are rendering a form with two "submit" buttons, maybe the backend does not know which submit is your expectation.
when response.status == false: Submit Again and Submit are rendered
when response.status == true: Next is rendered
but they are all normal submit buttons, does your backend or handleSubmit know that?
Case 2:
If the buttons are not rendered correctly, please check your JSON structure and vue syntax.
Noob question but I can get fields to render in Vue but not sure how to delete my fields. I added an index option in the v-for directives but not sure what to do after that. Thanks!
Here is a working JSFiddle: https://jsfiddle.net/xu55npkn/
<body>
<div id="app"></div>
<script>
const createNewOption = () => {
return {
text: '',
isAnswer: false
}
}
const createNewQuestion = () => {
return {
text: '',
options: [createNewOption()]
}
}
var vm = new Vue({
el: '#app',
template: `<div class="quiz-builder container">
<div v-for="question in questions">
<div class="input-group">
<input type="text" class="form-control" v-model="question.text" placeholder="Enter a question">
<span class="input-group-btn">
<button class="btn btn-danger" type="button">X</button>
</span>
<span class="input-group-btn">
<button class="btn btn-secondary" type="button" #click="addOption(question)">Add an option</button>
</span>
</div>
</br>
<div class="input-group" v-for="(option, index) in question.options" style="margin-bottom: 20px">
<span class="input-group-addon">
<input type="checkbox" v-model="option.isAnswer">
</span>
<input type="text" class="form-control" v-model="option.text" placeholder="Enter an option">
<span class="input-group-btn">
<button class="btn btn-danger" type="button">X</button>
</span>
</div></br>
</div>
<button class="btn btn-default" #click="addQuestion" :disabled="questions.length >= 5 ? true : false">
Add another question
</button>
<button class="btn btn-primary" style="background-color: #ffcc00; border: #ffcc00">
Create quiz
</button>
</div>`,
data () {
return {
questions: [createNewQuestion()],
showQuestions: false,
}
},
methods: {
addQuestion () {
this.questions.push(createNewQuestion())
},
removeQuestion (index) {
this.questions.shift(index)
},
addOption (question) {
question.options.push(createNewOption())
}
}
})
</script>
Based on your updated question, you have already solved for removing questions, although yev's answer is a much better way for removing questions.
To remove options, you need to add a new handler for removeOption that takes in both the question (which you are iterating over) and the option (which you are iterating over. Vue handles both of these scenarios for you. You can then find the index of the option and splice the array. See this fiddle.
template:
<button class="btn btn-danger" type="button" #click="removeOption(question, option)">
X
</button>
component:
removeOption (question, option) {
var index = question.options.indexOf(option);
if (index > -1) {
question.options.splice(index, 1);
}
}
Your delete button should look like:
<div v-for="(question, i) in questions">
<div>
<input v-model="question.text">
<span>
<button #click=removeQuestion(i)>X</button>
</span>
<span>
<button #click="addOption(question)">Add an option</button>
</span>
</div>
</div>
Notice I've added i (index) in your for loop and click handler for X button.
Your remove function will look like:
removeQuestion (index) {
this.questions.splice(index, 1);
}
Array.shift will remove only first item in the array which is not exactly what you want :)
I have an array of emails (as a part of a bigger model). These are displayed in seperate rows witha remove button for each (the address itself can be updated in the input box directly). Unfortunately I dont know how to do this in react when the inputs are renderd using a map function.
(I am converting a meteor blaze project to meteor react).
Everything renders but how do I attach to change event so I can update my array of emails? onChange + value need to be set somehow.
This is the map function
return this.data.emailGroup.emails.map((email) => {
return (
<div key={email} className="input-group">
<input type="text" className="form-control" onChange={self.handleEmailListChange} value={email}/>
<div className="input-group-btn">
<button type="button"
className="btn btn-default remove-email"><span
className="glyphicon glyphicon-remove"></span></button>
</div>
</div>
);
});
The initial state(which is populated with data from the db:
getInitialState() {
return {
name : '',
description: '',
emails : [],
newEmailAddress : ''
}
},
Upon request here is the render method(it requires a getContent method.The getcontent method is there because in meteor I need to wait for data so in the meantime I need a loading state.
getContent() {
return (
<div className="box box-success">
<div className="box-header with-border">
<h3 className="box-title">List of emails</h3>
</div>
<form role="form" className="form-horizontal">
<div className="box-body">
<p>Below is a list of email addresses which are added to this email group. If
you
want
to add more
you can add them one by one by inputting in the box below or add a list into
the
same box (email
addresses have to be seperated by either a space or ;) then press Add to add
to
the
list. You can edit
the addresses directly as well as remove them.</p>
<div className="input-group">
<input type="text" className="form-control"
value={this.state.newEmailAddress}
onChange={this.handleAddNewEmail}
placeholder="Email addresses seperated by a space or a semicolon ; i.e. test1#test.se;test2#test.se"/>
<span className="input-group-btn">
<button type="button" onClick={this.handleAddNewEmailButton} className="btn btn-info btn-flat add-email">Add</button>
</span>
</div>
<br/>
{this.renderEmail()}
</div>
</form>
</div>
)
},
render()
{
var contentStyle = {
minHeight : '946px'
};
return (
<div className="content-wrapper" style={contentStyle}>
<section className="content-header">
<h1>
{this.data.emailGroup? this.data.emailGroup.name : 'hello'}
</h1>
<small> Created by: Christian Klinton</small>
<br/>
<small> Last updated by: Christian Klinton - 2015-11-12 08:10:11</small>
<ol className="breadcrumb">
<li><i className="fa fa-dashboard"></i> Home</li>
<li>Email groups</li>
<li className="active">{this.data.emailGroup? this.data.emailGroup.name : 'loading'}</li>
</ol>
</section>
<section className="content">
<div className="row">
<div className="col-md-6">
<div className="box box-primary">
<div className="box-header with-border">
<h3 className="box-title">Information</h3>
</div>
<form role="form">
<div className="box-body">
<div className="form-group">
<label htmlFor="inputName">Name</label>
<input type="email" className="form-control" id="name"
onChange={this.handleNameChange}
placeholder="Set the name of the email group" autoComplete="off"
value={this.state.name}/>
</div>
<div className="form-group">
<label>Description</label>
<textarea className="form-control" rows="3" id="description"
placeholder="Enter a description what and how the template is used..."
onChange={this.handleDesriptionChange}
value={this.state.description}
></textarea>
</div>
</div>
</form>
</div>
</div>
<div className="col-md-6">
{this.data.emailGroup? this.getContent() : <p>Loading</p> }
</div>
<div className="form-group">
<div className="col-sm-offset-8 col-sm-4">
<div className="pull-right">
<button className="btn btn-primary">Delete all</button>
<button className="btn btn-primary save">Save</button>
</div>
</div>
</div>
</div>
</section>
</div>
)
}
React requires you to have something unique for every element in the rendered array, it's called a key, and it's an attribute.
If you don't know what to assign to the key, just assign it the array's indexes:
this.props.doors.map((door, index) => (
<div key={index} className="door"></div>
));
Here's the same solution applied to your problem:
return this.data.emailGroup.emails.map((email, index) => {
return (
<div key={index} className="input-group">
<input type="text"
className="form-control"
onChange={self.handleEmailListChange.bind(this, index)} value={email}/>
</div>
);
});
Notice how I bound handleEmailListChange to receive the index of the modified email. If handleEmailListChange accepts an index, it can update the modified email within the state:
handleEmailListChange: function(index, event) {
var emails = this.state.emails.slice(); // Make a copy of the emails first.
emails[index] = event.target.value; // Update it with the modified email.
this.setState({emails: emails}); // Update the state.
}
I believe what you're looking for is something like this:
MyPage = React.createClass({
mixins: [ReactMeteorData],
getMeteorData() {
// ...
},
render() {
const emails = this.data.emailGroup.emails.map((email) => {
return (
<div key={email} className="input-group">
<input type="text" className="form-control"
onChange={this.handleEmailListChange} value={email} />
<div className="input-group-btn">
<button type="button"
className="btn btn-default remove-email"><span
className="glyphicon glyphicon-remove" /></button>
</div>
</div>
);
});
return <div>
{emails}
</div>
}
});
I changed self to this. Since you're using the ES6 arrow function, there's no need to assign self = this.
You should place your Array.map directly inside your render() function. Just take care that each array element is wrapped inside a parent element (<div> here) and must have a unique key={}
class ArrayMap extends React.Component{
//your functions
handleEmailChanged(key){
// Handle email
}
render(){
return(
<div>
{
this.data.emailGroup.emails.map((email) => {
<div key={email.key}>
<button onClick={this.handleEmailChanged.bind(this,email.key)}/>
</div>
});
}
</div>
);
}
}