Ajax.ActionLink alternative with mvc core - asp.net-mvc

In MVC5 there is #Ajax.ActionLink that is useful to update just a partial view instead of reloading the whole View. Apparently in MVC6 is not supported anymore.
I have tried using #Html.ActionLink like the following but it doesn't update the form, it return just the partial view:
View:
#Html.ActionLink("Update", "GetEnvironment", "Environments", new { id = Model.Id }, new
{
data_ajax = "true",
data_ajax_method = "GET",
data_ajax_mode = "replace",
data_ajax_update = "environment-container",
#class = "btn btn-danger"
})
control:
public async Task<ActionResult> GetEnvironment(int? id)
{
var environments = await _context.Environments.SingleOrDefaultAsync(m => m.Id == id);
return PartialView("_Environment",environments);
}
Partial view:
#model PowerPhysics.Models.Environments
this is a partial view
Then I tried using ViewComponents. When the page loads the component works correctly but I don't understand how to refresh just the component afterward (for example with a button):
View:
#Component.InvokeAsync("Environments", new { id = Model.Id }).Result
component:
public class EnvironmentsViewComponent : ViewComponent
{
public EnvironmentsViewComponent(PowerPhysics_DataContext context)
{
_context = context;
}
public async Task<IViewComponentResult> InvokeAsync(int? id)
{
var environments = await _context.Environments.SingleOrDefaultAsync(m => m.Id == id);
return View(environments);
}
}
How can I update just a part of a view by using PartialViews in MVC6?

You can use a tag as follows:
<a data-ajax="true"
data-ajax-loading="#loading"
data-ajax-mode="replace"
data-ajax-update="#editBid"
href='#Url.Action("_EditBid", "Bids", new { bidId = Model.BidId, bidType = Model.BidTypeName })'
class="TopIcons">Link
</a>
Make sure you have in your _Layout.cshtml page the following script tag at the end of the body tag:
<script src="~/lib/jquery/jquery.unobtrusive-ajax/jquery.unobtrusive-ajax.js"></script>

ViewComponent's are not replacement of ajaxified links. It works more like Html.Action calls to include child actions to your pages (Ex : Loading a menu bar). This will be executed when razor executes the page for the view.
As of this writing, there is no official support for ajax action link alternative in aspnet core.
But the good thing is that, we can do the ajaxified stuff with very little jQuery/javascript code. You can do this with the existing Anchor tag helper
<a asp-action="GetEnvironment" asp-route-id="#Model.Id" asp-controller="Environments"
data-target="environment-container" id="aUpdate">Update</a>
<div id="environment-container"></div>
In the javascript code, just listen to the link click and make the call and update the DOM.
$(function(){
$("#aUpdate").click(function(e){
e.preventDefault();
var _this=$(this);
$.get(_this.attr("href"),function(res){
$('#'+_this.data("target")).html(res);
});
});
});
Since you are passing the parameter in querystring, you can use the jQuery load method as well.
$(function(){
$("#aUpdate").click(function(e){
e.preventDefault();
$('#' + $(this).data("target")).load($(this).attr("href"));
});
});

I add ajax options for Anchor TagHelper in ASP.NET MVC Core
you can see complete sample in github link :
https://github.com/NevitFeridi/AJAX-TagHelper-For-ASP.NET-Core-MVC
after using this new tagHelper you can use ajax option in anchor very easy as shown below:
<a asp-action="create" asp-controller="sitemenu" asp-area="admin"
asp-ajax="true"
asp-ajax-method="get"
asp-ajax-mode="replace"
asp-ajax-loading="ajaxloading"
asp-ajax-update="modalContent"
asp-ajax-onBegin="showModal()"
asp-ajax-onComplete=""
class="btn btn-success btn-icon-split">
<span class="icon text-white-50"><i class="fas fa-plus"></i></span>
<span class="text"> Add Menu </span>
</a>

Use tag helpers instead and make sure to include _ViewImport in your views folder.
Note: Make sure to use document.getElementsByName if there are several links pointing to different pages that will update your DIV.
Example - Razor Page
<script type="text/javascript" language="javascript">
$(function () {
var myEl = document.getElementsByName('theName');
$(myEl).click(function (e) {
e.preventDefault();
var _this = $(this);
$.get(_this.attr("href"), function (res) {
$('#' + _this.data("target")).html(res);
});
});
});
</script>
<a asp-action="Index" asp-controller="Battle" data-target="divReplacable" name="theName" >Session</a>
<a asp-action="Index" asp-controller="Peace" data-target="divReplacable" name="theName" >Session</a>
<div id="divReplacable">
Some Default Content
</div>

Related

asp.net mvc ajax.beginform being sent as html.beginform

I have a partial view from which I would like to display a modal dialog with updated data. User clicking the div would trigger both the display of the modal and the ajax call for the content of the modal to be updated.
<div class="nMmenuItem" >
#using (Ajax.BeginForm("editItem","nMrestaurant",new { id = Model.ID },
new AjaxOptions
{
HttpMethod = "get",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "myModalDocument"
}, new { id = "ajaxEditItem" }))
{
<div data-toggle="modal" data-target="#myModal"
onclick="$('form#ajaxEditItem').submit();">
<div class="text-center">
#Model.name
</div>
</div>
}
</div>
I have a placeholder for the modal inside the parent view:
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document" id="myModalDocument">
#Html.Partial("_editItem", new nMvMmenuItem())
</div>
</div>
But while the controller action is expecting an AjaxResquest, the controller is evaluating Request.IsAjaxRequest() as false.
public async Task<ActionResult> editItem(int? id)
{
if (Request.IsAjaxRequest())
{
return PartialView("_editItem", await db.nMmenuItems.FindAsync(id));
}
return View();
}
Which refreshes the whole view and prevents the modal from working.
I am bundling the following scripts in the _Layout.cshtml page:
"~/Scripts/jquery-{version}.js",
"~/Scripts/jquery-ui-{version}.js",
"~/Scripts/jquery.unobstrusive*",
"~/Scripts/jquery.validate",
"~/Scripts/bootstrap.js",
"~/Scripts/respond.js"
Thanks for your help!
Check that you've got the unobtrusive ajax client scripts installed - your bundle pattern looks like it will pick them up if they are there, but I don't believe they are installed in the default project:
Install-Package Microsoft.jQuery.Unobtrusive.Ajax
While the Ajax.BeginForm is included in the standard MVC project, the client scripts are not and these are what is responsible for loading the content without refreshing the whole page.
I found that attaching submit() to the form's onclick event would not perform an ajax request.
My solution is thus to remove Ajax.SubmitForm and instead deal with the click event in my js:
The updated view looks like this:
<div class="nMmenuItem">
<form method="get" action="#Url.Action("editItem","nMrestaurant",new { id = Model.ID })"
data-nM-ajax="true" data-nM-target="#myModalContent">
<div>
<div class="text-center">
#Model.name
</div>
</div>
</form>
In the js I will bind the form submission to the click event of the parent div:
$('.nMmenuItem').click(ajaxFormSubmit);
And the function that handles the form submission and opens the resulting modal dialog:
var ajaxFormSubmit = function () {
var $form = $(this).children('form:first');
var options = {
url: $form.attr("action"),
type: $form.attr("method"),
data: $form.serialize()
};
$.ajax(options).done(function (data) {
var $target = $($form.attr("data-nM-target"));
$target.replaceWith(data);
$("#myModal").modal(dialogOpts);
});
return false;
};

How to run Angular JS on after page load rendered html?

I'm developing asp.net mvc a project with angular js.
I'm working on tabs and install related partial view after click event.
I am sending with partial view html of the json to main page but angular codes doesn't work on the page
What can i do?
Sample Problem
html:
<div ng-app="MyAppS">
<div ng-controller="AnaTest">
<button id="btn1" ng-click="btn1Click()">click</button>
</div>
<div id="m_area">
</div>
<br />{{ 'Hello Angular' }}</div>
javascript:
var m_app = angular.module('MyAppS', []);
function AnaTest($scope) {
$scope.btn1Click = function () {
var runtimeBtn = angular.element("<button ng-click=\"btn2Click()\">Help Me! </button>");
$('#m_area').html(runtimeBtn);
};
$scope.btn2Click = function(){
debugger;
alert('Why can not show?!');
};
};
m_app.controller('AnaTest', AnaTest);
You need to $compile it:
var runtimeBtn = $compile(angular.element("<button ng-click=\"btn2Click()\">Help Me!</button>"))($scope);
See it here: http://jsfiddle.net/7yqrjdkk/8/
However, a more "Angular" way to do it would be putting it under the same controller/scope and simply using ng-show, like this: http://jsfiddle.net/7yqrjdkk/9/

Facing issue while showing the error messages in Partial View using unobstructive JQuery

Following is my Area in MVC3
Model
public class AdminModule
{
[Display(Name = "My Name")]
[Required]
public String MyName { get; set; }
}
Partial View
#model _1.Areas.Admin.Models.AdminModule
#using (Html.BeginForm(null, null, FormMethod.Post, new { id = "myForm" }))
{
#Html.LabelFor(i => i.MyName)
#Html.TextBoxFor(i => i.MyName)
#Html.ValidationMessageFor(i => i.MyName)
<p id="getDateTimeString">
</p>
<input type="submit" value="Click here" id="btn" />
}
<script language="javascript" type="text/javascript">
$('#btn1').click(function () {
debugger;
var $form = $("#myForm");
// Unbind existing validation
$form.unbind();
$form.data("validator", null);
// Check document for changes
$.validator.unobtrusive.parse(document);
// Re add validation with changes
$form.validate($form.data("unobtrusiveValidation").options);
if ($(this).valid()) {
var url = '#Url.Action("Index_partialPost", "Admin",
new { area = "Admin" })';
$.post(url, null, function (data) {
alert(data);
$('#myForm').html(data);
});
}
else
return false;
});
</script>
Controller Action
[HttpPost]
public ActionResult Index_partialPost(AdminModule model)
{
return PartialView("_PartialPage1", model);
}
[HttpGet]
public ActionResult Index_partial()
{
return PartialView("_PartialPage1");
}
Whenever I submit the form and leaves the required field empty. it goes to server i think. I checked here...
My confusion is => How can I modify my below mentioned code to display the same validation messages mentioned in model at client end using $.post ?
You could enable unobtrusive client side validation. Start by adding the following script reference:
<script type="text/javascript" src="#Url.Content("~/scripts/jquery.validate.unobtrusive.js")"></script>
and then:
#model _1.Areas.Admin.Models.AdminModule
#using (Html.BeginForm(null, null, FormMethod.Post, new { id = "myForm" }))
{
#Html.LabelFor(i => i.MyName)
#Html.TextBoxFor(i => i.MyName)
#Html.ValidationMessageFor(i => i.MyName)
<p id="getDateTimeString"></p>
<input type="submit" value="Click here" />
}
<script type="text/javascript">
$('#myForm').submit(function () {
if ($(this).valid()) {
$.post(this.action, $(this).serialize(), function(data) {
$('#myForm').html(data);
$('#myForm').removeData('validator');
$('#myForm').removeData('unobtrusiveValidation');
$.validator.unobtrusive.parse('#myForm');
});
}
return false;
});
</script>
UPDATE:
Now that you sent me your actual code by email I see that there are a hell lot of a problems with it. Instead of going through all of them I prefer to completely rewrite everything from scratch.
So we start by the ~/Areas/Admin/Views/Shared/_LayoutPage1.cshtml:
<!DOCTYPE html>
<html>
<head>
<title>#ViewBag.Title</title>
</head>
<body>
<div>
<ul>
<li>#Html.ActionLink("Home", "Index", "Home", new { area = "" }, null)</li>
</ul>
#RenderBody()
</div>
<script src="/Scripts/jquery-1.4.1.min.js" type="text/javascript"></script>
<script src="/Scripts/jquery.validate.js" type="text/javascript"></script>
<script src="/Scripts/jquery.validate.unobtrusive.js" type="text/javascript"></script>
#RenderSection("Scripts", required: false)
</body>
</html>
Notice how I moved all scripts to the bottom of the file as well as added a specifically dedicated section where custom scripts will be placed.
Next we move to the ~/Areas/Admin/Views/Admin/Index.cshtml:
#model _1.Areas.Admin.Models.AdminModule
#{
ViewBag.Title = "Index";
Layout = "~/Areas/Admin/Views/Shared/_LayoutPage1.cshtml";
}
<div id="formContainer" data-url="#Url.Action("Index_partial", "Admin", new { area = "Admin" })"></div>
<input id="BTN" type="button" value="Button" />
#section Scripts {
<script type="text/javascript" src="#Url.Content("~/areas/admin/scripts/myscript.js")"></script>
}
Here you could notice that I have replaced the type of the button from submit to button because this button is not contained within a form to submit. I have also gotten rid of the <p> element you were having with id="myForm" which was not only useless but you were ending up with duplicate ids in your DOM which is invalid markup. I have also used the data-url HTML5 attribute on the container div to indicate the url of the server side script that will load the form. And the last thing I did in this file was to override the scripts section we defined earlier in the Layout with a custom script.
So the next part is the custom script: ~/areas/admin/scripts/myscript.js:
$('#BTN').click(function () {
var $formContainer = $('#formContainer');
var url = $formContainer.attr('data-url');
$formContainer.load(url, function () {
var $form = $('#myForm');
$.validator.unobtrusive.parse($form);
$form.submit(function () {
var $form = $(this);
if ($form.valid()) {
$.post(this.action, $(this).serialize(), function (data) {
$form.html(data);
$form.removeData('validator');
$form.removeData('unobtrusiveValidation');
$.validator.unobtrusive.parse($form);
});
}
return false;
});
});
return false;
});
Pretty standard code here. We subscribe to the click event of the button and load the partial using an AJAX call. As soon as this is done we instruct the unobtrusive validation framework to parse the newly added contents to our DOM. The next step is to AJAXify the form by subscribing to the .submit event. And because in the success handler we are once again replacing the contents of the container we need to instruct the unobtrusive validation framework to parse the new contents.
and finally the partial:
#model _1.Areas.Admin.Models.AdminModule
#using (Html.BeginForm("Index_partialPost", "Admin", FormMethod.Post, new { id = "myForm" }))
{
#Html.LabelFor(i => i.MyName)
#Html.TextBoxFor(i => i.MyName)
#Html.ValidationMessageFor(i => i.MyName)
<p id="getDateTimeString"></p>
<input type="submit" value="Click here" />
}
Here you could notice that I have gotten rid of absolutely any traces of javascript. javascript belongs to separate files. It has nothing to do in views. You should not mix markup and scripts. When you have separate scripts not only that your dynamic markup will be much smaller but also you could take advantage of things like browser caching for the external scripts, minification, ... Another thing you will notice in this partial is that I remove the <script> nodes in which you were referencing jQuery and the jQuery.validate scripts. You already did that in the Layout, do not repeat it twice.

Multiple ViewModels with Knockout and ASP.NET MVC4 SPA

I'm new to ASP.NET MVC SPA and Knockout.js os maybe it's a simple mistake I made...
Situation: I have two partialviews in my website and I want that every partialview has his own Knockout ViewModel so I won't get a huge ViewModel.
My current ViewModel:
/// <reference path="../_references.js" />
function MobileDeliveriesViewModel() {
var self = this;
// Data
self.currentDelivery = ko.observable();
self.nav = new NavHistory({
params: { view: 'deliveries', deliveryId: null }
});
// Test
self.foo = "FooBar"
self.bar = "BarFoo"
self.nav.initialize({ linkToUrl: true });
// Navigate Operations
self.showDeliveries = function () { self.nav.navigate({ view: 'deliveries' }) }
self.showCustomers = function () { self.nav.navigate({ view: 'customers' }) }
}
function BarFooViewModel() {
var self = this
//MobileDeliveriesViewModel.call(self)
self.bar2 = "BarFooTwo"
}
ko.applyBindings(new MobileDeliveriesViewModel());
ko.applyBindings(new MobileDeliveriesViewModel(), $('#BarFoo')[0]);
ko.applyBindings(new BarFooViewModel(), document.getElementById('BarFoo'));
My Index.cshtml:
<div data-bind="if: nav.params().view == 'deliveries'">
#Html.Partial("_DeliveriesList")
</div>
<div class="BarFoo" data-bind="if: nav.params().view == 'customers'">
#Html.Partial("_CustomersList")
</div>
<script src="~/Scripts/App/DeliveriesViewModel.js" type="text/javascript"></script>
My CustomerPartialView:
<div id="BarFoo" class="content">
<p data-bind="text: bar"></p>
<p data-bind="text: bar2"></p>
<button data-bind="click: showDeliveries, css: { active: nav.params().view == 'deliveries' }">Deliveries</button>
</div>
My DeliveriesPartialView:
<div class="content">
<p data-bind="text: foo"></p>
<button data-bind="click: showCustomers, css: { active: nav.params().view == 'customers' }">Customers</button>
</div>
If I run this, it won't recognize the bar2 propertie in my BarFooViewModel...
I have tried 2 different applyBindings at the end of my ViewModel.
Anybody got an idea or is their a better way/pattern to do this?
are there JS errors on page?
nav.params().view
but params: { view: 'deliveries', deliveryId: null } - it's not function.
and if you want use a few view models on single page - check this http://www.knockmeout.net/2012/05/quick-tip-skip-binding.html?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+KnockMeOut+%28Knock+Me+Out%29 acticle. you have to use "stopBinding"
It looks like you are applying multiple data bindings to the same sections.
ko.applyBindings(new MobileDeliveriesViewModel();
This will bind to all elements one the page.
ko.applyBindings(new MobileDeliveriesViewModel(), $('#BarFoo')[0]);
this will try to bind to all elements inside the div
ko.applyBindings(new BarFooViewModel(), document.getElementById('BarFoo'));
This will also try to bind to all elements inside the div.
To keep things simple, you should try to bind a single view model to a single html section. I've found that trying to bind two view models in the same html section has been hard to get work correctly and trouble shoot.
Jack128's answer also makes some good points.

ASP.NET MVC.NET JQueryUI datepicker inside a div loaded/updated with ajax.actionlink

I'm trying to incorporate jqueryUI's datepicker inside a partialview like this:
<% using (Ajax.BeginForm("/EditData",
new AjaxOptions { HttpMethod = "POST",
UpdateTargetId = "div1" }))
{%>
Date:
<%= Html.TextBox("date", String.Format("{0:g}", Model.date), new { id = "datePicker"})%>
<% } %>
<script type="text/javascript">
$(function() {
$("#datePicker").datepicker();
});
</script>
When I directly call the url to this partial view, so it renders only this view the datepicker works perfectly. (For the purpose of testing this I added the needed jquery and jqueryui script and css references directly to the partial view)
But if I use a Ajax.Actionlink to load this partial view inside a div (called div2, submitting the above form should update div1) like this:
<div id="div1">
<%= Ajax.ActionLink("Edit", "/EditData", new { id = Model.id }, new AjaxOptions { HttpMethod = "GET", UpdateTargetId = "div2" } )%>
</div>
<div2>placeholder for the form</div>
The datepicker won't appear anymore.
My best guess is the javascript included in the loaded html doesn't get executed,
($(document).ready(function() {
$("#datepicker").datepicker();
}); doesnt work either
If that's the case how and where should I call the $("datepicker").datepicker(); ?
(putting it in the ajaxoptions of the ajax.actionlink as oncomplete = "$(function() {
$('#datepicker').datepicker();});" still doesnt work.
If that's not the case, then where's my problem?
The answer given by veggerby probably will be working in the given scenario, therefor i marked it as correct answer.
My problem here was that the javascript is in a portion of html being dynamicly loaded thrue ajax. Then the loaded javascript code wont be picked up by the javascript interpreter (or whatever im supposed to call the javascript handling on the clientside).
In my case veggerby's sollution wouldnt work either but that's because in my app i even loaded that piece of html+javascript thrue ajax. which results in the same problem.
i didnt feel like putting the javascript in the first normally loaded page, since it doesnt always load the same piece of app (thus possibly executing code when its not needed).
i resolved this by creating a sepperate .js script which is included in the site.master:
function rebindJQuery(data) {
jQuery('#div2').html(data.get_data());
jQuery('#datepicker').datepicker();
return false; //prevent original behavior, in this case folowing the link
}
which gets executed by
<%= Ajax.ActionLink("Edit", "/EditData", new { id = Model.id }, new AjaxOptions { HttpMethod = "GET", UpdateTargetId = "div2" , oncomplete="rebinJQuery" } )%>
i have yet to find a way to get the UpdateTargetId into my rebindJQuery(data) function, so this can be more generic. Nontheless this solves my problem. (and a couple of compairable questions asked here on stackoverflow)
I don't know why that does not work, but you could skip using the Ajax.ActionLink and instead use JQuery on itself to do this task, i.e.:
<div id="div1">
<%= Html.ActionLink("Edit", "/EditData", new { id = Model.id } )%>
</div>
<div2>placeholder for the form</div>
<script type="text/javascript">
$(document).ready(function() {
$("#div1 a").click(function() {
$.get(
$(this).attr("href"),
null,
function (data) {
$("#div2").html(data);
$("#datepicker").datepicker();
});
return false; // to prevent link
});
});
</script>
jQuery live events might be useful.
http://docs.jquery.com/Events/live

Resources