I have a MVC5 setup with two Dropdowns that via Javascript automatically submits when a value is selected.
They are currently inside the same form, so i would like to have them submit to different Actions on my Backend
View:
<div class="panel-body">
#using (Html.BeginForm("", "BrugerSession"))
{
#Html.AntiForgeryToken()
<div class="row">
<div class="col-md-6">
#Html.LabelFor(model => model.Emails)
#Html.DropDownListFor(x => x.ValgtEmail, Model.Emails, "Vælg Email")
</div>
<div class="col-md-6">
#Html.LabelFor(model => model.Printere)
#Html.DropDownListFor(x => x.ValgtPrinter, Model.Printere)
</div>
</div>
}
</div>
JavaScript
$(document).ready(function () {
$("#ValgtEmail").change(function () {
$(this).closest('form').trigger('submit');
});
});
$(document).ready(function () {
$("#ValgtPrinter").change(function () {
$(this).closest('form').trigger('submit');
});
});
The trick here is, that i am using How do you handle multiple submit buttons in ASP.NET MVC Framework? to support multiple submit-targets in the Backend.
Why main Question is: Can the Javascript Trigger method submit the data in the propper way, so it will works with the Solution from the above link?
I tried looking into the http://api.jquery.com/trigger/ Documentation and there is support for additional parameters. But i do know how to format my Javascript to achieve what I need.
Update:
I never managed to get this working. Instead i surrounded each Select with its own form.
I hope my answer will help you. You can use JQuery AJAX.
First drop down
$("#dropdown1").change(function(){
$.ajax({
url: '/ControllerName/Action1',
data: { parm1:parm1 },
success: function () {
// Success function
},
error: function () {
// Error message
}
});
});
Second drop down
$("#dropdown2").change(function(){
$.ajax({
url: '/ControllerName/Action2',
data: { parm1:parm1 },
success: function () {
// Success function
},
error: function () {
// Error message
}
});
});
Related
I am new to js and would like to convert my JQuery-based js to Vue. I want to send a get request and output back the data. What is the best way of doing this?
Here is the html:
<div>
<div>
<input type="text" id="var1" placeholder="Search...">
</div>
<div>
<button id="submit">Submit</button>
</div>
</div>
<div>
<p>Results</p>
<p id="results"></p>
</div>
Below is my js:
$(document).read(function() {
$('#var1').keypress(function(e) {
if (e.keyCode == 13)
('#submit').click();
});
});
$(document).ready(function() {
$("#submit").click(function() {
var var1 = document.getElementById("var1").value
// sends get request to URL
$.getJSON("URL" + var1, function(search, status) {
console.log(search);
// cont.
$("#results").text(search.results);
});
});
});
EDIT: Here is what I have so far with axios:
function performGetRequest() {
var var1 = document.getElementById('var1').value;
axios.get('URL', {
params: {
id: var1
}
})
.then(function (response) {
console.log(search);
})
}
I am not sure if the above code is correct or how to factor in keypress and click-- is there a simple way to do that?
Well, I am not sure what you want to do with this ajax call, but hopefully this may help you. Vue is data driven, so I always try to focus on that aspect. So this is an example of how you can access and input and send the data using axios.
<div>
<div>
<input v-model='input' type="text" placeholder="Search...">
</div>
<div>
<button #click="searchInput()">Submit</button>
</div>
</div>
<div>
<p>Results</p>
<p >{{ result }}</p>
</div>
you must have those models in your data
// Your data
data() {
return {
input: '',
result: '',
}
}
and your method will look something like this.
searchInput() {
axios({
method: 'GET',
url: url + this.input,
}).then(response => {
this.result = response.data;
}).catch(error => {
//handle error
})
}
So this is a very basic example. you can do the same process in different ways, you could pass the input to the method or loop over the results, but the idea is taking advantage of Vue.js data driven system and think data first.
Hopefully this will help you, remember to escape your input and add necessary validations. Good luck
I am trying to create an AutoComplete textbox, while using an EditorTemplate. The problem I am facing is that by using the Html.BeginCollectionItem() extension solution (https://www.nuget.org/packages/BeginCollectionItem/), the Id's of the EditorFor() and TextBoxFor() methods get set dynamically and this breaks my javascript. Next to that, I do not exactly know if this is even possible (and if so, how. Below you will find how far I have come).
In the main view I have a loop to generate a partial view for each item in a collection
for (int i = 0; i < Model.VoedingCollection.Count; i++)
{
#Html.EditorFor(x => x.VoedingCollection[i], "CreateVoedingTemplate")
}
The partial view CreateVoedingTemplate.cshtml uses the Html.BeginCollectionItem() method
#using (Html.BeginCollectionItem("VoedingCollection"))
{
string uniqueId = ViewData.TemplateInfo.HtmlFieldPrefix.Replace('[', '_').Replace(']', '_').ToString();
string searchId = "Search_";
string standaardVoedingId = "StandaardVoeding_";
foreach (KeyValuePair<string, object> item in ViewData)
{
if (item.Key == "Count")
{
searchId = searchId + item.Value.ToString();
standaardVoedingId = standaardVoedingId + item.Value.ToString();
}
}
<div class="form-horizontal">
<div class="form-group" id=#standaardVoedingId>
#Html.LabelFor(model => model.fk_standaardVoedingId, "Voeding naam", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.HiddenFor(model => model.fk_standaardVoedingId)
<input type="text" id='#searchId' placeholder="Search for a product"/>
</div>
</div>
</div>
<script type="text/javascript">
var id = '#uniqueId' + '_fk_standaardVoedingId'.toString();
var search = '#searchId'.toString();
var url = '#Url.RouteUrl("DefaultApi", new { httproute = "", controller = "AgendaApi" })';
$(document.getElementById(search)).autocomplete({
source: function (request, response) {
$.ajax({
url: url,
data: { query: request.term },
dataType: 'json',
type: 'GET',
success: function (data) {
response($.map(data, function (item) {
return {
label: item.standaardVoedingNaam,
value: item.standaardVoedingId
}
}));
}
})
},
select: function (event, ui) {
$(document.getElementById(search)).val(ui.item.label);
//$('#id').val(ui.item.value);
document.getElementById(id).value = ui.item.value;
return false;
},
minLength: 1
});
</script>
}
<link href="~/Content/SearchBox/jquery-ui.css" rel="stylesheet" />
<script src="~/Scripts/SearchBox/jquery-1.9.1.js"></script>
<script src="~/Scripts/SearchBox/jquery-ui.js"></script>
In the script above, I am trying to make a function where the user can type in the name of a standaardVoeding item and then get results, where, after the user selects a standaardVoeding item, the standaardVoedingId property gets set. Then, after submitting the whole form, the controller receives the standaardVoedingId (with all the other info as well)
So I guess Javascript somehow cannot handle the Razor View # code and, next to that, Html.BeginCollectionItem does something fishy because you cannot set the value of its textboxes via code during runtime. Next to that, I have tried doing alert(document.getElementById(*html.begincollectionitemId*)) and it finds the fields fine. But apparently all other methods do not work?
Is there perhaps a better solution to getting this to work?
The BeginCollectionItem() method alters the id and name attributes of the html generated by the inbuilt helpers, in your case, for the hidden input, instead of
<input ... name="fk_standaardVoedingId" .... />
it will generate
<input ... name="VoedingCollection[xxxx].fk_standaardVoedingId" .... />
where xxxx is a Guid.
While it would be possible to use javascript to extract the Guid value from the textbox (assuming that was generated correctly usind #Html.TextBoxFor()) and build the id of the associated hidden input to use as a selector, it is far easier to use class names and relative selectors.
You also need to remove your scripts and css from the partial and put that in the main view (or its layout). Apart from the inline scripts, your duplicating it for each item in your collection.
Your partial needs to be
#using (Html.BeginCollectionItem("VoedingCollection"))
{
<div class="form-horizontal">
<div class="form-group">
#Html.LabelFor(model => model.fk_standaardVoedingId, "Voeding naam", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10 item"> // add class name
#Html.HiddenFor(model => model.fk_standaardVoedingId)
<input type="text" class="search" placeholder="Search for a product"/>
</div>
</div>
</div>
}
Note the class name for the textbox and its container which also includes the hidden input. Then in the main view, the script will be
<script type="text/javascript">
var url = '#Url.RouteUrl("DefaultApi", new { httproute = "", controller = "AgendaApi" })';
// Attach the script to all textboxes
$('.search').autocomplete({
source: function (request, response) {
$.ajax({
url: url,
data: { query: request.term },
dataType: 'json',
type: 'GET',
success: function (data) {
response($.map(data, function (item) {
return {
label: item.standaardVoedingNaam,
value: item.standaardVoedingNaam, // this needs to be the name
id: item.standaardVoedingId // add property for the id
}
}));
}
})
},
select: function (event, ui) {
// Get the associated hidden input
var input = $(this).closest('.item').find('input[type="hidden"]');
// Set the value of the id property
input.val(ui.item.id);
},
minLength: 1
});
</script>
Based on your comments that your are not dynamically adding or removing items in the view, then there is no point in the extra overhead or using the BeginCollectionItem() method. Change the name of your partial to standaardvoeding.cshtml (assuming that's the name of the class) and move it to the /Views/Shared/EditorTemplates folder.
Then in the main view, replace your for loop with
#Html.EditorFor(m => m.VoedingCollection)
which will generate the correct html for each item in the collection. Finally remove the BeginCollectionItem() method from the template so that its just
<div class="form-horizontal">
<div class="form-group">
#Html.LabelFor(m => m.fk_standaardVoedingId, "Voeding naam", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10 item"> // add class name
#Html.HiddenFor(m => m.fk_standaardVoedingId)
<input type="text" class="search" placeholder="Search for a product"/>
</div>
</div>
</div>
I want to pass data from a simple HTML form to a controller through Ajax, then process the data and return a response back.
At the moment I have the following:
HomePage.ss
<form method="POST" class="form-horizontal submit-form" onsubmit="return checkform(this);">
<!-- Text input-->
<div class="form-group">
<label class="col-md-4 control-label" for="name">Name</label>
<div class="col-md-8">
<input id="name" name="name" type="text" placeholder="insert full Name" class="form-control input-md" required="" />
</div>
</div>
<!-- Button -->
<div class="form-group">
<label class="col-md-4 control-label" for="send-btn"></label>
<div class="col-md-8">
<button id="send-btn" name="send-btn" class="btn btn-primary">Submit</button>
</div>
</div>
</form>
JavaScript
$('form.submit-form').submit(function() {
$.ajax({
type: 'POST',
url: 'processForm',
data: $(this).serialize(),
success: function(data) {
alert('data received');
}
});
});
HomePage.php
class HomePage_Controller extends Page_Controller {
public function events() {
$events = CalendarEvent::get();
return $events;
}
public function processForm() {
if (Director::is_ajax()) {
echo 'ajax received';
} else {
//return $this->httpError(404);
return 'not ajax';
}
}
}
In developer tools I can see that I got the xhr processForm with a 404 not found error.
How do I get this Ajax form working correctly with the SilverStripe controller?
Spider,
I've done something similar to below. This is a quick and dirty demo and hasn't been tested, but it may get you going in the right path. If you're unfamiliar with how forms work within SilverStripe there is a lesson for front end forms in SilverStripe. I've found the lessons useful personally and provide the code for the lesson as well: http://www.silverstripe.org/learn/lessons/introduction-to-frontend-forms?ref=hub
Page.php
<?php
class Page extends SiteTree
{
}
class Page_Controller extends Content_Controller
{
private static $allowed_actions = array(
'MyForm',
);
public function MyForm()
{
Requirements::javascript(THIRDPARTY_DIR . '/jquery/jquery.min.js');
Requirements::javascript(THIRDPARTY_DIR . '/jquery-validate/jquery.validate.min.js');
Requirements::javascript('/path/to/your/validation/script.js');
$fields = FieldList::create(
TextField::create('name')
->setTitle('Name')
);
$actions = FieldList::create(
FormAction::create('doSubmit')
->setTitle('Submit')
);
$requiredFields = RequiredFields::create(
'name'
);
$form = Form::create($this, 'MyForm', $fields, $actions, $requiredFields);
return $form;
}
public function doSubmit($data, $form)
{
//process $data or create your new object and simpley $form->saveInto($yourObject); then $yourObject->write()
//then deal with ajax stuff
if ($this->request->isAjax()) {
return $this->customise(array(
'YourTemplateVar' => 'Your Value'
))->renderWith('YourIncludeFile');
} else {
//this would be if it wasn't an ajax request, generally a redirect to success/failure page
}
}
}
YourValidationScript.js
(function ($) {
$(function () {
$('#MyForm_Form').validate({
submitHandler: function (form) {
$.ajax({
type: $(form).attr('method'),
url: $(form).attr('action') + "?isAjax=1",
data: $(form).serialize()
})
.done(function (response) {
$('.content').html(response);
})
.fail(function (xhr) {
alert('Error: ' + xhr.responseText);
});
},
rules: {
name: "required"
}
});
})
})(jQuery);
You need to understand how HTTP request routing is handled in SilverStripe.
When you send request POST /processForm, it is treated as page and managed by ModelAsController. That is why you get 404 error - there is no SiteTree record with URLSegment = processForm.
Solution 1
Use Form object. It creates all routing configuration automatically during runtime. Read more
https://docs.silverstripe.org/en/3.3/tutorials/forms/
https://docs.silverstripe.org/en/3.3/developer_guides/forms/
Solution 2
Use this approach, when you really want to go down to the simple one method request handler. Register custom controller and routing.
You specify your route in mysite/_config/routing.yml
---
Name: siteroutes
---
Director:
rules:
processCustomForm: CustomFormController
Handle your request
class CustomFormController extends Controller
{
public function handleRequest( SS_HTTPRequest $request, DataModel $model ) {
if (!$request->isPost()) {
// handle invalid request
}
$name = $request->postVar('name')
// process your form
}
}
I just started looking into dropzone.js Is it possible to somehow modify the previewTemplate area to add additional info about the files uploaded and then submit the form to an mvc method?
For simplicity I want to add two fields DocumentTypeID and ExpirationDate for each file that a user wants to upload
#model MyProject.Model.Document
#using (Html.BeginForm("Create", "Document", FormMethod.Post, new { enctype = "multipart/form-data", #class = "dropzone", #id = "my-awesome-dropzone" }))
{
<div class="row-fluid">
<fieldset class="span6">
<div class="editor-label">
#Html.LabelFor(model => model.DocumentTypeID, "DocumentType")
</div>
<div class="editor-field">
#Html.DropDownList("DocumentTypeID", String.Empty)
#Html.ValidationMessageFor(model => model.DocumentTypeID)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.ExpirationDate)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ExpirationDate)
#Html.ValidationMessageFor(model => model.ExpirationDate)
</div>
</fieldset>
<div class="span6"> <div class="dropzone-previews"></div> </div>
</div>
}
Here is the controller method which for now should accept one file at a time
[HttpPost]
public ActionResult Create(Document document, HttpPostedFileBase file)
{
if (ModelState.IsValid && file != null)
{
db.Documents.Add(document);
document.FilePath = ProcessDocumentUpload(Request.Files[0], document.DocumentID);
db.SaveChanges();
return "";//? // not sure what to return yet
}
}
Now the Js function for dropzone
<script type="text/javascript">
$(function () {
// "myAwesomeDropzone" is the camelized version of the HTML element's ID
Dropzone.options.myAwesomeDropzone = {
autoDiscover: false,
paramName: "file", // The name that will be used to transfer the file
maxFilesize: 5, // MB
maxFiles: 1, //for now upload one at a time
//I started looking at the template and added two elements as an experiment.
previewTemplate: "<div class=\"dz-preview dz-file-preview\">\n <div class=\"dz-details\">\n <div class=\"dz-filename\"><span data-dz-name></span></div>\n <div class=\"dz-size\" data-dz-size></div>\n <img data-dz-thumbnail />\n </div>\n <input type=\"text\" data-dz-doc-expiration-date class=\"dz-doc-input\" />\n <select class=\"dz-doc-input\" data-dz-doc-document-type-id ></select>\n <div class=\"dz-progress\"><span class=\"dz-upload\" data-dz-uploadprogress></span></div>\n <div class=\"dz-success-mark\"><span>✔</span></div>\n <div class=\"dz-error-mark\"><span>✘</span></div>\n <div class=\"dz-error-message\"><span data-dz-errormessage></span></div>\n</div>",
//dictDefaultMessage: "Drop files here to upload or click",
// The configuration that allows the whole form to be submitted on button click
autoProcessQueue: false,
uploadMultiple: false,
parallelUploads: 1,
addRemoveLinks: true,
previewsContainer: ".dropzone-previews", //show a preview in another place
// The setting up of the dropzone
init: function () {
var myDropzone = this;
// First change the button to actually tell Dropzone to process the queue.
$("input[type=submit]").on("click", function (e) {
// Make sure that the form isn't actually being sent.
e.preventDefault();
e.stopPropagation();
myDropzone.processQueue();
});
// Listen to the sendingmultiple event. In this case, it's the sendingmultiple event instead
// of the sending event because uploadMultiple is set to true.
this.on("sendingmultiple", function () {
// Gets triggered when the form is actually being sent.
// Hide the success button or the complete form.
});
this.on("successmultiple", function (files, response) {
// Gets triggered when the files have successfully been sent.
// Redirect user or notify of success.
});
this.on("errormultiple", function (files, response) {
// Gets triggered when there was an error sending the files.
// Maybe show form again, and notify user of error
});
},
accept: function (file, done) {
//maybe do something here for showing a dialog or adding the fields to the preview?
}
};
});
</script>
Thanks for looking!
have you tried to handle the event 'sending'?
$dropzone.on('sending', function (file, xhr, formData) {
formData.append('id', $id);
});
MVC controller
public JsonResult UploadImage(string id)
{
for (int i = 0; i < Request.Files.Count; i++)
{
HttpPostedFileBase file = Request.Files[i];
...
}
return Json(true, JsonRequestBehavior.DenyGet);
}
I've been using also MVC 4 and it has worked well.
I have the following partial view (I have removed some of the formatting just to keep it simple on this forum). My form tag is on the parent page, and the items on the dialog are a part of that form.
This is a strongly typed partial view. I have defined the password and confirmPassword fields as [Required] in my Model.
#Html.LabelFor(m => m.password)
Reset Password
<div id="dialogResetPassword" title="Reset Password">
<p>Reset password for user: #Model.userId</p>
#Html.LabelFor(m => m.password)
#Html.PasswordFor(m => m.password)
#Html.ValidationMessageFor(m => m.password)
<div class="rowEnd"></div>
#Html.LabelFor(m => m.confirmPassword)
#Html.PasswordFor(m => m.confirmPassword)
#Html.ValidationMessageFor(m => m.confirmPassword)
<div class="rowEnd"></div>
</div>
I have the following javascript for the initialization of the dialog:
function initializeResetPasswordDialog() {
$(resetPasswordDialogId).dialog(
{
autoOpen: false,
autoResize: true,
buttons: {
Ok: function () {
if (!($('#userForm').valid())) {
return false;
}
//more code goes here ...
closeResetPasswordDialog();
},
Cancel: function () {
closeResetPasswordDialog();
}
}
});
}
My dialog initializes, opens and closes fine, but when I try to check the validity of the items in the dialog, I always get "valid".
I do not want to submit the entire form, but just submit the password and the confirm password fields on the Ok button, and also fire up my validations. Any suggestions on how to do this?
Ok, so I solved it by adding the following function to the open parameter for the jQuery dialog:
open: function () {
$(this).parent().appendTo("#userForm");
}
I noticed that when initializing a jQuery dialog, even though it was within my form, when the HTML page rendered in the browser, the dialog div was moved outside the form. For the validations to work, the inputs must be within a form.
I hope this post helps other people too!