MVC, show modal content dependent on dropdown (ajax) - c#

I have no idea how to do something like this.
I have a dropdown with six element, and button.
When I click butto I want to show bootstrap modalpopup. It's a simple.
But I want to tt depends on the selection drop down.
My view:
<button type="button" class="btn btn-default" data-toggle="modal" data-target=".bs-example-modal-lg-addCompanyRisk">
<i class="fa fa-plus"></i> Add
</button>
<div class="modal fade bs-example-modal-lg-addCompanyRisk" tabindex="-1" role="dialog" aria-hidden="true" id="allCustomerModal">
<div class="modal-dialog modal-lg">
<div class="modal-content">
[... Load index from other view...]
<div class="modal-body">
<button type="submit" class="btn btn-default" data-dismiss="modal">#Resources.Common.Cancel</button>
</div>
</div>
</div>
</div>
the content of the list which is to settle on a modal should be dependent on the dropdown.
If the backet all form and button (class="btn btn-default") is a submit, then I dont know how show modal from ActionResult(controller)
EDIT
Partially I solved my problem.
I add to dropdown Ajax.BeginForm and submit
#using (Ajax.BeginForm("GetAllRiskForCompanu", "InsuranceCompanyRisks", null, new AjaxOptions
{
HttpMethod = "Post",
}))
{
#Html.AntiForgeryToken()
#Html.DropDownListFor(model => model.InsuranceCompanyId, ViewBag.InsuranceCompany as SelectList, #Resources.Common.Select, new { #class = "form-control", #required = "required", onchange = "$(this.form).submit();" })
#Html.ValidationMessageFor(model => model.InsuranceCompanyId, "", new { #class = "text-danger" })
}
on controller write method:
[HttpPost]
public Void GetAllRiskForCompanu(FormCollection form)
{
int? companyId = form["InsuranceCompanyId"].GetNullableInt();
if (companyId.HasValue)
{
//set session varible
InsurancePolicySession.InsuranceCompanyRisks = icrf.GetAll(companyId.Value);
}
}
by button add I show modala with render partialView
#Html.Partial("~/Views/InsurancePolicyItem/IndexPolicyCompanyRisk.cshtml", #InsurancePolicySession.InsuranceCompanyRisks)
when I change dropdown selected value the session refresh but when I show modal still view old value.

Rather than mess around with an AjaxForm and directly manipulating a Bootstrap modal, you might find it easier to use something like Bootbox, which will generate your Bootstrap modal as needed.
I would use a normal form:
#using(Html.BeginForm("action", "controller", FormMethod.Post, new { id="some-id", role="form" }))
{
#Html.DropDownListFor(m => m.SomeId, Model.Somethings, new { #class="form-control" })
<button type="submit" class="btn btn-default">Submit</button>
}
Then, capture the form submission and do an Ajax request to get your content:
$(function(){
$('#some-id').on('submit', function(e){
e.preventDefault(); // prevent normal form submit
var form = $(this);
var data = form.serialize();
$.post(form.attr('action'), data)
.done(function(response, status, jqxhr){
// handle response...
})
})
})
Inside the success handler (.done()), build up your dialog. Bootbox has built-in functions for alert, confirm, and prompt (it's primary purpose is to mostly replicate those native functions), but you'll probably want to use dialog(), which lets you completely customize the modal. You'll want to read the documentation to get the most out of it, but a basic usage could be (starting from where you see // handle response... above):
var dialog = bootbox.dialog({
title: 'Some Title',
message: response,
buttons: {
cancel: {
label: 'Cancel',
className: 'btn-default',
callback: function(){
dialog.modal('hide');
}
},
save: {
label: 'Submit',
className: 'btn-primary',
callback: function(){
// submit your data from the injected content, somehow...
}
}
}
});
I primarily use Bootbox at this point, simply because I find it easier than having the modal template on the page. I've also run into the same issue you seem to be having, which is that a reused modal doesn't seem to want to update it's content.
Disclaimer: I am currently a collaborator on the Bootbox project, although I am mostly there to keep the documentation up to date and triage issues.

Related

Update main view after ajax rendered a partial view

I have a list of "apps" that users can run. Each app targets a specific API of ours to demonstrate what the API does. Some of these apps require user input so that I can pass user-given parameters into the API.
Each app is responsible for generating the HTML that represents its output. For Apps that do not require any input, it's a straight forward process of executing them in a controller/action from an ajax request, and updating the view with the output.
The challenge is wiring up user input support. I've managed to get 90% of the way there and have hit a roadblock. The apps are responsible for instantiating their own view model. Using a bit of convention, each App has an associated partial view that is located under the same path that the app's namespace is. This lets me create a view model for the app, and return the partial view for each app like this:
public ActionResult GetViewModel(string appId)
{
IApp app = AppFactory.GetAppById(appId);
string path = app.GetType().FullName.Replace('.', '/');
return PartialView($"~/Views/{path}.cshtml", app.CreateViewModel());
}
An example partial view, using an app supplied view model, looks like this:
#using Examples.DataAccess.Query;
#model Query_02_ParameterizedQueryViewModel
#using (#Html.BeginForm("RunAppFromViewModel", "Home", FormMethod.Post))
{
#Html.ValidationSummary(true)
<fieldset>
<div class="form-inline">
<div class="form-group">
#Html.LabelFor(viewModel => viewModel.City)
#Html.EditorFor(viewModel => viewModel.City, new { placeholder = "Phoenix" })
#Html.ValidationMessageFor(viewModel => viewModel.City)
#Html.HiddenFor(viewModel => viewModel.AppId)
</div>
</div>
<button class="btn btn-default" type="submit">Run</button>
</fieldset>
}
The main view has a button that opens a modal bootstrap dialog. When the dialog is opened, I make an ajax request to the server to get the view model and partial view. I then insert the partial view into the modal dialog and update the client-side validation so it works with the unobtrustive stuff. The problem however is that when the form is posted back to the server, and the outputted HTML from the app is returned from the server to the client, I dont know how to update the main view with it.
For example, this is the Main view and the JavaScript that handles both the View Model based Apps and the non-VM based apps.
#using Examples.Browser.ViewModels;
#using Examples.Browser.Models;
#{
ViewBag.Title = "Home Page";
}
#model List<ApiAppsViewModel>
<div class="jumbotron">
<h1>Framework API Micro-Apps</h1>
<p class="lead">Micro-apps provide a way to execute the available APIs in the Framework, without having to write any code, and see the results.</p>
</div>
<div class="container">
<h3 class="text-center">Available API Apps</h3>
<div class="table-responsive">
<table class="table table-hover table-responsive">
<tr>
<th>#nameof(ApiApp.Components)</th>
<th>#nameof(ApiApp.Name)</th>
<th>#nameof(ApiApp.Description)</th>
<th>Options</th>
</tr>
#foreach (ApiAppsViewModel app in this.Model)
{
<tr>
<td>#string.Join(",", app.Components)</td>
<td>#app.Name</td>
<td>#app.Description</td>
<td>
#if (app.IsViewModelRequired)
{
<button type="button"
data-app="#app.Id.ToString()"
data-vm-required="#app.IsViewModelRequired"
data-app-name="#app.Name"
data-toggle="modal"
data-target="#appParameters"
class="btn btn-success">
Run App
</button>
}
else
{
<button type="button"
data-app="#app.Id.ToString()"
data-vm-required="#app.IsViewModelRequired"
class="btn btn-success">
Run App
</button>
}
</td>
</tr>
<tr class="hidden">
<td colspan="4">
<div class="container alert alert-info" data-app="#app.Id.ToString()">
</div>
</td>
</tr>
}
</table>
</div>
</div>
<div class="modal fade"
id="appParameters"
role="dialog"
aria-labelledby="appParametersLabel">
<div class="modal-dialog"
role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
<h4 class="modal-title" id="appParametersLabel"></h4>
</div>
<div class="modal-body" id="appDialogBody">
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript">
$('.btn-success').click(function () {
var button = $(this);
var appId = $(this).data("app");
var vmRequired = $(this).data("vm-required");
if (vmRequired == "False") {
var url = "/Home/RunApp?appId=" + appId;
$.get(url, function (data) {
$("div[data-app='" + appId + "']").html(data);
var buttonColumn = button.parent();
var appRow = buttonColumn.parent();
var hiddenRow = appRow.next()
hiddenRow.removeClass("hidden");
appRow.click(function () {
var hiddenColumn = hiddenRow.children().first();
var resultsDiv = hiddenColumn.children().first();
resultsDiv.empty();
hiddenRow.addClass("hidden");
$(this).off();
hiddenRow.off();
})
hiddenRow.click(function () {
var hiddenColumn = $(this).children().first();
var resultsDiv = hiddenColumn.children().first();
resultsDiv.empty();
$(this).addClass("hidden");
appRow.off();
$(this).off();
})
});
} else {
var appName = $(this).data("app-name");
$('#appParametersLabel').html(appName);
var url = "/Home/GetViewModel?appId=" + appId;
$.get(url, function (data) {
$('#appDialogBody').html(data);
var dialog = $('#appDialogBody');
$.validator.unobtrusive.parse(dialog);
});
$('#appParameters').modal({
keyboard: true,
backdrop: "static",
show: false,
}).on('show', function () {
});
}
});
</script>
When there isn't a view model needed, I stuff the results in an invisible row and make the row visible. Since the View Model apps have their form data submitted from a partial view, when I return the HTML from the controller, it renders it out as raw text to the browser. I assume I can write some java script to handle this but i'm not sure what that would look like. How do I get the form post from the partial view, to return the HTML it generates back to the invisible row within the main view?
This is the controller action that the form posts to, and returns, along with the controller action non-view model based apps use to run their apps and generate the HTML.
[HttpGet]
public async Task<string> RunApp(string appId)
{
IApp app = AppFactory.GetAppById(appId);
if (app == null)
{
return "failed to locate the app.";
}
IAppOutput output = await app.Run();
if (output == null)
{
return "Failed to locate the app.";
}
return output.GetOutput();
}
[HttpPost]
public async Task<string> RunAppFromViewModel(FormCollection viewModelData)
{
IApp app = AppFactory.GetAppById(viewModelData["AppId"]);
foreach(PropertyInfo property in TypePool.GetPropertiesForType(app.ViewModel.GetType()))
{
property.SetValue(app.ViewModel, viewModelData[property.Name]);
}
IAppOutput output = await app.Run();
return output.GetOutput();
}
If you want to update the existing page with the data returned by the RunAppFromViewModel() method, then you need to submit your form using ajax. Since the form is loaded dynamically after the initial page has been loaded, you need to use event delegation. You will also need to store the element you want updated when the form is loaded.
var hiddenRow;
$('.btn-success').click(function () {
// store the element to be updated
hiddenRow = $(this).closest('tr').next('tr').find('.container');
....
});
// Handle the submit event of the form
$('#appDialogBody').on('submit', 'form', function() {
// check if the form is valid
if (!$(this).valid())
{
return;
}
var formData = $(this).serialize(); // serialize the forms controls
var url = '#Url.Action("RunAppFromViewModel", "Home");
$.post(url, formData , function(response) {
hiddenRow.html(response); // assumes your returning html
});
return false; // cancel the default submit
});

Summernote and form submission in MVC c#

I am using the summernote plugin for text box:
http://summernote.org/#/getting-started#basic-api
This is the form I have using summmernote:
<div class="modal-body" style="max-height: 600px">
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset class="form-horizontal">
<div id="textForLabelLanguage"></div>
<button type="submit" class="btn btn-primary">Save changes</button>
#Html.ActionLink("Cancel", "Index", null, new { #class = "btn " })
</fieldset>
}
</div>
<script type="text/javascript">
$(document).ready(function () {
$('#textForLabelLanguage').summernote();
});
</script>
Now, In my controller, this is the code I have:
public ActionResult Create(UserInfo newInfo , [Bind(Prefix = "textForLabelLanguage")] string textForLabelLanguage)
{
//logic here
}
Now the problem is that textForLabelLanguage param is always null.
This happens because I have to pass $('#textForLabelLanguage').code(); into MVC when submiting the form but I have no idea how to do that!
How do I solve my problem?
I found my solution to the problem. This is how I am making the controller get the correct information:
<div class="modal-body" style="max-height: 600px">
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset class="form-horizontal">
<textarea name="textForLabelLanguage" id="textForLabelLanguage" />
<button type="submit" class="btn btn-primary">Save changes</button>
#Html.ActionLink("Cancel", "Index", null, new { #class = "btn " })
</fieldset>
}
</div>
<script type="text/javascript">
$(document).ready(function () {
$('#textForLabelLanguage').summernote();
});
</script>
Basically, if I use a textarea with a name instead of an input or anything else, it works!
However, and be warned, even though this solution works, I then get a error in the controller saying:
A potentially dangerous Request.Form value was detected from the client
This happens because I am allowing HTML. But this is a problem for another question!
Please, use [AllowHTML]
There's a good article on MSDN
Request Validation in ASP.NET
"To disable request validation for a specific property, mark the property definition with the AllowHtml attribute:"
[AllowHtml]
public string Prop1 { get; set; }
similar to what was posted earlier you can use the HTML helper
#HTML.TextAreaFor( m=> m.text, new {#id = "textFor*Model*"})
instead of <textarea>

How to set focus on font awesome icon mapped to radio button

I am using Twitter Bootstrap in an ASP.NET MVC application. In one page I want to show a Grid or List view when a user click on the relevant icon. To do that, I'm using radio buttons and it does show based on user selection.
But the problem is that it always focuses on the Grid icon, even if it fires list mode.
Here's My Code:
<div class="row" >
<div class="col-xs-4 col-sm-4">
<form class="pull-left">
<div class="btn-group" data-toggle="buttons">
<label class="btn btn-primary active" title="Show as a Grid" >
<i class="fa fa-table"></i>
#Html.RadioButtonFor(model => Model.Context.ViewMode, "grid",
new { name = "viewmode", #class = "",
onchange = "setLocation('?viewmode=grid');" })
Grid
</label>
<label class="btn btn-primary" title="Show as a List" >
<i class="fa fa-list"></i>
#Html.RadioButtonFor(model => Model.PagingFilteringContext.ViewMode, "list",
new { name = "viewmode", #class = "",
onchange = "setLocation('?viewmode=list');" })
List
</label>
</div>
</form>
</div>
</div>
<div class="show-content">
#if (Model.Context.ViewMode == "grid")
{
<label>Grid content here...</label>
}
else
{
<label>List content here...</label>
}
</div>
// set visibility between icons
$(document).ready(function () {
$("#Context_ViewMode").on('click', function () {
ToggleRadioButtons("#Context_ViewMode", $(this));
});
});
function ToggleRadioButtons(groupName, current) {
var chk = $(groupName + " .fa-table");
$(chk).removeClass('fa-table').addClass('fa-list');
$(current).find(">:first-child").removeClass('fa-list');
$(current).find(">:first-child").addClass('fa-table');
}
But it didn't set focus on List icon when clicked. Any ideas?
Edit:
I managed to get event firing work, but it doesn’t stay as selected if ‘List’ selected, change back to ‘Grid’ highlighted(active) after loading correct ‘List’ result from the server.
Summary of changes:
Added new class ‘fawsm-radiobutton’ for both labels
Added new class ‘nonactive’ for label list
Changed the JavaScript to add remove 'active' and 'notactive'
Here’s My Code changes:
<label class="fawsm-radiobutton btn btn-primary active" title="Show as a Grid" >
<i class="fa fa-table"></i>
#Html.RadioButtonFor(model => Model.Context.ViewMode, "grid",
new { name = "viewmode", #class = "",
onchange = "setLocation('?viewmode=grid');" })
Grid
</label>
<label class="fawsm-radiobutton btn btn-primary notactive" title="Show as a List" >
<i class="fa fa-list"></i>
#Html.RadioButtonFor(model => Model.PagingFilteringContext.ViewMode, "list",
new { name = "viewmode", #class = "",
onchange = "setLocation('?viewmode=list');" })
List
</label>
$(document).ready(function () {
$("#Context_ViewMode>.fawsm-radiobutton").on('change', function () {
ToggleRadioButtons("#Context_ViewMode", $(this));
});
});
function ToggleRadioButtons(groupName, current) {
var chk = $(groupName + " .fawsm-radiobutton.active");
$(chk).removeClass('active’).addClass('notactive');
$(current).find(">:first-child").removeClass('notactive');
$(current).find(">:first-child").addClass('active');
}
When I use developer tool(F12) on the browser it shows the removal and addition of ‘active’ and ‘notactive’ classes to lable. But after loading List items from the server it revert back to original ‘Grid’ icon in active mode.
So I guess that when the browser renders
#if (Model.Context.ViewMode == "grid")
{}
else{}
section I need to notify client to do the above changes to the label classes. I do not know how to do it. Do I need to use something like AJAX?
You should use the .focus() method.

Handling form submit event

I have the following form:
#using (Html.BeginForm(new { ReturnUrl = ViewBag.ReturnUrl, #class = "form-vertical login-form", id = "loginform" }))
<button type="submit" class="btn green pull-right">
Login <i class="m-icon-swapright m-icon-white"></i>
</button>
}
And this the javascript handling the event function
$(document).ready(function () {
$("#loginform").submit(function () {
alert('Handler for .submit() called.');
return false;
});
}
However this doesn't work at all.
the alert is never triggered
That's normal, you never assigned your form an id or a class. Look at the generated HTML in your browser to understand what I mean.
You are using a completely wrong overload of the BeginForm helper.
Try this instead:
#using (Html.BeginForm(null, null, new { returnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { #class = "form-vertical login-form", id = "loginform" }))
{
<button type="submit" class="btn green pull-right">
Login <i class="m-icon-swapright m-icon-white"></i>
</button>
}
Now please read about the different BeginForm overloads on MSDN and compare yours with mine.
As you can see there's a difference between the routeValues parameter and the htmlAttributes parameter.

What's the best way to call a modal dialog in ASP.NET MVC using Twitter Bootstrap?

I'm currently using Twitter's Bootstrap toolkit on a new project and I had a question on the best way to use the modal dialog in ASP.NET MVC3.
Is the best practice to have a Partial that contains the modal's markup and then use javascript to render that onto the page or is there a better approach?
Here goes my little tutorial which demonstrates Twitter's Bootstrap (2.x) modal dialog that works with forms and partials in ASP.Net MVC 4.
To download similar project but targeting MVC 5.1 and Bootstrap 3.1.1 please visit this site.
Start with an empty MVC 4 Internet template.
Add reference to Bootstrap using NuGet
In the App_Start/BundleConfig.cs add the following lines:
bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include("~/Scripts/bootstrap.js"));
bundles.Add(new StyleBundle("~/Content/bootstrap").Include(
"~/Content/bootstrap.css",
"~/Content/bootstrap-responsive.css"));
In the Views/Shared/_Layout.cshtml
modify the #styles.Render line so it will look like:
#Styles.Render("~/Content/css", "~/Content/themes/base/css", "~/Content/bootstrap")
and the #Scripts.Render line:
#Scripts.Render("~/bundles/jquery", "~/bundles/jqueryui", "~/bundles/bootstrap")
So far we have Bootstrap prepared to work with MVC 4 so let's add a simple model class MyViewModel.cs to the /Models folder:
using System.ComponentModel.DataAnnotations;
namespace MvcApplication1.Models
{
public class MyViewModel
{
public string Foo { get; set; }
[Required(ErrorMessage = "The bar is absolutely required")]
public string Bar { get; set; }
}
}
In the HomeController Add the following lines:
using MvcApplication1.Models;
//...
public ActionResult Create()
{
return PartialView("_Create");
}
[HttpPost]
public ActionResult Create(MyViewModel model)
{
if (ModelState.IsValid)
{
try
{
SaveChanges(model);
return Json(new { success = true });
}
catch (Exception e)
{
ModelState.AddModelError("", e.Message);
}
}
//Something bad happened
return PartialView("_Create", model);
}
static void SaveChanges(MyViewModel model)
{
// Uncommment next line to demonstrate errors in modal
//throw new Exception("Error test");
}
Create new Partial View in the Views/Home folder and name it _Create.cshtml:
#using MvcApplication1.Models
#model MyViewModel
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3 id="myModalLabel">Create Foo Bar</h3>
</div>
#using (Html.BeginForm("Create", "Home", FormMethod.Post, new { #class = "modal-form" }))
{
#Html.ValidationSummary()
<div class="modal-body">
<div>
#Html.LabelFor(x => x.Foo)
#Html.EditorFor(x => x.Foo)
#Html.ValidationMessageFor(x => x.Foo)
</div>
<div>
#Html.LabelFor(x => x.Bar)
#Html.EditorFor(x => x.Bar)
#Html.ValidationMessageFor(x => x.Bar)
</div>
</div>
<div class="modal-footer">
<button class="btn" data-dismiss="modal" aria-hidden="true">Undo</button>
<button class="btn btn-primary" type="submit">Save</button>
</div>
}
In the Home/Index.cshtml remove the default content from the template and replace it with following:
#{
ViewBag.Title = "Home Page";
}
<br />
<br />
<br />
#Html.ActionLink("Create", "Create", null, null, new { id = "btnCreate", #class = "btn btn-small btn-info" })
<div id='dialogDiv' class='modal hide fade in'>
<div id='dialogContent'></div>
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
<script type="text/javascript">
$(function () {
//Optional: turn the chache off
$.ajaxSetup({ cache: false });
$('#btnCreate').click(function () {
$('#dialogContent').load(this.href, function () {
$('#dialogDiv').modal({
backdrop: 'static',
keyboard: true
}, 'show');
bindForm(this);
});
return false;
});
});
function bindForm(dialog) {
$('form', dialog).submit(function () {
$.ajax({
url: this.action,
type: this.method,
data: $(this).serialize(),
success: function (result) {
if (result.success) {
$('#dialogDiv').modal('hide');
// Refresh:
// location.reload();
} else {
$('#dialogContent').html(result);
bindForm();
}
}
});
return false;
});
}
</script>
}
If you run your application, a nice Bootstrap modal will appear after clicking the Create button on the Home page.
Try to uncomment the SaveChanges() //throw line in HomeController.cs to prove that your controller handled errors will appear correctly in the dialog.
I hope that my sample clarifies a bit whole process of incorporating Bootstrap and creating modals in the MVC application.
Great example, I had to modify slightly for MVC 5 and Bootstrap 3.3.7, I changed the target div tags to the following, otherwise I was just getting the grey background and no modal dialog. Hope this helps someone.
<div id='dialogDiv' class='modal fade' tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div id='dialogContent'></div>
</div>
</div>
</div>
Thanks #zjerry the solution is great but jQuery validation does not work, in order to fix you need to change the function bindForm as follow:
function bindForm(dialog) {
$.validator.unobtrusive.parse('form');
$('form', dialog).submit(function () {
$.ajax({
url: this.action,
type: this.method,
data: $(this).serialize(),
success: function (result) {
if (result.success) {
$('#dialogDiv').modal('hide');
// Refresh:
// location.reload();
} else {
$('#dialogContent').html(result);
bindForm();
}
}
});
return false;
});
}
Notice the first line of the function, because the form is loaded after jQuery validation was initialized.
Many thanks
This really depends on your design, but you should have a template for the modals.
On a single web app for example, you should have a template lying around that you would create a new instance from each time.
Usually on a normal website you would want to store this template inside a js creation function, so that you won't have to send the file to the user each time via http, and they can have it cached.

Categories