I have 2 models, both JSON data. One is generated by user input in a form, the other is data provided by an API and is modified by user input in the same form.
here is the form in the view:
<form method="post" name="form" role="form" ng-controller="ContactFormController as ctrl" ng-submit="form.$valid && ctrl.sendMessage(input, ctrl.cartItems)" novalidate>
<p ng-show="success">Thanks for getting in touch!</p>
<p ng-show="error">Something went awry with your submission!, please try again.</p>
<div class="formgroup">
<legend>Express Order Form</legend>
<div id="formHeader"></div>
<fieldset>
<div class="inputItem">
<label for="name">Name:</label>
<input class="form-control" type="text" id="name" name="name" ng-model="input.name" required>
</div>
<div class="inputItem">
<label for="email">Email:</label>
<input class="form-control" type="email" id="email" name="email" ng-model="input.email" required>
</div>
<div class="inputItem">
<label for="School">School:</label>
<input class="form-control" type="text" id="school" name="school" ng-model="input.school" required>
</div>
<!-- Form Items -->
<div ng-repeat="item in ctrl.cartItems">
<div class="col-sm-6 cartItemLabel" ng-bind="item.label"></div>
<div class="col-sm-2 inputItem">
<input class="form-control" type="text" id="item.id" name="item.id" ng-change="ctrl.updateSub(item)" ng-model="item.qty">
</div>
<div class="col-sm-2 inputItem">
<input class="form-control" type="text" id="item.id" name="item.id" ng-model="item.value">
</div>
<div class="col-sm-2 inputItem">
<input class="form-control" type="text" id="item.id" name="item.id" ng-model="item.subTotal">
</div>
</div>
<!-- /Form Items -->
<div class="inputItem">
<label for="messsage">Message:</label>
<textarea class="form-control" rows="4" id="messsage" name="message" ng-model="input.message" required></textarea>
</div>
<div class="hidden">
<label for="honeypot">I promise I'm not a bot</label>
<input type="text" id="honeypot" name="honeypot" ng-model="input.honeyPot">
</div>
Here is the model that is NOT generate by the form above:
self.cartItems = [
{'id': 1, 'label': "Band-Aids (box)", 'value': 1.5, 'qty': 0, 'subTotal': 0},
{'id': 2, 'label': "Binders – 1/2”", 'value': 6.5, 'qty': 0, 'subTotal': 0},
{'id': 3, 'label': "Binders – 1”", 'value': 6.5, 'qty': 0, 'subTotal': 0},
{'id': 4, 'label': "Binders – 1 1/2”", 'value': 7.5, 'qty': 0, 'subTotal': 0},
{'id': 5, 'label': "Binders – 2”", 'value': 8.5, 'qty': 0, 'subTotal': 0}
]
I need to upload all data from both JSON arrays to an API. I tried to concatenate the 2 JSON arrays like this (the function that is called on submit in the form):
self.sendMessage = function( input, items ) {
var output = input.concat(items);
....[on to my $http call]
but I get a type error.
Forgive me if I left out info you need; I am a newbie to Angular.
You might like to take a look at angular.extend, I think it should solve your problem.
angular.extend doc
A type error usually means that a type is not what you expected. I would guess that input is not an array. Try changing your code to this:
var output = items.concat(input);
Edit: To further track down the issue, you can add a debugger; statement in your code to break in the code at runtime. Then you can verify the types are what you expect by opening the dev tools console in your browser while running that piece of code.
To all who helped me on this issue, a big thank you!
#ragingprodigy put me on the right track.
I had to change the input within the method in my controller to:
var output = [input].concat(items);
I also had to change the form elements' binding to include the controller (as ctrl)
ng-model="ctrl.input.[each form element on input]"
Thanks again to all who helped! I hope this answer helps someone else.
It would help if you could provide full code for investigation.
My guess is that you have an issue calling sendMessage, since 'input' is part of your model try calling it like this:
self.sendMessage = function(items ) {
var output = self.input.concat(items);
}
or if you have defined scope:
self.sendMessage = function(items ) {
var output = $scope.input.concat(items);
}
I usually use jquery with angular.
If this is your case, you could use $.extend (true, {}, obj1, obj2)
put your most valuable object at the end (probably the edited by the user), just in case you have the same property on both and wanna stay with only one of them.
Related
so I tried to implement reCaptcha v2 into my form in OctoberCMS. For that the SmallContactForm plugin by Janvince offers a settings area, where the secret key and the site key can be stored. It also implements the needed scripts. In the end, my code looks like this
<form method="POST" action="LINK" accept-charset="UTF-8" class="form-group" id="scf-form-id-contactForm" enctype="multipart/form-data"><input name="_handler" type="hidden" value="contactForm::onFormSend"><input name="_session_key" type="hidden" value="sessionkey"><input name="_token" type="hidden" value="token">
<div class="form-group"><label class="control-label required" for="contactForm-name" >Name</label><input id="contactForm-name" class="form-control" name="name" type="text" required=""></div>
<div class="form-group"><label class="control-label required" for="contactForm-email" >Email</label><input id="contactForm-email" class="form-control" name="email" type="email" required=""></div>
<div class="form-group"><label class="control-label " for="contactForm-betreff" >Betreff</label><input id="contactForm-betreff" class="form-control" name="betreff" type="text"></div>
<div class="form-group"><label class="control-label required" for="contactForm-nachricht" >Ihre Nachricht</label><textarea id="contactForm-nachricht" class="form-control" name="nachricht" rows="5" required=""></textarea></div>
<div id="_protect-wrapper-contactForm" class="form-group _protect-wrapper"><label class="control-label">Please clear this field</label><input type="hidden" name="_form_created" value="ididddhcfi"><input id="_protect-contactForm" name="_protect" class="_protect form-control" value="http://"></div>
<script>
document.getElementById('_protect-contactForm').setAttribute('value', '');
document.getElementById('_protect-wrapper-contactForm').style.display = 'none';
</script>
<div class="form-group">
<div class="g-recaptcha" data-sitekey="data-sitekey"></div>
</div>
<div id="submit-wrapper-contactForm" class="form-group"><button type="submit" data-attach-loading class="oc-loader bg-innotechblue text-white font-bold rounded px-4 py-2">Send</button></div>
</form>
<script src='https://www.google.com/recaptcha/api.js' async defer></script>
The function behind the scenes:
if( Settings::getTranslated('add_google_recaptcha') ) {
try {
$response=json_decode(file_get_contents("https://www.google.com/recaptcha/api/siteverify?secret=".Settings::get('google_recaptcha_secret_key')."&response=".post('g-recaptcha-response')."&remoteip=".$_SERVER['REMOTE_ADDR']), true);
} catch(\Exception $e) {
Log::error($e->getMessage());
$errors[] = e(trans('janvince.smallcontactform::lang.settings.antispam.google_recaptcha_error_msg_placeholder'));
}
I already double checked if I switched site key and secret key. I also tried to implement the script manually over and behind the form, but I always get an error:
Google reCAPTCHA validation error!
also this error is displayed twice, but I'm not sure if thats important. The Im not a robot button is rendered fine, and I works as well, but when I send the form, I get the error.
I hope someone can help me! thanks in advance!
So the I looked up the Errors in the CMS and got this:
file_get_contents(): https:// wrapper is disabled in the server configuration by allow_url_fopen=0
Turns out my server did not allow url fopen. After changing that, everything worked. I also wrote the author of the plugin. He changed the source code, so it should work without the allow_url_fopen parameter.
The zIndexOffset which i gave in bsConfig is not working. Also i need to enable autoclose on scroll...
Would anyone help me figure how to do it?
TYPESCRIPT
this.bsConfig = Object.assign(
{},
{
containerClass: this.datepickerColorTheme,
zIndexOffset: -3,
dateInputFormat: 'DD-MMM-YYYY',
showWeekNumbers: false
}
);
HTML
<div class="col-sm-9">
<input id="OfficialDob" placeholder="Date of Birth" class="form-control" bsDatepicker [bsConfig]="bsConfig"
formControlName="OfficialDob" />
</div>
</div>
Thanks
According to my research, that the best way to pass an Array of Objects through a POST method is by using the following name convention:
<!-- first student -->
<input type="text" name="students[][first]">
<input type="text" name="students[][last]">
<input type="text" name="students[][age]">
<!-- second student -->
<input type="text" name="students[][first]">
<input type="text" name="students[][last]">
<input type="text" name="students[][age]">
Body-parser setup:
var bodyParser = require("body-parser");
app.use(bodyParser.urlencoded({extended: true}));
When I typed in the post route "req.body.students", I was expecting an array of objects like this:
[ { first: Fname1, last: Lname1, age: Age1} , { first: Fname2, last: Lname2, age: Age2}
Instead, my console.log shows that I got this:
[ { first : [Fname1 , Fname2], last : [Lname1, Lname2], age : [Age1, Age2] } ]
What is going wrong with it? Cheers!
This is simply how the qa library handles data in the format.
It isn't how I would expect the library to behave, and it is different to how PHP (the obvious inspiration) handles data. So you might consider this a bug and join in the discussion on this issue.
You can work around the issue by using explicit indexes for your rows:
<!-- first student -->
<input type="text" name="students[0][first]">
<input type="text" name="students[0][last]">
<input type="text" name="students[0][age]">
<!-- second student -->
<input type="text" name="students[1][first]">
<input type="text" name="students[1][last]">
<input type="text" name="students[1][age]">
A test case:
var qs = require("qs");
var data = "students[0][name]=alice&students[0][age]=11&students[1][name]=bob&students[1][age]=12";
console.log(qs.parse(data));
Outputs:
{ students: [ { name: 'alice', age: '11' }, { name: 'bob', age: '12' } ] }
I am trying to submit an array of objects using a regular form, without AJAX, and am finding that instead of the request body being parsed into an array of objects it just has many fields corresponding to the names of the objects.
I know that when submitting an array of primitives, you simply fill many inputs with the same name and it will populate; however, I cannot seem to wrap my head around applying this to complex objects.
My form code is fairly straightforward:
<div class="col-sm-9">
<div class="row">
<div class="col-md-6">
<div class="form">
<div class="form-group">
<label for="attachment[0].name" class="control-label">Name</label>
<input name="attachment[0].name" class="form-control" value="First Name" type="text">
</div>
<div class="form-group">
<label for="attachment[0].uri" class="control-label">URI</label>
<input name="attachment[0].uri" class="form-control" value="First URI" type="text">
</div>
<div class="form-group">
<label for="attachment[0].description" class="control-label">Description</label>
<textarea rows="4" value="First Description" name="attachment[0].description" class="form-control">First Description</textarea>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form">
<div class="form-group">
<label for="attachment[1].name" class="control-label" >Name</label>
<input name="attachment[1].name" class="form-control" value="Second Name" type="text">
</div>
<div class="form-group">
<label for="attachment[1].uri" class="control-label">URI</label>
<input name="attachment[1].uri" class="form-control" value="Second URI" type="text">
</div>
<div class="form-group">
<label for="attachment[1].description" class="control-label">Description</label>
<textarea rows="4" name="attachment[1].description" class="form-control">Second Description</textarea>
</div>
</div>
</div>
</div>
I have made a sample repository demonstrating my issue; https://github.com/xueye/express-form-issue where you can just run node server.js, navigate to http://localhost:3000 and submit the entry; the request body will show up in your console, where it should show up as:
{ name: '',
type: '',
'attachment[0].name': 'First Name',
'attachment[0].uri': 'First URI',
'attachment[0].description': 'First Description',
'attachment[1].name': 'Second Name',
'attachment[1].uri': 'Second URI',
'attachment[1].description': 'Second Description' }
Is it possible to POST data in the way I am attempting to?
When you make a POST request, with a regular form or with JavaScript, you are sending formatted text to the server.
You can't send an array, but you can send a text representation of an array.
None of the data formats supported by forms come with a standard method to represent an array of data.
A pattern (introduced by PHP) uses square brackets to describe array-like structures.
This is similar to what you are using, except you sometimes use JavaScript style dot-notation. Switch to purely using square-brackets.
name="attachment[1][uri]"
… but you need to decode that data format on the server.
To do that in Express 4 use:
var bodyParser = require("body-parser");
var phpStyleParser = bodyParser.urlencoded({ extended: true })
and
app.post('/example', phpStyleParser, function(req, res) {
console.log(req.body);
res.json(req.body);
});
Is there any possible method to create a form with one or more elements like:
<form action="{% url "foo" %}" method="POST">
<div id="report_item">
<input type="text" name="dontknow">
<input type="date" name="dontknow">
</div>
<div id="report_item">
<input type="text" name="dontknow">
<input type="date" name="dontknow">
</div>
</form>
And to get a dictionary from this form as below:
{data:
{1: {
text: 'bla', date: '2014-03-02'
},
{2: {
text: 'second text', date: '2014-03-01'
}
}
Not quite, but you probably want to look at formsets. That gives you a series of repeated forms, which you can output on the template by just iterating through. Once you've validated the POST data if you really need a dictionary you can do something like:
data = {i: form.cleaned_data for i, form in enumerate(formset.forms)}