For an app I am working on, I've got the following Razor code for a View I am working on:
#Html.InputFor(m => m.Property1); // A date
#Html.InputFor(m => m.Property2); // Some other date
#Html.InputFor(m => m.SomeOtherProperty); // Something else.
<a href='#' id='some-button'>Button Text Here</a>
<!-- SNIP: Extra code that dosen't matter -->
<script>
var $someButton = $('#some-button');
$(document).ready(function () {
$someButton.click(function (e) {
e.preventDefault();
window.open('#Url.Action("Foo", "Home", new {p1 = Model.Property1, p2 = Model.Property2, pX = Model.SomeOtherProperty})', '_blank');
});
});
</script>
...upon a comment, I checked the rendered HTML. The values come with values, as expected...
<input name="Property1" data-val="true" data-val-required="(Required)" type="text" value="1/1/2013">
<input name="Property2" data-val="true" data-val-required="(Required)" type="text" value="4/11/2013">
<input name="SomeOtherProperty" data-val="true" data-val-required="(Required)" type="text" value="42">
<a href='#' id='some-button'>Button Text Here</a>
<script>
var $someButton = $('#some-button');
$(document).ready(function () {
$someButton.click(function (e) {
e.preventDefault();
window.open('http://localhost:xxxx/Home/Foo?p1=1%2F1%2F2013&p2=4%2F11%2F2013&pX=42', '_blank');
});
});
</script>
...and on the server side...
public ActionResult Foo(string p1, string p2, string pX)
{
var workModel = new FooWorkModel
{
Property1 = p1,
Property2 = p2,
SomeOtherProperty = pX
};
// Do something with this model, dosen't really matter from here, though.
return new FileContentResult(results, "application/some-mime-type");
}
I've noticed that only the first parameter (p1) is getting a value from the front end; all my other parameters are being passed null values!
Question: Why is the ActionResult being passed null values, when some value is assigned for these other fields? Or, a complimentary question: why would only the first parameter be successfully passing its value, while everything else is failing?
The issue is being caused by the escaped URL being generated by Url.Action(). (Source: How do I pass correct Url.Action to a JQuery method without extra ampersand trouble?)
Simply add a #Html.Raw() call around the Url.Action(), and data will flow as intended.
window.open('#Html.Raw(Url.Action("Foo", "Home", new {p1 = Model.Property1, p2 = Model.Property2, pX = Model.SomeOtherProperty}))', '_blank');
Related
Hi friends,I am working on MVC 4 Razor and I am stuck in a situation
where Employee Personal Details form is to be filled in
steps(wizard)..for which i used jquery accordion control..for every
step i put an accordion..The html in each accordion section is
rendered from partial view through ajax call on every click of
respective accordion (i.e. <h3></h3> tag)..
On page load first/top accordion is active by default. My problem is
to restrict the user to click on next accordion until he/she fills the
presently active accordion correctly..
Here is my full code:
View:
#model XXX.ViewModels.PersonalDetailsViewModel
#{
ViewBag.Title = "PersonalDetails";
Layout = "~/Views/Shared/Template.cshtml";
}
#using (Html.BeginForm("Lifestyle", "Apply", FormMethod.Post, new { id = "personalDetailForm" }))
{
<div class="centerdiv margin_top20">
<div class="row">
#Html.ValidationSummary(true, "Please Correct the following errors:")
</div>
<div style="width: 1000px;">
<div id="Personalaccordion" class="acordion_div" style="padding: 10px; float: left;">
<h3 class="acordion_div_h3" onclick="javascript:PersonalModule.GetRenderingView('Apply/GetBasicDetailsView','personalDetailForm','BasicDetailsDiv');">
<p>
Basic Details<span id="BasicDetailsDivExp"></span>
</p>
</h3>
<div id="BasicDetailsDiv">
</div>
<h3 class="acordion_div_h3" onclick="javascript:PersonalModule.GetRenderingView('Apply/GetPersonalAddressView','personalDetailForm','PersonalAddressDiv');">
<p>
Address<span id="PersonalAddressDivExp"></span></p>
</h3>
<div id="PersonalAddressDiv">
</div>
</div>
<ul id="conlitue_ul" style="margin-top: 20px;">
<li style="margin-left: 140px;">
<input type="submit" class="compareBtn float_lt" value="Continue Buying >" id="continue" /></li>
</ul>
</div>
</div>
}
#Scripts.Render("~/bundles/PersonalDetails")
<script type="text/javascript">
PersonalModule.GetRenderingView('Apply/GetBasicDetailsView', '', 'BasicDetailsDiv');
</script>
My Controller:
public ActionResult PersonalDetails(int leadId)
{
var personalDetailsViewModel = LeadHelper.GetPersonalDetails(leadId);
return View(personalDetailsViewModel);
}
public ActionResult GetBasicDetailsView(PersonalDetailsViewModel personalDetailsViewModel)
{
if (personalDetailsViewModel.BasicDetails == null)
{
ModelInitializerHelper.InitilaizeBasicDetailsVModel(personalDetailsViewModel);
}
ModelInitializerHelper.InitializeBasicLookup(personalDetailsViewModel);
return PartialView("Personal/BasicDetails", personalDetailsViewModel);
}
public ActionResult GetPersonalAddressView(PersonalDetailsViewModel personalDetailsViewModel)
{
if (personalDetailsViewModel.PersonalAddressDetails == null)
{
ModelInitializerHelper.IntializePersonalAddressVModel(personalDetailsViewModel);
}
ModelInitializerHelper.InitializePersonalAddressLookup(personalDetailsViewModel);
return PartialView("Personal/PersonalAddress", personalDetailsViewModel);
}
My JS :
var PersonalModule = {
GetRenderingView: function (url, formId, containerID) {
var applicationurl = ApplicationRoot + '/' + url;
var objects = $('#BasicDetailsDivExp , #PersonalAddressDivExp' );
viewDivID = containerID;
GetAccordionView(applicationurl, formId, objects, containerID, 'accordion_plus', 'accordion_minus');
}
}
GetAccordionView: function (url, formId, objects, containerID, accordion_plus, accordion_minus) {
var formObjectData = null;
if (formId != undefined) {
formObjectData = $("#" + formId).serialize();
}
var renderView = function (data) {
$('#' + containerID).innerHtml = data;
}
ExpandAccordion(objects, containerID, accordion_plus, accordion_minus);
DoServerRequest(url, formObjectData, renderView);
}
ExpandAccordion: function (objects, spanIconID, accordion_plus, accordion_minus) {
var Objects = objects;
Objects.removeClass(accordion_minus);
Objects.addClass(accordion_plus);
$('#' + spanIconID + 'Exp').removeClass(accordion_plus).addClass(accordion_minus);
if (Browser.ie7) {
Objects.css("margin-top", "-22px");
}
}
DoServerRequest: function (url, data, funSuccess) {
$.ajax({
type: "POST",
url: url,
data: data,
async: false,
dataType: "json",
success: funSuccess,
error: function (errorResponse) {
if (errorResponse.readyState == 4 && errorResponse.status == 200) {
renderCurrentView(errorResponse.responseText)
}
else {
alert(errorResponse.responseText);
}
}
});
}
Please somebody help..I have heard lots of good thing about this forum
and this is my first Question...Thanks in advance..
I have removed my jquery validation attempt as it made the code
garbage thing Now I dont know what to write and where to write
If you are trying to validate data that has been added to form via Ajax after page load then you will need to use the rules method and add rules for these new elements. Jquery Validate has no way of knowing about them otherwise.
Example
Once you have loaded your new content via Ajax you need to find each element and add the necessary rules to them.
$('#yourDiv').find(".newElements").rules("add", {
required: true,
messages: {
required: "Bacon is required"
}
});
If you are using unobtrusive validate you may need to add your new elements to that also. See this SO question for more details.
Validating the Form
To check if the fields are valid, you will need to validate the form on click. This can be done using .validate(). You can then check if the form validated using .valid()
Example
$('#yourForm').validate();
if(!$('#yourForm').valid()) {
alert('Bacon is required');
}
I noticed that doing #Url.Action("myAction", new { param1 = 123, param2 = 456}) provides me with an invalid URL Home/myAction?param1=123¶m2=456.
I am attempting to do
$("#myAjaxDiv").load(url);
But only param1 is getting populated in the action method.
When I remove the & and make it just & then it works, but doing a string replace is super hacky.
url = url.replace("&", "&");
Am I missing something here?
EDIT: Per request I'm including some of my sample app. (you can create a new MVC app and just add these quickly and see for yourself)
Controller:
public ActionResult AjaxTest(int? year, int? month)
{
ViewBag.Message = string.Format("Year: {0}, Month: {1}", year.HasValue ? year.ToString() : "no year", month.HasValue ? month.ToString() : "no month");
return PartialView("AjaxTest");
}
AjaxTest View:
#ViewBag.Message
Index View:
<script>
$(function () {
var url="";
$("#noParams").click(function () {
url = "Home/AjaxTest";
$("#ajaxy").load(url)
$("#url").text(url);
});
$("#yearParam").click(function () {
url = "Home/AjaxTest?year=2012";
$("#ajaxy").load(url)
$("#url").text(url);
});
$("#yearAndMonthParam").click(function () {
url = "Home/AjaxTest?year=2012&month=10";
$("#ajaxy").load(url)
$("#url").text(url);
});
$("#generated").click(function () {
url = "#(Url.Action("AjaxTest", new { year=2012, month=10}))";
$("#ajaxy").load(url);
$("#url").text(url);
});
});
</script>
<a id="noParams" href="#">No Params</a> <br />
<a id="yearParam" href="#">Year Param</a> <br />
<a id="yearAndMonthParam" href="#">Year and Month Param</a> <br />
<a id="generated" href="#">Generated</a> <br />
<div id="ajaxy">
</div>
<div>
URL: <span id="url"></span>
</div>
By default every content (which is not IHtmlString) emitted using a # block is automatically HTML encoded by Razor (see this Razor intro article Html Encoding section)
The Url.Action returns just a plain string so thats why the & gets encoded.
Use the Html.Raw if you don't want the encodeing:
url = "#(Html.Raw(Url.Action("AjaxTest", new { year=2012, month=10})))";
You can build the url in this way also.
var url = "#Url.Action("AjaxTest","YourControllerName")?year=2012&month=10";
$("#ajaxy").load(url);
I have 2 Form.Request in 2 functions that are executed on 2 different buttons clicks
here is fiddle
http://jsfiddle.net/RtxXe/38/
seems like I did not set the events in right order in my functions since they are mixing up the responses. if you hit Clear cache and than Send you still get response from clear cache and vice versa. Unless you reload the page and click again you cant get the right response for each button as it should be .
Since this is not my original form and *I can only change it with js * , i added the clear cache button with new Element. I cant figure out as to why is this happening and any help is appreciated.
this is original html:
<div id="toolbar">
<ul>
<li id="adminsubmit">Send</li>
</ul>
</div>
<div id="response"></div>
<form action="http://www.scoobydoo.com/cgi-bin/scoobysnack" method="post" name="editform" id="myform">
<fieldset>
<!-- form elements go here -->
</fieldset>
<input type="hidden" name="task" value="">
</form>
and here is js:
var AdminForm = {
start: function() {
var toolbar = $$('#toolbar ul');
var addbtn2 = new Element('li', {
'id': 'cache',
'class': 'button',
html: 'Clear Cache'
});
addbtn2.inject(toolbar[0], 'top');
var btn1 = $('adminsubmit').getElement('a');
var btn2 = $('cache').getElement('a');
btn1.addEvent('click', function(event) {
event.preventDefault ? event.preventDefault() : event.returnValue = false;
AdminForm.formChange();
});
btn2.addEvent('click', function(event) {
event.preventDefault ? event.preventDefault() : event.returnValue = false;
AdminForm.clearCache();
});
},
formChange: function() {
var adminform = $('myform');
var target = $('response');
var adminsend = new Form.Request(adminform, target, {
onSend: function() {
target.set('html', 'formChange sending');
},
onComplete: function() {
target.set('html', 'formChange sent');
}
});
adminsend.send();
},
clearCache: function() {
var adminform = $('myform');
var target = $('response');
var clearingcahe = new Form.Request(adminform, target, {
onSend: function() {
target.set('html', 'clearCache sending');
},
onComplete: function() {
target.set('html', 'clearCache sent');
}
});
clearingcahe.send();
}
}
window.addEvent('domready', AdminForm.start);
The Form.Request in Mootools inherits Class.Occlude, see http://mootools.net/docs/more/Class/Class.Occlude
But the Class.Occlude will prevent that several Objects are created and applied to the same DOM Element. That is, it works like a singleton, so the first time you do new Form.Request(adminform, ...) it will return a new instance of Form.Request.
However, the second time you call new Form.Request(adminform, ...) the previous object will be returned instead.
Your fiddle actually demonstrates this very good, because the first one that is clicked of "Clear Cache" or "Send" will be the one that initiates the object. The second time it will discard your options and just return the old object.
So there are two ways to solve this:
Create the Form.Request but don't set the event handlers through the options but through
adminsend.removeEvents('complete'); adminsend.addEvent('complete', ....)
Don't forget to remove the old event handlers before applying the new! otherwise you will just apply more and more eventhandlers.
There are two "buttons" so make two forms, which would be much more semantically correct as well.
I have managed to create a simple wizard based on an answer given by Niemeyer. This works fine. I want to add validation. I have managed to add a required validion on the field Firstname. Leaving this empty displays an error. But what I could not succeed in is the following:
Validate the model in the current step, and have the go next enabled or disabled based whether there are errors. If it is too difficult to enable or disable the next button, that is ok. I can also live without the button disabled when there are errors. As long as the user is prevented to proceed to the next step when there are errors.
. My view looks like this:
//model is retrieved from server model
<script type="text/javascript">
var serverViewModel = #Html.Raw(Json.Encode(Model));
</script>
<h2>Test with wizard using Knockout.js</h2>
<div data-bind="template: { name: 'currentTmpl', data: currentStep }"></div>
<hr/>
<button data-bind="click: goPrevious, enable: canGoPrevious">Previous</button>
<button data-bind="click: goNext, enable: canGoNext">Next</button>
<script id="currentTmpl" type="text/html">
<h2 data-bind="text: name"></h2>
<div data-bind="template: { name: getTemplate, data: model }"></div>
</script>
<script id="nameTmpl" type="text/html">
<fieldset>
<legend>Naamgegevens</legend>
<p data-bind="css: { error: FirstName.hasError }">
#Html.LabelFor(model => model.FirstName)
#Html.TextBoxFor(model => model.FirstName, new { data_bind = "value: FirstName, valueUpdate: 'afterkeydown'"})
<span data-bind='visible: FirstName.hasError, text: FirstName.validationMessage'> </span>
</p>
#Html.LabelFor(model => model.LastName)
#Html.TextBoxFor(model => model.LastName, new { data_bind = "value: LastName" })
</fieldset>
</script>
<script id="addressTmpl" type="text/html">
<fieldset>
<legend>Adresgegevens</legend>
#Html.LabelFor(model => model.Address)
#Html.TextBoxFor(model => model.Address, new { data_bind = "value: Address" })
#Html.LabelFor(model => model.PostalCode)
#Html.TextBoxFor(model => model.PostalCode, new { data_bind = "value: PostalCode" })
#Html.LabelFor(model => model.City)
#Html.TextBoxFor(model => model.City, new { data_bind = "value: City" })
</fieldset>
</script>
<script id="confirmTmpl" type="text/html">
<fieldset>
<legend>Naamgegevens</legend>
#Html.LabelFor(model => model.FirstName)
<b><span data-bind="text:NameModel.FirstName"></span></b>
<br/>
#Html.LabelFor(model => model.LastName)
<b><span data-bind="text:NameModel.LastName"></span></b>
</fieldset>
<fieldset>
<legend>Adresgegevens</legend>
#Html.LabelFor(model => model.Address)
<b><span data-bind="text:AddressModel.Address"></span></b>
<br/>
#Html.LabelFor(model => model.PostalCode)
<b><span data-bind="text:AddressModel.PostalCode"></span></b>
<br/>
#Html.LabelFor(model => model.City)
<b><span data-bind="text:AddressModel.City"></span></b>
</fieldset>
<button data-bind="click: confirm">Confirm</button>
</script>
<script type='text/javascript'>
$(function() {
if (typeof(ViewModel) != "undefined") {
ko.applyBindings(new ViewModel(serverViewModel));
} else {
alert("Wizard not defined!");
}
});
</script>
The knockout.js implementation looks like this:
function Step(id, name, template, model) {
var self = this;
self.id = id;
self.name = ko.observable(name);
self.template = template;
self.model = ko.observable(model);
self.getTemplate = function() {
return self.template;
};
}
function ViewModel(model) {
var self = this;
self.nameModel = new NameModel(model);
self.addressModel = new AddressModel(model);
self.stepModels = ko.observableArray([
new Step(1, "Step1", "nameTmpl", self.nameModel),
new Step(2, "Step2", "addressTmpl", self.addressModel),
new Step(3, "Confirmation", "confirmTmpl", {NameModel: self.nameModel, AddressModel:self.addressModel})]);
self.currentStep = ko.observable(self.stepModels()[0]);
self.currentIndex = ko.dependentObservable(function() {
return self.stepModels.indexOf(self.currentStep());
});
self.getTemplate = function(data) {
return self.currentStep().template();
};
self.canGoNext = ko.dependentObservable(function () {
return self.currentIndex() < self.stepModels().length - 1;
});
self.goNext = function() {
if (self.canGoNext()) {
self.currentStep(self.stepModels()[self.currentIndex() + 1]);
}
};
self.canGoPrevious = ko.dependentObservable(function() {
return self.currentIndex() > 0;
});
self.goPrevious = function() {
if (self.canGoPrevious()) {
self.currentStep(self.stepModels()[self.currentIndex() - 1]);
}
};
}
NameModel = function (model) {
var self = this;
//Observables
self.FirstName = ko.observable(model.FirstName).extend({ required: "Please enter a first name" });;
self.LastName = ko.observable(model.LastName);
return self;
};
AddressModel = function(model) {
var self = this;
//Observables
self.Address = ko.observable(model.Address);
self.PostalCode = ko.observable(model.PostalCode);
self.City = ko.observable(model.City);
return self;
};
And I have added an extender for the required validation as used in the field Firstname:
ko.extenders.required = function(target, overrideMessage) {
//add some sub-observables to our observable
target.hasError = ko.observable();
target.validationMessage = ko.observable();
//define a function to do validation
function validate(newValue) {
target.hasError(newValue ? false : true);
target.validationMessage(newValue ? "" : overrideMessage || "This field is required");
}
//initial validation
validate(target());
//validate whenever the value changes
target.subscribe(validate);
//return the original observable
return target;
};
This was a tricky one, but I'll offer a couple of solutions for you...
If you simply want to prevent the Next button from proceeding with an invalid model state, then the easiest solution I found is to start by adding a class to each of the <span> tags that are used for displaying the validation messages:
<span class="validationMessage"
data-bind='visible: FirstName.hasError, text: FirstName.validationMessage'>
(odd formatting to prevent horizontal scrolling)
Next, in the goNext function, change the code to include a check for whether or not any of the validation messages are visible, like this:
self.goNext = function() {
if (
(self.currentIndex() < self.stepModels().length - 1)
&&
($('.validationMessage:visible').length <= 0)
)
{
self.currentStep(self.stepModels()[self.currentIndex() + 1]);
}
};
Now, you may be asking "why not put that functionality in the canGoNext dependent observable?", and the answer is that calling that function wasn't working like one might thing it would.
Because canGoNext is a dependentObservable, its value is computed any time the model that it's a member of changes.
However, if its model hasn't changed, canGoNext simply returns the last calculated value, i.e. the model hasn't changed, so why recalculate it?
This wasn't vital when only checking whether or not there were more steps remaining, but when I tried to include validation in that function, this came into play.
Why? Well, changing First Name, for example, updates the NameModel it belongs to, but in the ViewModel, self.nameModel is not set as an observable, so despite the change in the NameModel, self.nameModel is still the same. Thus, the ViewModel hasn't changed, so there's no reason to recompute canGoNext. The end result is that canGoNext always sees the form as valid because it's always checking self.nameModel, which never changes.
Confusing, I know, so let me throw a bit more code at you...
Here's the beginning of the ViewModel, I ended up with:
function ViewModel(model) {
var self = this;
self.nameModel = ko.observable(new NameModel(model));
self.addressModel = ko.observable(new AddressModel(model));
...
As I mentioned, the models need to be observable to know what's happening to them.
Now the changes to the goNext and goPrevious methods will work without making those models observable, but to get the true real-time validation you're looking for, where the buttons are disabled when the form is invalid, making the models observable is necessary.
And while I ended up keeping the canGoNext and canGoPrevious functions, I didn't use them for validation. I'll explain that in a bit.
First, though, here's the function I added to ViewModel for validation:
self.modelIsValid = ko.computed(function() {
var isOK = true;
var theCurrentIndex = self.currentIndex();
switch(theCurrentIndex)
{
case 0:
isOK = (!self.nameModel().FirstName.hasError()
&& !self.nameModel().LastName.hasError());
break;
case 1:
isOK = (!self.addressModel().Address.hasError()
&& !self.addressModel().PostalCode.hasError()
&& !self.addressModel().City.hasError());
break;
default:
break;
};
return isOK;
});
[Yeah, I know... this function couples the ViewModel to the NameModel and AddressModel classes even more than simply referencing an instance of each of those classes, but for now, so be it.]
And here's how I bound this function in the HTML:
<button data-bind="click: goPrevious,
visible: canGoPrevious,
enable: modelIsValid">Previous</button>
<button data-bind="click: goNext,
visible: canGoNext,
enable: modelIsValid">Next</button>
Notice that I changed canGoNext and canGoPrevious so each is bound to its button's visible attribute, and I bound the modelIsValid function to the enable attribute.
The canGoNext and canGoPrevious functions are just as you provided them -- no changes there.
One result of these binding changes is that the Previous button is not visible on the Name step, and the Next button is not visible on the Confirm step.
In addition, when validation is in place on all of the data properties and their associated form fields, deleting a value from any field instantly disables the Next and/or Previous buttons.
Whew, that's a lot to explain!
I may have left something out, but here's the link to the fiddle I used to get this working: http://jsfiddle.net/jimmym715/MK39r/
I'm sure that there's more work to do and more hurdles to cross before you're done with this, but hopefully this answer and explanation helps.
It is easy to submit form to an action method in the controller which has strongly typed textboxes for example, with a submit button, but what if I want to send the exact same form with the strongly typed textboxes through jquery perhaps the $.ajax call after something else has been clicked.
code like this:
#Html.TextBoxFor(m => m.topTenFav.YoutubeLink,new { id="youTubeLinkTxt"})
does all the work for us and it's very simple to map the properties of our object in the controller
[HttpPost]
public ActionResult AddTopTenFav(HomeViewModel topTen)
{
topTen.topTenFav.Date = DateTime.Now;
topTen.topTenFav.UserName = User.Identity.Name;
repository.AddTopTen(topTen);
repository.Save();
return RedirectToAction("Index");
}
How would I send this form to the controller, map the textboxes in the form to object's properties on a click event such as
$("#btnAddGenre").click(function () {}
#using (Html.BeginForm(
"AddTopTenFav", "Home", FormMethod.Post, new { id = "AddTopTenFavForm" }))
{
<span id="youTubeLinkSpan">Youtube Link</span>
<div>
#Html.TextBoxFor(m => m.topTenFav.YoutubeLink,new { id="youTubeLinkTxt"})
</div>
<span id="youTubeNameSpan">Song Title</span>
<div>
#Html.TextBoxFor(m => m.topTenFav.Title,new { id="youTubeNameTxt"})
</div>
<button type="submit" name="btnSubmit" value="">submit</button>
}
You can do the following post:
$(document).ready(function(){
$('#btnAddGenre').click(function () {
$.post(
$('#AddTopTenFavForm').attr('action'),
$('#AddTopTenFavForm').serialize,
function (data) {
window.location = #Url.Action("Index");
},
'html' // returned data type
);
});
});
I use the html data type so you can return whatever you want and the redirect occurs on the window.location using the #Url.Action to give the location.
Please if it work mark as accepted answer
yes you can post the data of strongly typed textboxex using jquery.
First you have to do
take the values of all the textboxex in jquery using the below code.
var xx= $("#xx").val();
this will give the val in xx from your mvc text box.
Then by using jquery ajax call you can call the action method.
the code is below.
$.get("/XXXX/YY/1", { xxName: xx }, function (data) {
var status = data;
alert(status);
if (status) {
return true;
}
else {
alert("The book with this name is already present. TRY DIFFERENT NAME!")
return false;
}
});
here xxxx is controller amd yy is action method name.the next parameter is the value of all the textboxes which you want to send as an parameter.
This will perform the ajax call and return the value.
Please tell me if you find any problem the i will give the whole code.