How to use MVC Model binding with an Angular ng-bind attribute - c#

In my view I have a tag with an ng-bind attribute that is showing the correct boolean value:
<span id="ShowFlag" name="ShowFlag" ng-bind="session.view.showFlag"></span>
When the form is posted on the server side I would like to bind this to a property on the relevant model.
public bool ShowFlag { get; set; }
However, this is always returning false, whereas the value shown in Span tag is showing correctly as true on the page. Is there something obvious I'm missing here?

I think you're something you're missing about how AngularJs binding works. if you want to get a value from the server into an angular model you can use Razor to get that data into JavaScript (the best place is in your Angular controller.)
Here is a quick sample I put together.
This is code from the MVC Controller. In this example we are using Model data and ViewBag data.
public ActionResult Index()
{
dynamic model = new ExpandoObject();
model.ShowFlag = "True";
ViewBag.ShowFlag = "ViewBag True";
return View(model);
}
This is what the view looks like including reference so Angular, JQuery and the code for the AngularJs app and controller:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Demo</title>
</head>
<body>
<div>
<h2>Sample For Stack Overflow</h2>
<div ng-app="glennapp">
<div ng-controller="testController">
<input type="text" ng-model="showFlag" />
<input type="text" ng-model="showFlag2" />
<div>
<span ng-bind="showFlag" ></span>
<span ng-bind="showFlag2" ></span>
</div>
</div>
</div>
</div>
<script type="text/javascript" src="//code.jquery.com/jquery-1.11.3.min.js"></script>
<script type="text/javascript" src="//code.angularjs.org/1.4.8/angular.min.js"></script>
<script type="text/javascript">
var mainApp = angular.module('glennapp', ['glennControllers']);
var glennControllers = angular.module('glennControllers', []);
glennControllers.controller('testController', ['$scope', function ($scope) {
$scope.showFlag = '#ViewBag.ShowFlag';
$scope.showFlag2 = '#Model.ShowFlag';
}]);
</script>
</body>
</html>
Another option would be to create an MVC action that returns JsonResult and then write some JavaScript to make an Ajax call and retrieve the data.

When posting a form only input and select tag values are passed to the server
in you case ShowFlag is a span, so you need to make it an input:
<input type="checkbox" id="ShowFlag" name="ShowFlag" ng-bind="session.view.showFlag"/>
If you are posting to server with ajax, make sure that you serialize your model properly:
for example for the following action:
public ActionResult (FlagsConatiner container)
{
//
}
public class FlagsConatiner
{
public bool ShowFlag { get; set; }
}
Serialized model should look like this:
{
"ShowFlag":"true"
}

As pointed out above, you must use an input for the binding to be successful. I used the following which is now working:
<input type="hidden" id="ShowFlag" name="ShowFlag" ng-value="session.view.showFlag">

Related

How to redirect to another ".cshtml" file in asp.net core?

I'm new to asp.net core. I have created some razor pages and I want to redirect them by button click events. I have a login page, and I want to redirect to another cshtml page when user clicks on the submit button. This is my Login.cshtml code:
#page "/"
#model Food_Reservation.Pages.Login.LoginModel
#{
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>صفحه ورود</title>
<link href="~/css/Shared.css" , rel="stylesheet" type="text/css" runat="server" />
<link href="~/css/LoginCSS/Login.css" , rel="stylesheet" type="text/css" runat="server" />
<script src="~/js/Login/Login.js" type="text/javascript" language="javascript" defer></script>
</head>
<body>
<main>
<form id="login-form" action="" method="post">
<section id="userpass">
<ul>
<li>
<label for="userId">username</label>
<input type="text"
name="user-name"
id="userId"
class="ltr"
required />
</li>
<li>
<label for="password">password</label>
<input type="password"
name="pass"
id="password"
class="ltr"
required />
</li>
</ul>
</section>
<section>
<button id="enter-btn" runat="server" onclick="RedirectPage();">Enter</button>
</section>
</form>
</main>
</body>
</html>
}
I have write some code in Login.cshtml.cs file, but not working:
namespace Food_Reservation.Pages.Login
{
public class LoginModel : PageModel
{
public void RedirectPage()
{
Response.Redirect("~/Food/Index.cshtml");
}
}
}
Also I write in javascript file:
void RedirectPage()
{
return Redirect("~/Food/Index.cshtml");
}
Still not working...
What is wrong here?
I have tried to redirect to another ".cshtml" file, on click event of a button, but it didn't work.
I have tried to redirect from one ".cshtml" file to the other one, on a button click event, but it doesn't work. It again reloads the current page.
In PageModel class, It will include an OnGet() method by default to load the page, Similar to HttpGet method in mvc. When you want to submit form in Post method, You need to use OnPost()/OnPostAsync() method to accept this http post request in PageModel class. In your Code, I think you wanna submit the form then do some actions finally redirect to another Razor page, So you can use RedirectToPage("xx") method.
public void OnGet()
{
}
public IActionResult OnPost()
{
//other code
//redirect to index.cshtml
return RedirectToPage("pagename");
}
There are also some overloads.
More details you can refer to this Microsoft Docs.
using Microsoft.AspNetCore.Mvc;
namespace MyWebApp.Controllers
{
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
public IActionResult About()
{
return Redirect("/about");
}
}
}
<button id="enter-btn" runat="server" asp-page="~/Food/Index.cshtml">Enter</button>

Prevent MVC JsonResult redirect to new page with Json [duplicate]

Why Ajax.BeginForm redirect my page to new empty page after submission?
My controller code is:
[HttpPost]
public void ProductCommentAdd(int productId, string text)
{
//Do something
}
Mvc ajax call in view is:
#using (Ajax.BeginForm("ProductCommentAdd", "Shop", new AjaxOptions() { HttpMethod = "POST"}))
{
<input type="hidden" value="#Model.ProductId" name="ProductId"/>
<textarea name="text"></textarea>
<input type="submit" value="Submit"
}
When I click submit button my page redirect to new empty page. How I can fix it?
you need to include the following script to your page:
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")"
type="text/javascript"></script>
As #Mat J and #user3206982 suggests: you probably need the unobtrusive.ajax.js file.
You can download the package from nuget and
add it in the bundles config:
bundles.Add(new ScriptBundle("~/bundles/jqueryunobtrusive").Include(
"~/Scripts/jquery.unobtrusive*"));
and in the html:
#Scripts.Render("~/bundles/jqueryunobtrusive")
Source: https://dotnetthoughts.net/mvc5-ajax-form-is-not-updating-div-but-replacing-the-whole-page-instead/

MVC how to post a single value in the _layout file to controller?

This is the _layout file:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
...
<div class="container body-content">
#RenderBody()
<input type="submit" name="pageSize" value="12" />
<hr />
<footer>
<p>© #DateTime.Now.Year - Test co. ltd.</p>
</footer>
</div>
#Scripts.Render("~/bundles/jquery")
#Scripts.Render("~/bundles/bootstrap")
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
#RenderSection("scripts", required: false)
</body>
</html>
And as you see I added an input called pageSize, and what I need to do is when the user clicks the pageSize button, the "12" will be post to the controller, and I am thinking of posting the "12" through querystring (i.e. when the user clicks the button, the url will be changed to www.myurl.com/propertyReport?pageSize=12)
Currently, the button is displayed in the page, but there's no action when the button is clicked. How I can add an action to the button so the "12" will be passed to the controller?
Please note that I don't want to redirect to another page after clicking the button, I would like to post the 12 back to the controller and keep the existing url (i.e. something like this: www.myurl.com/propertyReport?userID=123&from=20150201&pageSize=12)
Update:
As per Stephen Muecke and Ivan's comment recommend, I added an Ajax.beginForm to wrap around the button:
#using (Ajax.BeginForm("Options", "Shared", new AjaxOptions() { HttpMethod = "POST" }))
{
<input type="submit" name="pageSize" value="12" />
}
How the options method inside the shared controller is hit. So now I am able to get the "12" from query string, but now the problem is: what should I return in the ActionResult so I can stay on the same page?
SharedController:
public ActionResult Options(string pageSize)
{
//Do stuff with the pageSize
return //what should I return here?
}
I changed to jquery post and now it solves the problem
<script type="text/javascript">
function changePageSize(s) {
$.post('/Shared/Options?pageSize=' + s, function (data, status) {
window.location.reload();
});
}
</script>
Added this in the _Layout file and call this function whenever the button is clicked.

knockoutjs mvc button action change per viewModel

I am currently using knockoutjs with one of my MVC applications.
The Layout template looks like this:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>#ViewBag.Title</title>
#Styles.Render("~/Content/css")
#Scripts.Render("~/bundles/modernizr")
</head>
<body>
<div class="container-fluid head-content">
<div class="row">
<div class="col-xs-6">
<img class="img-responsive" src="~/Images/logo.jpg" />
</div>
<div class="col-xs-3">
<a class="block" href="#" style="display: none" data-bind="visible: showBack, click: goBack">
<div class="block-text">
<h4>Back</h4>
</div>
</a>
</div>
<div class="col-xs-3">
<a class="block" href="#" style="display: none" data-bind="visible: showHome, click: navigateToHome">
<div class="block-text">
<h4>Home</h4>
</div>
</a>
</div>
</div>
</div>
<div class="container-fluid body-content">
#RenderBody()
</div>
#Scripts.Render("~/bundles/jquery")
#Scripts.Render("~/bundles/bootstrap")
#RenderSection("scripts", required: false)
</body>
</html>
and my Index partial looks like this:
#Html.Partial("_Login")
#Html.Partial("_Home")
#Html.Partial("_CutLengths")
#Html.Partial("_MoveStock")
#section scripts {
#Scripts.Render("~/bundles/knockout")
#Scripts.Render("~/bundles/app")
}
My problem is that depending on which page I am on, I would like to use the back button to go to another page. For example, if I am on cutLengths I would want the back button to take me home.
My app.viewmodel.js has a method which looks like this:
// Other operations
self.addViewModel = function (options) {
var viewItem = {},
navigator;
// Add view to AppViewModel.Views enum (for example, app.Views.Home).
self.Views[options.name] = viewItem;
// Add binding member to AppViewModel (for example, app.home);
self[options.bindingMemberName] = ko.computed(function () {
if (self.view() !== viewItem) {
return null;
}
return new options.factory(self, dataModel);
});
if (typeof (options.navigatorFactory) !== "undefined") {
navigator = options.navigatorFactory(self, dataModel);
} else {
navigator = function () {
self.view(viewItem);
};
}
// Add navigation member to AppViewModel (for example, app.NavigateToHome());
self["navigateTo" + options.name] = navigator;
};
What I would like to do is pass a string from the ViewModel I am currently viewing which when the back button is pressed will know to direct me to the right ViewModel.
Is it possible to do this?
I hope I have explained it well, if I haven't please ask and I will try harder :D
You can use either ViewData or ViewBag for passing data from the controller to view. So one option is to just add a few dynamic properties to ViewBag for current view model and prior view model.
ViewData is a dictionary of objects that are stored and retrieved using strings as keys.
ViewBag uses the dynamic feature that was introduced into C# 4.It allows an object to have properties dynamically added to it. I would use this for passing your view model state around.
Neither provide compile time checking, which is the beauty of them, you can add anything you want. With that said it’s always good practice to use strongly typed view models over ViewBag and ViewData.
If you'd rather put something in your view model instead of adding properties to ViewBag, than just add another property in each view model called PreviousViewModel and populate it any time you use the model.
Examples using ViewBag or ViewData
ViewData["LastViewModel"] = "CutLengths";
ViewBag.LastViewModel = "CutLengths";
Access your ViewBag in the Views is no problem, they have global scope. ViewBag is like a global variable that you can attach anything to-- so I'd use them judiciously-- maybe some type of a singleton application manager would be a better design.
Hope this helps
I have solved this now. First I changed my HTML
<div class="col-xs-3">
<a class="block btn-back" href="#" data-bind="visible: showBack, click: goBack"></a>
</div>
<div class="col-xs-3">
<a class="block btn-home" href="#/home" data-bind="visible: showHome"></a>
</div>
Then I edited my app.viewmodel.js file and added these
// Data
self.back = ko.observable(null);
// UI state
self.showBack = ko.observable(true);
self.showHome = ko.observable(true);
self.goBack = function () {
if (self.back()) {
window.location.href = self.back();
} else {
window.history.back();
}
self.back(null); // Reset
};
self.setBackUrl = function (url) {
self.back(url);
}
Then on my addViewModel navigate function, I added this:
if (typeof (options.navigatorFactory) !== "undefined") {
navigator = options.navigatorFactory(self, dataModel);
} else {
navigator = function () { // This is our navigator function which sets the current view
self.showBack(true);
self.showHome(true);
self.error(null); // Reset errors
self.view(viewItem);
};
}
And then in my other view models, I just make a call to setBackUrl like this:
app.setBackUrl("#/cut-lengths");
And if I want to hide my buttons, that is easy too. I just create a navigatorFactory on the viewModel like this:
app.addViewModel({
name: "Home",
bindingMemberName: "home",
factory: HomeViewModel,
navigatorFactory: function (app) {
return function () {
app.showBack(false);
app.showHome(false);
app.error(null);
app.view(app.Views.Home);
}
}
});

ASP.NET MVC3 - Bug using Javascript

I'm trying to use Ajax.BeginForm() to POST A Json result from my controller (I'm using MVC3). When the Json result is called it should be sent to a javascript function and extract the object using
var myObject = content.get_response().get_object();
However it just throws a "Microsoft JScript runtime error: Object doesn't support this property or method" when trying to invoke the Ajax POST.
My code:
Controller:
[HttpPost]
public ActionResult Index(string message)
{
return Json(new { Success = true, Message = message });
}
View:
<!DOCTYPE html>
<html>
<head>
<script src="#Url.Content("~/Scripts/jquery-1.4.4.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/MicrosoftAjax.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/MicrosoftMvcAjax.js")" type="text/javascript"></script>
<script type="text/javascript">
function JsonAdd_OnComplete(mycontext) {
var myObject = mycontext.get_response().get_object();
alert(mycontext.Message);
}
</script>
</head>
<body>
<div>
#using(Ajax.BeginForm("Index", "Home", new AjaxOptions() { HttpMethod = "POST", OnComplete = "JsonAdd_OnComplete" }))
{
#Html.TextBox("message")
<input type="submit" value="SUBMIT" />
}
</div>
</body>
</html>
The strange thing is that the exactly same code works in MVC2 - Is this a bug, or have I forgot something?
Thanks in advance.
AFAIK in ASP.NET MVC 3 RC MS AJAX has been deprecated in favor of jQuery which is used by all Ajax.* helper methods. Javascript has become unobtrusive as well. This means that you no longer have to call .get_response().get_object() but simply:
function JsonAdd_OnComplete(myObject) {
// here myObject is already the JSON object
// as returned by the controller action
alert(myObject.Message);
}
Have you looked at the OnSuccess method? When that method is called the object passed to it is a JSON object based on a JSONResult so you could do this...
<script type="text/javascript">
function JsonAdd_OnSuccess(mycontext) {
alert(mycontext.Message);
}
</script>
#using(Ajax.BeginForm("Index", "Home",
new AjaxOptions() {
HttpMethod = "POST",
OnSuccess = "JsonAdd_OnSuccess" })) {
#Html.TextBox("message")
<input type="submit" value="SUBMIT" />
}
In addition the OnComplete method returns an object that contains the following properties
context.responseText
context.responseBody
context.responseXml
You'll have to eval() the responseText to convert the text to JSON though.
if you are using the method provided by BuildStarted, here's how to structure the eval:
var json = eval("(" + context.responseText + ")");

Categories