adjusting dynamic form to work with multiple sections - javascript

I have a script which dynamically adds rows to a form with default values:
$(document).ready(function() {
var defaults = {
'name[]': 'Name',
'email[]': 'Email',
'organisation[]': 'Organisation',
'position[]': 'Position'
};
var setDefaults = function(inputElements, removeDefault)
{
$(inputElements).each(function() {
if (removeDefault)
{
if ($(this).data('isDefault'))
{
$(this).val('')
.removeData('isDefault')
.removeClass('default_value');
}
}
else
{
var d = defaults[this.name];
if (d && d.length)
{
this.value = d;
$(this).data('isDefault', true)
.addClass('default_value');
}
}
});
};
setDefaults(jQuery('form[name=booking] input'));
$(".add").click(function() {
var x = $("form > p:first-child").clone(true).insertBefore("form > p:last-child");
setDefaults(x.find('input'));
return false;
});
$(".remove").click(function() {
$(this).parent().remove();
});
// Toggles
$('form[name=booking]').delegate('input', {
'focus': function() {
setDefaults(this, true);
},
'blur': function() {
if (!this.value.length) setDefaults(this);
}
});
});
For the following form:
<form method="post" name="booking" action="bookingengine.php">
<p><input type="text" name="name[]">
<input type="text" name="email[]">
<input type="text" name="organisation[]">
<input type="text" name="position[]">
<span class="remove">Remove</span></p>
<p><span class="add">Add person</span><br /><br /><input type="submit" name="submit" id="submit" value="Submit" class="submit-button" /></p>
</form>
I would now like to split the form into 2 sections, each of which can have rows added dynamically to it. The second section would only have spaces for name and email, so the form as a whole, before any additional rows are added, would look something like this:
But I'm not sure how to achieve this. Either I would create a separate form with a seperate script, and then would need to know how to submit the information from both forms together, or I would just have one form but would then need to work out how to add rows dynamically to each section.
Could someone help with this?
Thanks,
Nick

I've implemented this in a fully functional example here.
I cleaned up your code a little bit, but it's basically the same. The main addition is that I wrapped the inputs in a fieldset element (you could use a div just as well, but fieldset is the semantically correct element for grouping related input fields). Your 4-input section lives in one fieldset, and your 2-input section lives in another; the "Add Person" handler looks for the parent fieldset, clones the first child, and adds it into that fieldset. Conveniently, in your use case the defaults for the first fieldset are the same as those for the second fieldset, but it would be easy enough to set up multiple sets of defaults and pass them into the setDefaults function.
A few other changes to the code:
I split your setDefaults function into two different functions, setDefaults and removeDefaults - you weren't gaining anything by making them a single function, and splitting them makes the code more legible.
I used .delegate to assign the "Remove" handler - otherwise the "Remove" button wouldn't work for new input sets. I also created the "Remove" button with jQuery, rather than cloning it, because I assumed that it wouldn't make sense to include it for the first input set.
I used jQuery in a couple of places where you were using raw Javascript (e.g. getting and setting input values). I generally assume that jQuery is more reliable for cross-browser DOM access and manipulation, so if you're loading the library already there's rarely any point not using it for all but the simplest DOM functions.
I removed your .data calls, since you can get the same information by inspecting the class, and it's generally better to reduce complexity. It's possible that .hasClass('test') is slightly slower than .data('test'), but I don't think it should make any difference here.

Create one form. Put two divs inside of it. Have your script add/remove form elements to the appropriate div.
When you submit the form it should automatically submit all of the form elements in both divs since the divs are contained in the form.

Related

Add new AngularJS variables with JavaScript

I'm trying to make a dynamic form with AngularJS and JavaScript. The objective is to add how many inputs the user need and transform all those inputs in AngularJS variables that print it on body. So I got that code:
$(function(){
var number = 1;
$('a.add').click(function(e){
e.preventDefault();
$('#this_div_contains_settings').append('<input type="text" name="example'+number+'" ng-model="example'+number+'" placeholder="Anything">');
number++;
});
$('#this_div_contains_settings').on('click','a.design_button', function(e){
e.preventDefault();
$(this).parent().remove();
});
});
This function add a INPUT on my DIV with the different ng-model every time it run.
The problem is, it just work's if the {{example1}} is already on my BODY, if I add it later with another function, it just doesn't work.
I'm new with AngularJS, so I didn't understand if I need to "refresh" the AngularJS everytime I add new variable or something like that.
Any help will be appreciated :)
Using jQuery is the wrong way to solve this problem. Instead, create an array inside of your controller that will hold the models for all of these inputs.
Depending on how you define/create your controllers, $scope may be replaced with this
$scope.examples = [];
Next, create a function that will add a new example to the array:
$scope.addExample = function () {
$scope.examples.push("");
}
Now, in your template, create the inputs using an ng-repeat:
<div id="this_div_contains_settings">
<input ng-repeat="example in examples" type="text" name="example" ng-model="example" placeholder="Anything">
</div>
and have your "add" button call your addExample function on click:
<a class="add" ng-click="addExample()">Add Example</a>
Finally, remove all of the code that was included in your question.
And for your .design_button that removes all the examples, that's easy too:
<a class="design_button" ng-click="examples = []">remove all examples!</a>
by that same concept, you could even remove the need for the addExample function, however i tend to keep logic in the controller (well, actually in the services, but that's another topic) anyway rather than putting it in the template.

JavaScript onclick return

I am new to JavaScript and I'm still figuring things out.
I already searched the web for this but I'm not quite sure what keywords should I use. I am creating some program with a random number using html and JS.
So in my javascript (inside the tag)
I have something like:
var x;
function initRandom() { // I run this function at loading time (i.e: <body onload="initRandom();">)
x = Math.random();
}
function checkGuessedNumber() { // this just checks if the number guessed by the user is == to x and displays "correct" in a div if it is correct otherwise "incorrect"
}
So the main problems I am encountering is that
The html elements gets reset after submit. For example, the text fields becomes blank, the things I displayed in a div becomes blank. It just shows for a short period of time then gets reset
After that, the generated number becomes a different number I think the html page loads once more every time I click submit. And I don't like that to happen.
What I am having confusions on is the return statement on the onClick() attribute and how is it different on without return. See examples below:
CODE1:
<form onsubmit="return checkGuessedNumber();">
<input type="text"> // input for the number
<input type="submit"> // click if already sure of input number above
</form>
CODE2:
<form onsubmit="checkGuessedNumber();"> // notice no return here
<input type="text">
<input type="submit">
</form>
And finally if I'll just gonna put the checkGuessedNumber on <input type="submit" onclick="checkGuessedNumber();"> OR having a return before that.
Here's a live demo (click) of everything in this post.
First, don't use inline js (js functions in your html, like onclick). Read some of these results: Why is inline js bad?
Just for completeness, I'll explain how it works anyway:
This disables the submit nature of the button.
<input type="submit" onclick="return false;">
Now, if you want to use a function, you still need to produce the above result, so:
<input type="submit" onclick="return foo()">
and foo will have to return false, so that return foo() is the same as return false:
function foo() {
//do what you need to do;
return false;
}
I'll update this in a moment explaining the best practice, NOT using inline js.
The best element for a "button" is <button>, so I recommend that.
<button id="my-btn">Click Me!</button>
I gave it an id so that we can easily identify it in javascript. There are plenty of other ways to get element references, but that's another topic. Now, in javascript:
//get the element reference
var myBtn = document.getElementById('my-btn');
//this will make the button call function "foo" when it is clicked.
myBtn.addEventListener('click', foo);
function foo(event) {
//do whatever you want
}
If you assign an event listener to an element that has a default behavior, you can prevent the default behavior like this:
//the "event" object is automatically passed to the event handler
function foo(event) {
event.preventDefault();
//do what you want here
}

Multi-step form

i'm having a problem on how should i implement/build my form. here's the overview.
the first step of the form is to fill up the "Responsibility Center". however, the user can add multiple responsibility center. then the next step would be - each responsibility center added should have one or many "account codes". at the end of the form, before submitting it, all the data should be editable.
the result should be like this:
|**responsibility center**||**account codes**|
| center 1 || account code 1 |
| || account code 2 |
| center 2 || account code 1 |
etc..
i just need some idea on how the form should be built/implemented.
EDIT 1
This is what i've tried
1st step
2nd step
result
EDIT 2
i already know how to add multiple rows (like on the 2nd step) and i can implement that already on the first to the 1st step. so here are my questions:
how can i add account codes per responsibility center?
if what i've tried is not a practical way to implement it, then how should i do it?
Unfortunately, I began writing this answer before you posted the pics of your app. The ideas are still relevant, but I would have tailored my example more to what you are doing. Sorry about that.
I would use jQuery and AJAX to get the job done. jQuery to handle insertion of new elements to the DOM, and for field validation; AJAX to verify that no account codes are duplicated between RCs, or what have you. Personally, I would also use AJAX to handle the form submission instead of using the more traditional <form action= method=> because it gives greater control over the process and doesn't whisk the user off to another page before I am ready. However, it is easiest to describe the <form> example, and you can first build that and then change it over to using AJAX if you want.
The example from here is assuming a blank slate (i.e. I had not seen your sample app before writing this):
First, in your jQuery/javascript, you need a counter to keep track of each RC added. This can be in the <head> tags of your HTML/PHP, or it can be stored in a separate file. If you click on my name and look at other AJAX answers I've given, you'll see many useful examples.
<script type="text/javascript">
$(document).ready(function() {
var ctr = 0;
});
</script>
In your HTML, you need a DIV into which you will append each RC DIV. You also need a link/button/whatever for user to initiate creation of a new RC. This would be a brief form, even just [RC Title] and [Account Code] with a link/button/whatever to create another [Account Code] field and a [Done/Submit] button.
HTML:
<div id="container">
<form action="yourprocessorfile.php" method="POST" id="myform"></form>
</div>
<input type="button" id="mybutt" value="Add New RC" />
JAVASCRIPT/jQuery (again, inside the (document).ready() section above):
$('#mybutt').click(function() {
ctr++;
var str = 'RC TITLE:<br><input id="RC-"'+ctr+' class="RC" type="text"><br>ACCOUNT CODE<br><input id="AC-"'+ctr+' class="AC" type="text"><br>';
$('#myform').append(str);
});
When user presses [Done], use jQuery again to check that each [Account Code] field has been completed.
$('#done').click(function() {
$('.RC').each(function() {
if ($(this).val() == '') {
alert('Please complete all fields');
$(this).focus();
return false;
}
});
$('.AC').each(function() {
if ($(this).val() == '') {
alert('Please complete all fields');
$(this).focus();
return false;
}
});
$('#myform').submit();
});
Edit 2 / Question 1:
You can add new account codes linked to an RC by:
You need to somehow assign a unique data element to the RC, such as an incrementing ID
have a link for adding the new AC
use jQuery to get the ID of the nearest RC element
use .split() to split-off the numerical portion (assign to a var)
use that number when creating your AC
$('.add_AC').click(function() { //Note I used a class, so you can have a link for each RC
var num = $(this).parent().attr('id').split('-')[1];
var str = '';
});
In the above example:
==> Because I used a class, it will fire whenever ANY element with that class is clicked. Of course, when you create the button, you must add that class to the button def, as:
<input type="button" class="add_AC" value="Add Account Code" />
num ==> uses chained jQuery methods to, one-after-another, get the number portion of the RC's id.
$(this) ==> whichever [Add Account Code] button/link/whatever was clicked on.
.parent() ==> This may or may not be correct for your situation. This is the part where we traverse the DOM to find the RC element's ID code, which would look like this: RC-3. You will need to experiment with:
.parent().parent()
.sibling()
.parent().sibling()
.closest()
.prev() or .next()
Play with these selectors, with Dev Tools window opened. It should only take a handful of minutes to find your RC element -- or ask another question and post your HTML.
.attr('id') ==> Obviously, returns the text of the ID, in our case RC-3
.split('-')[1] ==> Creates an array with RC on one side (zero), and 3 on the other (1)
Hopefully this all gives you some idea of where to begin...

independently working div in Jquery

I am trying to make an independently working div which has a form inside of it.
I use jquery to calculate the price of a product depending of the user's selections in the form. However the user is able to add multiple items in his 'cart' so the form is duplicated to another div. The problem is that the calculation pattern can't separate these two divs and the calculation will be incorrect. The form is also interactive so it will be generated by the user's input. This is really complex set and renaming every variable by the 'product number' doesn't sound really efficient to me.
I'm kind of stuck here and i don't really know how to solve this problem. I had an idea that what if I put an iframe inside of the div and load my form and its calculation script inside of it, and then use post command to transfer the price of the product to the 'main page' to calculate the total price of all of the products the user wanted.
However it seems that jQuery scripts doesn't work independently inside of these iframes, they still have connection so they broke each other.
i will appreciate any kind of suggestions and help to solve this matter, thank you!
here's the code so far
Heres the body
var productNumber = 1;
<div id="div_structure">
</div>
<button id="newProduct" >Add new product</button><br \>
add new item
<!-- language: lang-javascript -->
$('#newProduct').click(function ()
{
$('<div id="productNo'+productNumber+'">')
.appendTo('#div_structure')
.html('<label onclick="$(\'#div_productNo'+productNumber+'\').slideToggle()">Product '+productNumber +' </label>'+
'<button onclick="$(\'#product'+productNumber+'\').remove()">Remove</button>');
$('<div id="div_product'+productNumber+'" style="display: none;">').appendTo('#product'+productNumber+'');
$('<iframe src="productform.html" seamless frameborder="0" crolling="no" height="600" width="1000">').appendTo('#div_product'+productNumber+'');
productNumber++;
});
it also has a function that allows the user to remove the inserted div.
Here's just few lines from the productform
$(document).ready(function()
{
$('#productCalculation').change(function ()
{
shape = $('input[name=productShape]:checked', '#productCalculation').val();
alert(shape);
});
});
<form id="productCalculation">
<div id="div_productShape" class="product1">
<h1>Select the shape of the product</h1>
<input type="radio" name="productShape" value="r1">R1</input><br \>
<input type="radio" name="productShape" value="r2">R2</input><br \>
<input type="radio" name="productShape" value="r3">R3</input><br \>
</div>
.
.
.
</form>
I translated all of the variables so they may not function correctly since i didn't test the translated version. So the problem is, if i try to make selections in the second generated div it wont even alert() the selected variable
There are two problems with this code: You say somewhere "I translated all of the variables so they may not function correctly since i didn't test the translated version. So the problem is, if i try to make selections in the second generated div it wont even alert() the selected variable". This is because event handlers are attached to elements that are in the DOM at that specific moment. To get it to work for all elements, use event delegation:
$(document).ready(function()
{
$(document).on( 'change', '#productCalculation', function ()
{
shape = $('input[name=productShape]:checked', '#productCalculation').val();
alert(shape);
});
});
Your other question is "My question in a nutshell: Is there a way to restrict jquery to function only in certain div even though i use the same variable names in the second div ". You can use the this variable to access the element the click was invoked on. From this element you can traverse the DOM if needed, for example with .parent().
$('div').on( 'change', function( e ) {
console.log( $(this).val() );
} );

Change input value or add a new input if it doesn't exist

Background: I need to append certain data to post; similar to what jQuery's ajaxSetup does that for asynchronous requests except I need it for native form submits.
I need to be able to add a couple of form fields to a form before it is submitted, but I want to make sure I don't add duplicate fields in case they're already there (i.e. original submit failed because of validation or something).
At first I thought something like this would be nice and coherent:
$("form").live("submit", function ()
{
var $this = $(this);
($this.find('#stuff') ||
$this.append('<input type="hidden" id="stuff" name="stuff" />'))
.val('some value');
// carry on with the native submit
// this is actually correct, as opposed to $this.submit()
// which would create a loop
this.submit();
});
Meaning look for #stuff, if it's not found create it, then set its value to "some value". However because the result of the .find() is actually a jQuery wrapper, it would be implicitly converted to a true meaning that even if there are no matching elements found, the .append() code would never be executed.
Is there a nice way to tackle this whole "look for an element and create it if it doesn't already exist" scenario?
change $this.find('#stuff') to $this.find('#stuff').length
edit
if you want to be able to all of it in one statement you can do
(
($this.find('#stuff').length && $this.find('#stuff')) ||
$('<input type="hidden" id="stuff" name="stuff" />').appendTo($this)
).val('some val');
To keep checking logic readable one would expand the conditional:
if ($this.find('#stuff').length) {
$this.find('#stuff').val('some val');
} else {
$this.append('<input type="hidden" id="stuff'" name="stuff" value="some val" />');
}
Alternatively one could just remove and re-add the element... I know, "dom operations are expensive" but if there are several fields to be operated on it's just so much prettier this way:
$this.find('#stuff', '#stuff2', ...).remove()
.append('<input type="hidden" id="stuff" name="stuff" value="some val" />...');

Categories

Resources