JadedEric
JadedEric

Reputation: 2103

AngularJS with ASP.net MVC

Okay, this seems to be getting the better of me.

I've been reading blog after blog, where guys explain how to get this going, but after countless hours of removing, adding, changing, debugging, pulling-at-hair, copious amounts of coffee and no luck in getting this to work, I'm turning here for some professional insight.

As the title suggest, I'm trying to get angular to work within an MVC 5 with Razor views, but I'm experiencing some very weird behavior.

Firstly, angular does not kick in on page load. I have to refresh the landing page at least 5 - 10 times before it kicks in.

When it eventually works, Chrome goes into overburn, consuming nearly all of the available resources before crashing. Console in developer tools comes up with a message "Warning: Trying to load angular multiple times".

The error is very descriptive, so I assume that I'm loading the _layout.cshtml page twice, when the client-side routing eventually kicks in, but each Razor view contains the @{ Layout = null } call, as it has been explained on so many blogs.

I've tried everything I could possibly read up on, and I cannot get passed this issue.

Following, my code:

ASP.Net Razor View

1.) _Layout.cshtml

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" data-ng-app="webStudio" data-ng-controller="StudioController">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
    <title data-ng-bind="Page.title">...</title>
    <base href="/" />
    @Styles.Render("~/Content/css")
    @Scripts.Render("~/bundles/modernizr")
</head>
<body>

    <div class="page {{css}}" data-ng-view="">
        @RenderBody()
    </div>

    <div class="__javascript-webstudio">
        @Scripts.Render("~/bundles/jquery")
        @Scripts.Render("~/bundles/ng")
        @Scripts.Render("~/bundles/yeti")
        @Scripts.Render("~/bundles/ngapp")
        @Scripts.Render("~/bundles/ngfactory")
        @Scripts.Render("~/bundles/ngservice")
        @Scripts.Render("~/bundles/ngcontroller")
        @RenderSection("scripts", required: false)
    </div>
</body>
</html>

I assumed that, because the _layout.cshtml is used for the index page (Home), it is where the angular instantiate should occur. I've seen blogs where they skip the _layout.cshtml all together, but to keep it consistent, I want this to be my starting point. I have tried to not use Layout in Home/Index, and call all my scripts in there, but it does the exact same thing, couple of refreshes, and Warning.

2.) Home/Index.cshtml

@*{
    Layout = null
}*@

<a href="@Url.Action("Index", "Gallery")">Go there</a>

Layout = null has been commented out, because this causes _layout.cshtml to not even load my JS files, weird behaviour :/

3.) Gallery/Index.cshtml

@{
    Layout = null;
}

Date: @DateTime.Now

This is just a page to test the route.

4.) Web.config

<rewrite>
    <rules>
        <rule name="AngularJS" stopProcessing="true">
            <match url=".*" />
            <conditions logicalGrouping="MatchAll">
                <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
                <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
                <add input="{REQUEST_URI}" pattern="^/(api)" negate="true" />
            </conditions>
            <action type="Rewrite" url="/" />
        </rule>
    </rules>
</rewrite>

The rewrite rule is important to ensure that the routing is done appropriately. I am using HTMLMode for angular, so this step is vital.

5.) app.js

var webStudio = angular.module("webStudio", ["ngRoute"]);

webStudio.config(function ($routeProvider, $locationProvider)
{
    $routeProvider
        .when("", {
            templateUrl: "/Home",
            controller: "HomeController"
        })
        .when("/", {
            templateUrl: "/Home",
            controller: "HomeController"
        })
        .when("/Home", {
            templateUrl: "/Home",
            controller: "HomeController"
        })
        .otherwise({ redirectTo: "/Error/NotFound" });

    $locationProvider.html5Mode(true);
});

There really is nothing weird about this, it creates the angular app reference, attaches the routing to the module and form there I configure the routing behavior.

From what I've read and understand, it makes sense to call the controller/action block as you would do normally in ASP.Net MVC.

The default routing for MVC I did not touch, as it's irrelevant in this scope.

I'm at a loss as to why this behavior is occurring and don't know where to start investigating. I've read blogs on the refresh issue and have tried what has been stated, but in this scenario, I just cannot get it to work.

Update

6.) BundleConfig.cs

public static void RegisterBundles(BundleCollection bundles)
{
    bundles.Add(new ScriptBundle("~/bundles/jquery")
        .Include("~/Scripts/vendor/jquery/jquery-{version}.js")
        .Include("~/Scripts/vendor/jquery/jquery.validate.js"));

    bundles.Add(new ScriptBundle("~/bundles/ng")
        .Include("~/Scripts/vendor/angularjs/angular.js")
        .Include("~/Scripts/vendor/angularjs/angular-route.js"));

    bundles.Add(new ScriptBundle("~/bundles/yeti")
        .Include("~/Scripts/vendor/ngfoundation/mm-foundation-0.6.0.js"));

    bundles.Add(new ScriptBundle("~/bundles/ngapp")
        .Include("~/Scripts/app.js"));

    bundles.Add(new ScriptBundle("~/bundles/ngfactory")
        .Include("~/Scripts/factories/studio.js"));

    bundles.Add(new ScriptBundle("~/bundles/ngservice")
        .Include("~/Scripts/services/studio.js"));

    bundles.Add(new ScriptBundle("~/bundles/ngcontroller")
        .Include("~/Scripts/controllers/studio.js"));

    bundles.Add(new ScriptBundle("~/bundles/modernizr")
        .Include("~/Scripts/vendor/modernizr-*"));

    bundles.Add(new StyleBundle("~/Content/css")
        .Include("~/Content/normalize.css")
        .Include("~/Content/foundation.css")
        .Include("~/Content/site.css"));
}

Upvotes: 2

Views: 4779

Answers (1)

Sourav Mondal
Sourav Mondal

Reputation: 435

I think this will help you AngularJS with ASP.net MVC

For setup angularJS in asp.net mvc application follow below steps

  1. Load required js first. you can use asp.net mvc bundles

    bundles.Add(new ScriptBundle("~/bundles/angular").Include( "~/Scripts/angular.js", "~/Scripts/angular-route.js"));

  2. Modify _Layout.cshtml

    <!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>
            @* Here  ng-app="MyApp" is used for auto-bootstrap an AngularJS application. here ng-app="MyApp" means <body> element is the owner 
            of AngularJS application*@
        <body ng-app="MyApp">
            @RenderBody()
    
            @Scripts.Render("~/bundles/jquery")
            @* Add Angular Library Here *@
            @Scripts.Render("~/bundles/angular")
            <script src="~/Scripts/MyApp.js"></script>
            @RenderSection("scripts", required: false)
        </body>
        </html>
    
  3. Add a new js File for add angularjs module, controller etc. here 'ngRoute' is optional (not required if you don't want to use angularjs routing)

       (function () {
            //Create a Module 
            var app = angular.module('MyApp', ['ngRoute']);  // Will use ['ng-Route'] when we will implement routing
            //Create a Controller
            app.controller('HomeController', function ($scope) {  // here $scope is used for share data between view and controller
                $scope.Message = "Yahoooo! we have successfully done our first part.";
            });
        })();
    
  4. Add new action into your controller for Get View.

    public ActionResult Index()
                {
                    return View();
                }
    
  5. Add view for the Action & design.

       @{
            ViewBag.Title = "Index";
        }
    
        <h2>Index</h2>
        <div ng-controller="HomeController">
            {{Message}}
        </div>
    
  6. Run application.

Upvotes: 2

Related Questions