I am working my way through a MVC 5 application, using Bootstrap modals. I have come across a bit of an inconsistency when trying to access a particular Partial page with modals from different web pages. One of the modals works fine, the other does not work at all.
(I have referenced Modals in MVC 5 when learning how to do this - so it's possible i'm making a boneheaded mistake somewhere, but I do not think so).
I'll start with the relevant code, then at the bottom I will go over what I have tried.
modalform.js (this was pretty much copy/pasted from the above reference object)
$(function () {
$.ajaxSetup({ cache: false });
$("a[data-modal]").on("click", function (e) {
// hide dropdown if any
$(e.target).closest('.btn-group').children('.dropdown-toggle').dropdown('toggle');
$('#myModalContent').load(this.href, function () {
$('#myModal').modal({
backdrop: 'static',
keyboard: true
}, 'show');
bindForm(this);
});
return false;
});
});
Modal Partial Page (Create)
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title" id="myModalLabel">Add New Site</h4>
</div>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="modal-body">
<div class="form-horizontal">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.OrganisationSelected, htmlAttributes: new { #class = "control-label col-lg-3" })
<div class="col-md-10">
#Html.DropDownListFor(model => Model.OrganisationSelected, Model.Organisations, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.OrganisationSelected, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.SiteName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.SiteName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.SiteName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.SiteAbbr, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.SiteAbbr, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.SiteAbbr, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.SiteNotes, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.SiteNotes, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.SiteNotes, "", new { #class = "text-danger" })
</div>
</div>
<div class="modal-footer">
<button class="btn" data-dismiss="modal">Cancel</button>
<input class="btn btn-primary" type="submit" value="Save" />
</div>
</div>
</div>
}
AllSites.cshtml (This one works fine - and yes, I know about the for loop not being best practice.)
#model IEnumerable<DRPlanner.ViewModels.OrganisationSiteViewModel>
#using DRPlanner.Helpers
#{
ViewBag.Title = "All Sites";
}
<!-- modal placeholder-->
<div id='myModal' class='modal fade in'>
<div class="modal-dialog">
<div class="modal-content">
<div id='myModalContent'></div>
</div>
</div>
</div>
#for (int i = 0; i < Model.Count(); i++)
{
if ((i == 0) || (Model.ElementAt(i).OrgId != Model.ElementAt(i - 1).OrgId))
{
<strong>
#Html.RouteLink(Model.ElementAt(i).OrganisationName.ToString() + " (" + Model.ElementAt(i).OrganisationAbbr.ToString() + ")", "OrgShort", new { org = Model.ElementAt(i).OrgId })
</strong>
#:<ul class="list-unstyled">
}
if (Model.ElementAt(i).SiteName != "")
{
<li>
#Html.RouteLink(Model.ElementAt(i).SiteName.ToString(), "SiteShort", new { org = Model.ElementAt(i).OrgId, site = Model.ElementAt(i).SiteId })
</li>
}
if ((i == Model.Count() - 1) || (!Model.ElementAt(i).OrganisationAbbr.Equals(Model.ElementAt(i + 1).OrganisationAbbr)))
{
#:</ul>
}
}
<br />
#Html.RouteLink("New Organisation", "NewOrg", new { controller = "Organisation", action = "Create" })
#Html.RouteLink("New Site", "NewSite", new { controller = "Site", action = "Create" }, new { data_modal = "", id = "btnCreate", #class = "btn btn-small btn-primary" })
Sites.cshtml (This one doesn't work)
#model IEnumerable<DRPlanner.ViewModels.OrganisationSiteViewModel>
#{ViewBag.Title = "Sites";}
Sites For #ViewBag.OrganisationName
<ul class="list-unstyled">
#foreach (var item in Model)
{
<li>#Html.RouteLink(item.SiteName.ToString(), "SiteShort", new { site = item.SiteId })</li>
}
</ul>
#Html.ActionLink("Add Site", "Create", "Site", null, new { data_modal = "", id = "btnCreate", #class = "btn btn-small btn-primary" })
<script src="~/Scripts/modalform.js"></script>
<!-- modal placeholder-->
<div id='myModal' class='modal fade in'>
<div class="modal-dialog">
<div class="modal-content">
<div id='myModalContent'></div>
</div>
</div>
</div>
What I have done:
I have debugged the running of the modalform.js in the VS2013 debugger, and found that when it is run from the AllSites.cshtml, the $('#myModal').modal is referring to a function (I have no clue how or where this function is pulled from, sorry), but when it is run on the Site.cshtml, $('#myModal').modal is undefined. So I know that is where the error is: i'm just clueless as to how to resolve the issue (or perhaps just clueless in general). One other piece of information: Sites.cshtml and AllSites.cshtml are both actions of DIFFERENT controllers to where the Create action exists. Basically, I am trying to get the modal to be accessible from multiple locations within the site. Any help is greatly appreciated (I don't have 15 rep yet, so upvotes will need to be pro-rata'ed for later).
The Error: 0x800a01b6 - JavaScript runtime error: Object doesn't support property or method 'modal'
Ok, I found the problem.
The _Create.cshtml class had some unnecessary javascript and css files being referenced/loaded up. It seems that one (or more) of these was causing a conflict, which (naturally) disappeared once I commented/removed these unnecessary references.
Thanks all for your time and efforts. Issue is now resolved!
Related
I'm writing an MVC project that includes the possibility to add points in a Google map. So far I can add points to db by hardcoding latitude and longitude values, but I want to let the user select the position on the map and send them back to the controller from the Razor view.
I have already written part of the view and I'm able to open a modal popup with the map and a hardcoded location.
What I want to do is:
1. let the user insert an address in a textbox
2. open the modal popup and have the map centered with a marker near the address
3. save back the position in two fields of the view in order to send them back to the controller.
I have been using the code provided by Google Developers at:
https://developers.google.com/maps/documentation/javascript/examples/place-details
and have come to the following code:
All the code is in the Razor View:
<form>
<fieldset>
<legend>Ubicazione: </legend>
<div class="form-group">
#Html.LabelFor(model => model.Indirizzo, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
<button type="button" class="btn btn-default" style="float:left"
data-toggle="modal" data-target="#findInModal" data-backdrop="static"
data-keyboard="true" data-address='pippo'>
Trova posizione
</button>
#Html.EditorFor(model => model.Indirizzo, new { htmlAttributes = new { #class = "form-control" }, #id="addr" })
#Html.ValidationMessageFor(model => model.Indirizzo, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Latitudine, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#*#Html.EditorFor(model => model.Latitudine, new { htmlAttributes = new { #class = "form-control" } })*#
#Html.DisplayFor(model => model.Latitudine, new { htmlAttributes = new { #class = "form-control" } })
#Html.HiddenFor(model => model.Latitudine)
#Html.ValidationMessageFor(model => model.Latitudine, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Longitudine, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#*#Html.EditorFor(model => model.Longitudine, new { htmlAttributes = new { #class = "form-control" } })*#
#Html.DisplayFor(model => model.Longitudine, new { htmlAttributes = new { #class = "form-control" } })
#Html.HiddenFor(model => model.Longitudine)
#Html.ValidationMessageFor(model => model.Longitudine, "", new { #class = "text-danger" })
</div>
</div>
</fieldset>
</form>
I would like the value of the data-address='pippo' to be the address in the "#Html.EditorFor(model => model.Indirizzo …."
Here is the modal popup content that opens the map and centers it to a hardcoded position:
<!-- Find Location In Modal -->
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="modal fade" id="findInModal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" style="height: 0px;"></h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Chiudi">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<div id="gMap" style="height:400px;width:100%;"></div>
<script src="https://maps.googleapis.com/maps/api/js?key=APIKEY&language=it®ion=IT&libraries=places&callback=gMap" async defer></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script>
let myCenter;
let mapProp;
let map;
let service;
function gMap() {
myCenter = new google.maps.LatLng(41.893140, 12.483330);
mapProp = { center: myCenter, zoom: 15, scrollwheel: true, draggable: true, mapTypeId: google.maps.MapTypeId.HYBRID };
map = new google.maps.Map(document.getElementById("gMap"), mapProp);
let request = {
query: 'Musei Capitolini',
fields: ['name', 'geometry'],
};
service = new google.maps.places.PlacesService(map);
service.findPlaceFromQuery(request, function (results, status) {
if (status === google.maps.places.PlacesServiceStatus.OK) {
for (let i = 0; i < results.length; i++) {
createMarker(results[i]);
}
map.setCenter(results[0].geometry.location);
}
});
}
function createMarker(place) {
let marker = new google.maps.Marker({
map: map,
position: place.geometry.location,
icon: "/Content/icons/01.png"
});
google.maps.event.addListener(marker, 'click', function () {
infowindow.setContent(place.name + place.geometry.location);
infowindow.open(map, this);
});
}
</script>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Chiudi</button>
<button type="button" class="btn btn-primary">Salva</button>
</div>
</div>
</div>
</div>
}
and here is the script that open the popup:
<script>
$('#findInModal').on('show.bs.modal', function (event) {
let button = $(event.relatedTarget) // Button that triggered the modal
let AddressToFind = button.data('address') // Extract info from data-* attributes
// If necessary, you could initiate an AJAX request here (and then do the updating in a callback).
// Update the modal's content. We'll use jQuery here, but you could use a data binding library or other methods instead.
let modal = $(this)
modal.find('.modal-title').text('Find near: ' + AddressToFind)
})
</script>
At the moment I have not implemented how to return the place.geometry.location values to the parent view, but they are suppose to update the model => model.Latitudine and model => model.Longitudine values that will be returned to the controller to update the db.
Thank in advance for any help.
No help from devs, but going deep into the Google Developers documentation and testing it at JSFiddle, I finally came out with the following solution:
#model GoogleMapTest.Models.MapPoint
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>MapPoint</legend>
<div class="form-group">
<div class="editor-label">
#Html.LabelFor(model => model.Address)
</div>
<div class="editor-field">
#Html.DisplayFor(model => model.Address)
#Html.HiddenFor(model => model.Address, new { id = "Address" })
#Html.ValidationMessageFor(model => model.Address)
</div>
<button type="button" class="btn btn-default" style="float: left"
data-toggle="modal" data-target="#findInModal" data-backdrop="static"
data-keyboard="true">
Find Position
</button>
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Latitude)
</div>
<div class="editor-field">
#Html.DisplayFor(model => model.Latitude)
#Html.HiddenFor(model => model.Latitude, new { id = "Latitude" })
#Html.ValidationMessageFor(model => model.Latitude)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Longitude)
</div>
<div class="editor-field">
#Html.DisplayFor(model => model.Longitude)
#Html.HiddenFor(model => model.Longitude, new { id = "Longitude" })
#Html.ValidationMessageFor(model => model.Longitude)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
<!-- Find Location In Modal -->
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="modal fade" id="findInModal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" style="height: 0px;">Set Point in Map</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<input id="pac-input" class="controls" type="text" placeholder="Find...">
<div id="gMap" style="height:400px;width:100%;"></div>
<script src="https://maps.google.com/maps/api/js?sensor=false&key=AIzaSyDMD8_QumYpomzDTcHW-myppqsyCZRsnB8"
// APIkey end with call to libraries "&libraries=places&callback=initAutocomplete"
async defer></script>
<script>
function initAutocomplete() {
var map = new google.maps.Map(document.getElementById('gMap'), {
center: { lat: 41.893321, lng: 12.482929 },
zoom: 13,
mapTypeId: 'hybrid',
scrollwheel: true,
zoomControl: false,
scaleControl: true,
streetViewControl: false,
fullscreenControl: false,
mapTypeControl: false
});
// Create the search box and link it to the UI element.
var input = document.getElementById('pac-input');
var searchBox = new google.maps.places.SearchBox(input);
map.controls[google.maps.ControlPosition.TOP_LEFT].push(input);
// Bias the SearchBox results towards current map's viewport.
map.addListener('bounds_changed', function () {
searchBox.setBounds(map.getBounds());
});
var markers = [];
// Listen for the event fired when the user selects a prediction and retrieve
// more details for that place.
searchBox.addListener('places_changed', function () {
var places = searchBox.getPlaces();
if (places.length == 0) {
return;
}
// Clear out the old markers.
markers.forEach(function (marker) {
marker.setMap(null);
});
markers = [];
// For each place, get the icon, name and location.
var bounds = new google.maps.LatLngBounds();
places.forEach(function (place) {
if (!place.geometry) {
console.log("Returned place contains no geometry");
return;
}
// create infowindow: just while debugging not needed in View
var infowindow = new google.maps.InfoWindow();
// create geocoder for address
var geocoder = new google.maps.Geocoder();
// Create a marker for each place.
markers.push(new google.maps.Marker({
map: map,
//icon: "/Content/icons/01.png",
title: place.name,
position: place.geometry.location,
draggable: true
}));
// Add listeners to marker:
// click: opens infowindow
markers[0].addListener('click', function () {
infowindow.open(map, markers[0]);
infowindow.setContent(place.name + ': ' + place.geometry.location);
infowindow.open(map, this);
// Add listeners to map
google.maps.event.addListener(markers[0], 'dragend', function (evt) {
infowindow.setContent('Lat: ' + evt.latLng.lat() + ' Lng: ' + evt.latLng.lng());
infowindow.open(map, this);
map.setCenter(markers[0].getPosition());
markers[0].setMap(map);
map.setTilt(0);
map.setZoom(20);
// update return values
// gLat, gLong, gAddr are global variables
gLat = evt.latLng.lat();
gLong = evt.latLng.lng();
// get address
var latlng = { lat: parseFloat(gLat), lng: parseFloat(gLong) };
geocoder.geocode({ 'location': latlng }, function (results, status) {
if (status === 'OK') {
if (results[0]) { gAddr = results[0].formatted_address } else {
gAddr = 'NotFound';
}
}
});
});
});
if (place.geometry.viewport) {
// Only geocodes have viewport.
bounds.union(place.geometry.viewport);
} else {
bounds.extend(place.geometry.location);
}
});
map.fitBounds(bounds);
});
}
</script>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Chiudi</button>
<button type="button" class="btn btn-primary" onclick="updFields(gLat, gLong, gAddr)">Salva</button>
</div>
</div>
</div>
</div>
}
<script>
$('#findInModal').on('show.bs.modal', function () {
})
function updFields(lat,lng, addr) {
//alert('Lat: ' + lat + '\nLng: ' + lng + '\nAddr: ' + addr);
//document.getElementById("Address").value = addr;
//document.getElementById("Latitude").value = lat;
//document.getElementById("Longitude").value = lng;
$('#Address').val(addr);
$('#Latitude').val(lat);
$('#Longitude').val(lng);
}
</script>
infowindow and alerts are just in while developing, but are not part of the final solution.
Basically I implemented a Google Maps Search Box with the Autocomplete and Place service, and a Geocoder to retrieve the address from the coordinators.To this I added a function that updates my hidden fields.The code of the view is much more complex because it contains other fields not relevant to the position in the map and makes it possible to manually modify the fields, but basically this is the backbone of the code.
I would have like to post images, it seem that my APIkey has overgone quota.
I have placed a partial view into a modal to update a password like so:
<div class="modal fade" id="modalPassword" tabindex="-1" role="dialog"
aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-body">
<div class="modal-content">
<div id="message"></div>
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title" id="myModalLabel">Change Password</h4>
</div>
<div class="modal-
<div id="passwordForm">
#{
#Html.Action("ChangePassword","Account");
}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
This is my partial view:
#model WebApplication1.Models.ViewModel.ChangeUserPassword
#{
Layout = null;
}
<script src="~/Scripts/jquery-1.10.2.min.js"></script>
<script src="~/Scripts/jquery.validate.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
<form id="form">
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset id="submitPasswordForm">
<div class="col_full">
#Html.LabelFor(model => model.OldPassword, htmlAttributes: new { #class = "capitalize t600" })
#Html.TextBoxFor(model => model.OldPassword, null, new { #class = "sm-form-control", id = "txtOldPassword" })
#Html.ValidationMessageFor(model => model.OldPassword)
</div>
<div class="col_full">
#Html.LabelFor(model => model.ChangedPassword, htmlAttributes: new { #class = "capitalize t600" })
#Html.TextBoxFor(model => model.ChangedPassword, null, new { #class = "sm-form-control", id = "txtChangedPassword" })
#Html.ValidationMessageFor(model => model.ChangedPassword)
</div>
<div class="col_full">
#Html.LabelFor(model => model.ConfirmPassword, htmlAttributes: new { #class = "capitalize t600" })
#Html.TextBoxFor(model => model.ConfirmPassword, null, new { #class = "sm-form-control", id = "txtConfirmPassword" })
#Html.ValidationMessageFor(model => model.ConfirmPassword)
</div>
<div class="modal-footer">
<button type="button" class="btn btn-warning" data-dismiss="modal">Cancel</button>
<input type="submit" value="Save Changes" class="btn btn-primary" id="btn_save_password" />
</div>
</fieldset>
</form>
When I click the "btn_save_password", I invoke the onclick event like so:
$("#btn_save_password").click(function (event) {
event.preventDefault();
var data = $("#submitPasswordForm").serialize();
$.ajax({
type: "POST",
url: "#Url.Action("ChangePassword", "Account")",
data: data,
success: function (result) {
$("#passwordForm").empty();
//$("div").remove("#passwordForm");
addHtml(result);
},
error: function () {
$("#passwordForm").html("Error occured");
}
});
});
function addHtml(htmlString) {
$("#msg").html(htmlString);
}
Then it invokes a method in my controller "ChangePassword"
[Authorize]
public ActionResult ChangePassword()
{
return PartialView();
}
[HttpPost]
public ActionResult ChangePassword(ChangeUserPassword password)
{
if (ModelState.IsValid)
{
var cookie = HttpContext.Request.Cookies["Sys_user_id"];
var um = new UserManager();
if (cookie != null && um.GetAccountPassword(Convert.ToInt32(cookie.Value), password.OldPassword))
{
um.ChangeUserPassword(password, Convert.ToInt32(cookie.Value));
}
else
{
ModelState.AddModelError("","Wrong current password");
}
}
else
{
ModelState.AddModelError("","Error");
}
return View();
}
The "ChangePassword" method invokes the PartialView "ChangePassword.html" like so:
[Authorize]
public ActionResult ChangePassword {
return PartialView();
}
I can view the partial view on the modal and I am able to successfully update the database. But the problem is, I want to be able to send a successful message or error message into the modal when it is successful or not. Upon submission, whether it has updated the database or not, it refreshes the page and the modal is gone. I want to be able to get the message into the modal.
Your help is greatly appreciated.
EDIT --
I can now see the validation message in the Modal but it only works once. As soon as I click the "btn_save_password" again, the page refreshes.
Add two <div> sections containing your messages (Successs and Failed) with the hide class in the partial view. After Ajax Submission, Add class Show for the suitable div.
eg:
<div class="alert alert-danger text-center hide" role="alert"id="FailedMesssage">
Failed....!!!
</div>
<div class="alert alert-success text-center hide" role="alert" id="successMesssage">
success....!!!
</div>
<button type = "button" value="Submit" onclick ="Test()"/>
Set Var Value as 0 or 1 using ajax code then try following script
<script>
function Test()
{
var value =1;
if(value == 1)
{
$("#successMesssage").addClass("show").removeClass("hide");
}
else
{
$("#FailedMesssage").addClass("show").removeClass("hide");
}
}
</script>
I have coded a edit modal, is does works, but when I dismiss the modal and try to open it a second time, it just goes dark.
My link calling the modal
#Html.ActionLink("Editar", "GetEditSv", "Sv", new { id = sv.IDServico },new{data_target = "#modal-container", data_toggle = "modal"})
my container inside the parent view
<div id="modal-container" class="modal fade hidden-print" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
</div>
</div>
a script to erase the modal data and the parent view (take on the internet)
<script>
$(function () {
//when the modal is closed
$('#modal-container').on('hidden.bs.modal', function () {
//remove the bs.modal data attribute from it
$(this).removeData('bs.modal');
//and empty the modal-content element
$('#modal-container .modal-content').empty();
});
});
$('#modal-container').on('show.bs.modal', function (event) {
var button = $(event.relatedTarget);
var url = button.attr("href");
var modal = $(this);
//note that this will replace the content of modal-contant ever time the modal is opened
modal.find('.modal-content').load(url);
});
</script>
A part of the modal view (a separated file)
#model ControleIntegrado.Models.Servico
#using (Html.BeginForm("EditSv", "Sv", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Servico</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.IDServico)
<div class="form-group">
#Html.LabelFor(model => model.Data, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Data.Date, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Data, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Salvar" class="btn btn-default" />
<input type="button" class="btn btn-default" data-dismiss="modal" value="cancelar"/>
</div>
</div>
</div>
}
<script src="~/Scripts/bootstrap.js"></script>
<script src="~/Scripts/jquery-ui-1.12.1.js"></script>
<script src="~/Scripts/jquery.validate.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
and the action on the controller
public ActionResult GetEditSv(int id)
{
using(DBControle db = new DBControle())
{
foreach (var item in db.Servico)
{
if(item.IDServico == id)
{
return PartialView("GetEditSv", item);
}
}
}
return ViewBag();
}
https://i.stack.imgur.com/2y7NP.png
Try this, use $('body').find('.modal-backdrop').removeClass('modal-backdrop'); to remove the backdrop effect (dark background)
<script>
$(function () {
//when the modal is closed
$('#modal-container').on('hidden.bs.modal', function () {
//remove the bs.modal data attribute from it
$(this).removeData('bs.modal');
//and empty the modal-content element
$('#modal-container .modal-content').empty();
//remove backdrop
$('#modal-container').find('.modal-backdrop').removeClass('modal-backdrop');
});
});
$('#modal-container').on('show.bs.modal', function (event) {
var button = $(event.relatedTarget);
var url = button.attr("href");
var modal = $(this);
//note that this will replace the content of modal-contant ever time the modal is opened
modal.find('.modal-content').load(url);
});
</script>
I have a page to add outcomes to a areas on a form. I utilise Editor templates to group the outcomes into areas and allow the outcome to be selected from a drop down list. I'd like to show an optional comments box for two of the 3 possible outcomes.
The main page is the following:
#model DBS.ViewModels.OutcomeQuestionnaireVM
#{
ViewBag.Title = "Outcomes";
}
<h2>Add Outcomes</h2>
#if (Model.Error == true)
{
<h3 class="danger">You MUST select an outcome for at least 1 area.</h3>
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.HiddenFor(x => x.DebriefId)
<hr />
#Html.EditorFor(m => m.Groups, new { outcomes = Model.Outcomes })
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Next" class="btn btn-default" />
</div>
</div>
</div>
}
The group editor template is:
#model DBS.ViewModels.OutcomeQuestionGroupVM
#{
Layout = null;
}
<h4>#Html.DisplayFor(m => m.Name)</h4>
#Html.EditorFor(m => m.Questions, new { outcomes = ViewData["outcomes"] })
The final editor template for the outcome is:
#model DBS.ViewModels.OutcomeQuestionVM
#{
Layout = null;
}
<div class="form-group">
<div class="row">
<div class="col-md-4">
#Html.DisplayFor(m => m.Question)
</div>
<div class="col-md-4">
#Html.HiddenFor(m => m.ID)
#Html.DropDownListFor(m => m.OutcomeId, (SelectList)ViewData["outcomes"], "Please Select if applicable", new { #class = "form-control", #id = "OutcomeId" })
#Html.ValidationMessageFor(m => m.OutcomeId, "", new { #class = "text-danger" })
</div>
<div class="col-md-4" id="Comments">
#Html.HiddenFor(m => m.Comments)
#Html.TextAreaFor(model => Model.Comments, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Comments, "", new { #class = "text- danger" })
</div>
</div>
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
<script type="text/javascript">
$(document).ready(function ()
{
document.getElementById('Comments').hide;
});
$(function () {
$(".DropChange").change(function () {
var valone = $('#OutcomeId').val();
if (valone == 1 || valone == 2)
{
$('#Comments').show();
}
else if (valone == 3)
{
$('#Comments').hide();
}
else
{
$('#Comments').hide();
}
});
});
</script>
}
However javascript doesn't do anything.
Sections are not supported in partial views, which is fortunate in your case or you would be adding multiple inline copies of each script in the jqueryval bundle as well as your own script (one each time the template is added).
Your also generating invalid html because of the duplicate id attributes generated by new { id = "OutcomeId" } and <div class="col-md-4" id="Comments"> which means the script would never have worked anyway.
You are also generating a hidden input for Comments before the textarea for the same property, which means that when the form is submitted, the value of Comments will be the initial value (as generated by #Html.HiddenFor(m => m.Comments)) and the value of the <textarea> would be ignored.
Its the responsibility of the view to include the scripts, not the partial, so move the scripts to the main view (or layout) and use class names and relative selectors.
Your html in the template should be
<div class="row">
<div class="col-md-4">
#Html.DisplayFor(m => m.Question)
</div>
<div class="col-md-4">
#Html.HiddenFor(m => m.ID)
// add class name to handle the .change() event
#Html.DropDownListFor(m => m.OutcomeId, (SelectList)ViewData["outcomes"], "Please Select if applicable", new { #class = "form-control outcome" })
#Html.ValidationMessageFor(m => m.OutcomeId, "", new { #class = "text-danger" })
</div>
<div class="col-md-4" class="comments"> // use class name
#Html.TextAreaFor(model => Model.Comments, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Comments, "", new { #class = "text- danger" })
</div>
</div>
then include css to initially hide all comments
.comments {
display: none;
}
and the script in the main view
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
<script type="text/javascript">
$(".outcome").change(function () {
var valone = $(this).val();
// get the associated comment
var comment = $(this).closest('.row').find('.comments');
if (valone == 1 || valone == 2) {
comment.show();
} else {
comment.hide();
}
});
</script>
}
Note it was not clear what $(".DropChange") was referring to but I assume its the dropdownlist in your template (which I gave class="outcome")
I am working on an asp.net mvc web application. on my main view i got the following create link:-
<a class="btn btn-success" data-modal="" href="/Staff/Create" id="btnCreate">
<span class="glyphicon glyphicon-plus"></span>
</a>
<!-- modal placeholder-->
<div id='myModal' class='modal fade in'>
<div class="modal-dialog">
<div class="modal-content">
<div id='myModalContent'></div>
</div>
</div>
</div>
and i have the following script:-
$(function () {
$.ajaxSetup({ cache: false });
$("a[data-modal]").on("click", function (e) {
$('#myModalContent').load(this.href, function () {
$('#myModal').modal({
keyboard: true
}, 'show');
$('#myModalContent').removeData("validator");
$('#myModalContent').removeData("unobtrusiveValidation");
$.validator.unobtrusive.parse('#myModalContent');
bindForm(this);
});
return false;
});
});
function bindForm(dialog) {
$('#myModalContent', dialog).submit(function () {
if ($('#myModalContent').valid()) {
$('#progress').show();
$.ajax({
url: this.action,
type: this.method,
data: $(this).serialize(),
success: function (result) {
if (result.success) {
$('#myModal').modal('hide');
$('#progress').hide();
//location.reload();
alert('www');
} else {
$('#progress').hide();
$('#myModalContent').html(result);
bindForm();
}
}
});
}
else {
return false;
}
});
}
Now when i click on the Create link the Create action method that will return the following partial view, which will be rendered inside a modal popup :-
#model SkillManagement.Models.Staff
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Staff</h4>
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.GUID, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.GUID)
#Html.ValidationMessageFor(model => model.GUID)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.UserName, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.UserName)
#Html.ValidationMessageFor(model => model.UserName)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.IsExternal, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.IsExternal)
#Html.ValidationMessageFor(model => model.IsExternal)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.FirstName, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.FirstName)
#Html.ValidationMessageFor(model => model.FirstName)
</div>
</div>
//code goes here
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
till now i have every thing working well, the Get Create action method will be called and the partial view will be rendered inside a modal popup.
but now inside my partial view if i click on "Create" button , the Post create action method will be called but not due to the javascript code . and when i check Request.IsAjax() inside my Post create action method, i got that it is not an ajax request which means the partial view send a normal Post http and not ajax request as defined inside the script,, can anyone advice what is wrong in my current approach ?
Thanks
as you can see you just pass the #myModalContent node to the bindForm function, and jQuery selector looks for
// will never find #myModalContent
$('#myModalContent', myModalContentDOMElement).submit(function () {
Instead you should do something like this
$('form', dialog).submit(function (e) {
e.preventDefault(); // stop the default form submit action
You are loading your form into the page via ajax, but the form you are loading is a regular html form if you want the form itself to use ajax, I believe are looking for #Ajax.BeginForm().
msdn documentation
#using (Ajax.BeginForm({objectparams})){
...