Ok so I am fairly new at AngularJS and just running through a demo, but I am having issues with the routing side of things and can't figure it out. I thought you guys would know instantly that I have done something dumb.
So here goes.
This is my JS file
var WebApplication2 = angular.module('WebApplication2', ['ng-route']);
WebApplication2.controller('LandingPageController', LandingPageController);
WebApplication2.config([
'$routeProvider',
function ($routeProvider) {
$routeProvider
.when('/routeOne', {
templateUrl: 'routesDemo/one'
})
.when('/routeTwo', {
templateUrl: 'routesDemo/two'
})
.when('/routeThree', {
templateUrl: 'routesDemo/three'
});
}
]);
And here is my html code
<html ng-app="WebApplication2" ng-controller="LandingPageController">
<head>
<title ng-bind="models.helloAngular"></title>
</head>
<body>
<h1>{{models.helloAngular}}</h1>
<ul>
<li>Route One</li>
<li>Route Two</li>
<li>Route Three</li>
</ul>
<div ng-view></div>
<script src="~/Scripts/angular.min.js"></script>
<script src="~/Scripts/angular-route.min.js"></script>
#Scripts.Render("~/bundles/AngularBundle")
</body>
</html>
I also have this js controller file
var LandingPageController = function ($scope) {
$scope.models = {
helloAngular: 'I work!'
};
}
I then have a controller with the following actionresults
public class RoutesDemoController : Controller
{
public ActionResult One()
{
return View();
}
public ActionResult Two()
{
return View();
}
public ActionResult Three()
{
return View();
}
}
This is a dependency injection error.
Try this :
var WebApplication2 = angular.module('WebApplication2', ['ngRoute']);
instead of :
var WebApplication2 = angular.module('WebApplication2', ['ng-route']);
Since you are using routing, you should not declare an ng-controller attribute on your view. With routing, each of your views can use a different controller.
Instead of the HTML tag you mentioned above, it should be like this:
<html ng-app="WebApplication2">
You should declare the controller you wish to use in your route. The templateUrl is the path to the HTML file you will use as the template:
$routeProvider.when('/routeOne', {
templateUrl: 'views/route-one.html',
controller: 'LandingPageController'
})
Your controller's code should look something like this:
WebApplication2.controller('LandingPageController', function ($scope) {
$scope.models = {
helloAngular: 'I work!'
};
});
Where you are using <h1>{{models.helloAngular}}</h1> that is not in the scope of your route. The views for your routes will render in <div ng-view></div>.
In your views/route-one.html file you can add <h1>{{models.helloAngular}}</h1>.
You can view this page in the docs for an example implementation of ngRoute.
Angular is used to build single-page applications—your ASP.net's router will have nothing to do with Angular's routes. You just need to declare a single static route that is pointed to your Angular application. Server-side and client-side routing will not work hand-in-hand.
Related
I'm stuck with a CORS issue which works totally fine when I'm using Internet Explorer, but doesn't work with Google Chrome.
I have 2 separate projects in Visual Studio 2013: PROJECT 1 is on port 1044, it's just an empty project containing an HTML page with a Button which uses AngularJS to make a call to ACTION GetCustomer residing inside PROJECT 2 on port 1042. The ACTION then returns JSON data back to PROJECT 1.
The cross domain call works fine when the Button is clicked in IE and returns the data back to the HTML TextBox saying "ShivDataFromServer". But the same doesn't happen in Chrome.
PROJECT 1 on port 1044:
HomePage.html:
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<script src="angular.min.js"></script>
<!--<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>-->
<body>
<script type="text/javascript" src="CustomerViewModel.js"></script>
<div ng-app>
<!-- Below is our VIEW i.e. Display-->
<div id="ViewCustomer" ng-controller="MyController">
Customer Name: <input type="text" id="txtCustomerName" ng-model="Customer.Name" /> <br />
Customer Amount: <input type="text" id="txtCustomerAmount" ng-model="Customer.Amount" /> <br />
<div style="width : 242px; height : 26px; background-color : {{CustomerView.Color}}"></div> <br />
<input type="button" id="btn1" value="Get data from Server" ng-click="GetData()" />
</div>
</div>
</body>
</html>
CustomerViewModel.js:
function MyController($scope, $http) {
$scope.Customer = { "Name": "ShivHardCodedData", "Amount": "1000" };
$scope.CustomerView = { "Color": "" };
//BELOW IS TRANSFORMATION LOGIC! Depending on Amount it will be GREEN or RED meaning "danger".
$scope.$watch("Customer.Amount", function() {
if ($scope.Customer.Amount < 1000) {
$scope.CustomerView.Color = "Green";
} else {
$scope.CustomerView.Color = "Red";
}
}
);
$scope.GetData = function () {
//BELOW WORKS!!
$http({ method: "GET", url: "http://localhost:1042/Customer/GetCustomer" })
.success(function (data, status, headers, config) { $scope.Customer = data; })
.error(function (data, status, headers, config) { });
} //END OF "GetData() function"
}
PROJECT 2 which is an MVC project on port 1042:
CustomerController.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using P10JsonJQuery_httpService.Models;
namespace P10JsonJQuery_httpService.Controllers
{
public class CustomerController : Controller
{
[ImAllowingCors]
public ActionResult GetCustomer()
{
Customer objCustomer=new Customer();
objCustomer.Name = "ShivDataFromServer";
objCustomer.Amount = 1000;
return Json(objCustomer, JsonRequestBehavior.AllowGet);
}
}
//CORS (Cross Origin Resource Sharing). I've made up name "ImAllowingCors" but ending "Attribute" is C# KEYWORD
public class ImAllowingCorsAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
//This means ALLOW any calls from a Cross-domain (i.e. allow calls from different server)
filterContext.RequestContext.HttpContext.Response.AddHeader("Access-Control-Allow-Origin", "*");
base.OnActionExecuting(filterContext);
}
}
}
ie does not count calls on different ports, but the same domain as being cross origin, whereas Chrome does. Thus, you need to set it up for chrome to allow this.
To do it the way you are trying, you will need to make sure the custom filter is registered and I think you will also need to change your attribute to [ImAllowingCorsAttribute]:
See: similar issue
You will need to allow Cors for your app. Something like the following annotation above the action:
[EnableCors(origins: "http://localhost:1042", headers: "*", methods: "*")]
Perhaps put a breakpoint in your attribute, as I doubt it is being hit. This should show you how to enable cors:
enabling Cors
I am developing a ASP.NET MVC 5 application. I am loading a partial view through angular 'ui.router' on a div in a parent page. Routing is working great until I refresh the page - than a partial view is loading on a whole page but I want it to still load as a partial.
This is my code. A parent view, FirstPage.cshtml:
<html ng-app="myApp">
<head>
<base href="/">
<title>First Page</title>
<script src="/Scripts/Angular/angular.js"></script>
<script src="/Scripts/Angular/angular-ui-router.js"></script>
<script src="/Scripts/app.js"></script>
</head>
<body>
<h1>First Page</h1>
<ul id="myMenu">
<li ui-sref="customer">Customer</li>
</ul>
<div ui-view="containerOne"></div>
</body>
</html>
This is my app.js:
var myApp = angular.module("myApp", ['ui.router']);
var proposalConfig = function ($stateProvider, $locationProvider) {
$locationProvider.hashPrefix('!').html5Mode(true);
$stateProvider
.state('customer', {
url: 'Proposal/Customers',
views: {
"containerOne": {
templateUrl: '/Proposal/Customers'
}
}
});
}
proposalConfig.$inject = ['$stateProvider', '$locationProvider'];
myApp.config(proposalConfig);
My RouteConfig.cs:
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "ProposalCustomers",
url: "Proposal/Customers",
defaults: new { controller = "Proposal", action = "Customers" });
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional });
My ProposalController.cs:
public ActionResult Customers()
{
return View();
}
public ActionResult FirstPage()
{
return View();
}
My Views/Proposal/Customers.cshtml:
<h2>Customer list</h2>
I hope my problem is clear.
EDIT:
I changed my RouteConfig.cs following the solution from this article:
http://www.codeproject.com/Articles/806500/Getting-started-with-AngularJS-and-ASP-NET-MVC-P
to have a default route like this:
routes.MapRoute(
name: "Default",
url: "{*url}",
defaults: new { controller = "Proposal", action = "FirstPage" }
When I refresh a page that has a route exactly like my controller/action, it still loads that specific route on a whole page instead as a partial. If I change a .state url to something else page refresh is working. But still if I put manually a controller/action path in the URL, it shows a view on a whole page instead as a partial.
Is there a way to avoid this?
You need to change you ng-view to ui-view as you are using angular $stateProvider not angular $routeProvider
HTML
<body>
<h1>First Page</h1>
<ul id="myMenu">
<li ui-sref="customer">Customer</li>
</ul>
<div ui-view=""></div>
</body>
You stateProvider should be change which is currently using single view and you declared it as like you are using nested-views
Config
var myApp = angular.module("myApp", ['ui.router']);
var proposalConfig = function($stateProvider, $locationProvider, $urlRouterProvider) {
$locationProvider.hashPrefix('!').html5Mode(true);
$urlRouterProvider.otherwise('/Proposal/Customers'); //default redirection if no route matched
$stateProvider.state('customer', {
url: '/Proposal/Customers',
templateUrl: '/Proposal/Customers',
controller: 'someController' //if you want
});
}
proposalConfig.$inject = ['$stateProvider', '$locationProvider'];
myApp.config(proposalConfig);
Hope this could help you, Thanks.
Ok, so what I'm guessing is happening is you mapped a route in your MVC application to serve the same route as your angular application, in this instance, you mapped 'Proposal/Customers' in your MVC router and in your Angular-UI-Router.
So what is happening is that if you're already in your Angular application, the Angular router has control and as you navigate around, it is loading the template as a child template and putting the HTML into the correct location. When you refresh, you're sending that route to the MVC router, though, and it's serving the HTML as the page.
EDIT
Forgot to mention, this is happening because you've got html5 routing turned on.
I am trying to implement angularjs routing with cshtml but i am unable to accomplish it. Can someone look into this ? what i am doing wrong.
index.cshtml:
Route 1
productmodule.js
var app = angular.module("productmodule", ["ngRoute"]);
app.config(['$routeProvider',
function ($routeProvider) {
$routeProvider.
when('/route1', {
templateUrl: '/Product/Details',
controller: 'ProductController'
}).
otherwise({
redirectTo: '/'
});
}]);
Details.cshtml
#{
Layout = "~/Views/Shared/_Layout.cshtml";
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title></title>
</head>
<body>
<div ng-view=""></div>
</body>
</html>
ProductController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace webapp.Controllers
{
public class ProductController : Controller
{
//
// GET: /Product/
public ActionResult Index()
{
return View();
}
public ActionResult Details()
{
return View();
}
}
}
What i wanted to achieve is when I click the Route 1 link from ( index.cshtml) I should be able to navigate to Details.cshtml
When you click your anchor tag it's going to send a request to the server (i.e. the MVC routing) for the resource at "/". Being that you didn't specify a controller or action it will look for the default, which is Home/Index (assuming you haven't changed the default route configuration). Look for code similar to the following in your .NET project.
routes.MapRoute(
"Default",
"{controller}/{action}",
new { controller = "Home", action = "Index" }
);
If you want Product/Index to be the default you can change "Home" to "Product". Alternatively you can change your anchor tag to specify the controller.
Route 1
Either way, MVC will deliver the Index view and at that point Angular's routing will take over, examine the "#/route1" and load the appropriate ng-view.
<script src="../../Scripts/MicrosoftAjax.debug.js" type="text/javascript"></script>
<script type="text/javascript">
function loginOK()
{
var item = document.getElementById('statusLabel');
item.innerHTML = "OK";
document.getElementById('LoadImg').style.visibility = 'hidden';
}
function process()
{
var lab = document.getElementById('statusLabel');
lab.innerHTML = 'Checking...';
lab.style.color = 'Black';
document.getElementById('LoadImg').style.visibility = 'visible';
}
function fail()
{
var lab = document.getElementById('statusLabel');
lab.innerHTML = 'Login is being used';
lab.style.color = 'Red';
document.getElementById('LoadImg').style.visibility = 'hidden';
}
</script>
<div style="width:30%; float:left;">
<label for="Login">Login:</label>
<%= Html.TextBoxFor(model=>model.Login) %>
<%= Html.ValidationMessageFor(model=>model.Login) %>
<img id="LoadImg" alt="" src="../../Content/Images/ajax-loader.gif" style="visibility:hidden;"/>
<br />
<label id="statusLabel" />
<br />
<%=Ajax.ActionLink("CheckLogin","CheckLoginAvailability", "Account",
new AjaxOptions { UpdateTargetId = "statusLabel", OnBegin = "process", OnFailure = "fail", OnSuccess="loginOK"})%>
</div>
and, in the AccountController:
[AcceptVerbs(HttpVerbs.Post)]
public void CheckLoginAvailability(string login)
{
//do some job
}
And, FireBug says that /Account/CheckLoginAvailability is not found. Also, after callback that ActionLink is hidden. Why ?
You are talking about Ajax.BeginForm in your question but this is nowhere to be seen in the markup you provided. There are a couple of issues that I can see with your code:
Your action method doesn't return an ActionResult. Yeah I know, you will say that this is possible, right, but that's against any good practices, conventions and rendering your controllers unit-test friendly.
You are using Microsoft Ajax which will mix markup and javascript which IMHO is bad for multiple reasons: increasing bandwidth which of course leads to decreased performance, incapacity to externalize javascript into separate files in order to cache them by client browsers, having to write things like document.getElementById, innerHTML, style.color, style.visibility, etc... which is not guaranteed to work cross browser.
Here's what I would suggest you to improve this. While this doesn't answer your question, take it as an alternative approach.
As always the first thing to deal with is to define a model which in your case might look something like this:
public class LoginViewModel
{
public string Login { get; set; }
}
Of course you might wish to add other fields such as Password, but this is out of scope for the moment. The next step is to write a controller dealing with this model (in parallel you should be already setting a unit-test for the future controller to prepare the ground):
public class HomeController : Controller
{
public ActionResult Index()
{
// Simply return the Login form
return View(new LoginViewModel());
}
[HttpPost]
public ActionResult Index(LoginViewModel model)
{
// Deal with the actual authentication, etc...
throw new NotImplementedException();
}
[HttpPost]
public ActionResult CheckLoginAvailability(LoginViewModel model)
{
// TODO: query your datasource to determine whether
// model.Login is taken
// For this purpose we will suppose that it is taken
bool isLoginTaken = true;
// return a JSON object containing the result
return Json(new { IsLoginTaken = isLoginTaken });
}
}
The last part is to paint the screen:
<%# Page Language="C#" Inherits="System.Web.Mvc.ViewPage<SomeNs.Models.LoginViewModel>" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Login</title>
<!-- Use a separate CSS to avoid mixing markup with styling -->
<link rel="stylesheet" type="text/css" href="<%: Url.Content("~/content/site.css") %>" />
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js"></script>
<!-- Always use HTML helpers when dealing with Urls -->
<script type="text/javascript" src="<%: Url.Content("~/scripts/login.js") %>"></script>
</head>
<body>
<% using (Html.BeginForm()) { %>
<%: Html.LabelFor(x => x.Login) %>:
<%: Html.TextBoxFor(x => x.Login) %>
<%: Html.ValidationMessageFor(x => x.Login) %>
<br/>
<!-- Always use HTML helpers when dealing with Urls -->
<img id="loadImg" alt="" src="<%: Url.Content("~/content/images/ajax-loader.gif") %>" style="display:none;" />
<br />
<div id="statusLabel"></div>
<br />
<!-- Give this link an id so that we can easily identify it from javascript -->
<%: Html.ActionLink("CheckLogin", "CheckLoginAvailability", "Home", null, new { id = "checkLogin" })%>
<input type="submit" value="Login" />
<% } %>
</body>
</html>
And the last part is to unobtrusively attach our javascript (using jQuery of course) in the login.js file:
// When the DOM is ready
$(function () {
// Attach a click handler to the checkLogin link
$('a#checkLogin').click(function () {
// When this link is clicked send an AJAX POST request
// to the address this link is pointing to
$.ajax({
type: 'POST',
url: this.href,
// Pass as parameter in the POST body the login
// entered by the user
data: { login: $('#Login').val() },
beforeSend: function () {
// show the spinner image before sending any AJAX request
// to inform the user of an ongoing activity
$('#loadImg').show();
},
complete: function () {
// hide the spinner image when the AJAX request completes
// no matter if it succeeded or not
$('#loadImg').hide();
},
success: function (result) {
// if the AJAX request succeeds
// query the IsLoginTaken property
// of the resulting JSON object
if (result.IsLoginTaken) {
// Show the status label with red if the login is taken
$('#statusLabel').html('Login is being used').css('color', 'red');
} else {
// Show the status label in black if the login is not taken
$('#statusLabel').html('OK').css('color', 'black');
}
}
});
return false;
});
});
As #SLaks says actions can return void but, I think the action signature is such that it is required to return an action result and you can return EmptyResult if you don't want to return anything.
see this http://www.asp.net/mvc/tutorials/asp-net-mvc-controller-overview-cs
try changing your AccountController to
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult CheckLoginAvailability(string login)
{
//do some job
return new EmptyResult();
}
I have an ASP.NET MVC app that opens a "Request" view in a new browser window. When the user submits the form, I'd like the window to close. What should my RequestController code look like to close the window after saving the request information? I'm not sure what the controller action should be returning.
You could return a View that has the following javascript (or you could return a JavaScript result) but I prefer the former.
public ActionResult SubmitForm()
{
return View("Close");
}
View for Close:
<body>
<script type="text/javascript">
window.close();
</script>
</body>
Here is a way to do it directly in your Controller but I advise against it
public ActionResult SubmitForm()
{
return JavaScript("window.close();");
}
Like such:
[HttpPost]
public ActionResult MyController(Model model)
{
//do stuff
ViewBag.Processed = true;
return View();
}
The view:
<%if(null!=ViewBag.Processed && (bool)ViewBag.Processed == true){%>
<script>
window.close();
</script>
<%}%>
It sounds like you could return an almost empty View template that simply had some javascript in the header that just ran "window.close()".
You can close by this code:
return Content(#"<script>window.close();</script>", "text/html");
This worked for me:
[HttpGet]
public ActionResult Done()
{
return Content(#"<body>
<script type='text/javascript'>
window.close();
</script>
</body> ");
}
This worked for me to close the window.
Controller:
return PartialView("_LoginSuccessPartial");
View:
<script>
var loginwindow = $("#loginWindow").data("kendoWindow");
loginwindow.close();
</script>
Using this you can close the window like this:
return Content("<script language='javascript'>window.close();</script>");