ASP.NET MVC and AngularJS - asp.net-mvc

I am using AngularJS 1.2.2 (and am totally new to it) and MVC 5. I am trying to get a controller get called but it is not working.
As far as I could tell, the most appropriate 'shell' page is Views/Shared/_Layout.cshtml.
Therefore, in this page I have
<html data-ng-app="myApp">
Latter on in the Views/Shared/_Layout.cshtml I have
<div class="navbar navbar-fixed-top">
<div class="container">
<ul class="nav nav-pills" data-ng-controller="NavbarController">
<li data-ng-class="{'active':getClass('/home')}">Home</li>
<li data-ng-class="{'active':getClass('/albums')}">Albums</li>
</ul>
</div>
</div>
But when I click on either of these two links my getClass method does not get called. The file containing this method is being refernced. Here is the code it contains
app.controller('NavbarController', function ($scope, $location) {
$scope.getClass = function(path) {
if ($location.path().substr(0, path.length) == path) {
return true;
} else {
return false;
}
};
});
Any idea why this is not being called?
EDIT
My structure is such:
I have an app folder in the root.
In the app folder I have an app.js with this code
var app = angular.module('myApp', []);
app.config(function ($routeProvider) {
$routeProvider
.when('/Home',
{
controller: 'HomeController',
templateUrl: '/Views/Home/Index.cshtml'
})
.when('/Album',
{
controller: 'AlbumController',
templateUrl: '/View/Album/Index.cshtml'
})
.otherwise({ redirectTo: '/Home' });
});
(Incidentally I am guessing that by referring to my individual cshtml files like this I will get the correct behavior).
I have a controllers folder with the above NavbarController class.
I also have a services folder with my services in them.
In the _Layout file I have these js files referenced
#Scripts.Render("~/Scripts/angular.js")
#Scripts.Render("~/Scripts/angular-route.js")
#Scripts.Render("~/bundles/jquery")
#Scripts.Render("~/bundles/bootstrap")
#RenderSection("scripts", required: false)
#Scripts.Render("~/app/app.js")
#Scripts.Render("~/app/controllers/navbarController.js")
#Scripts.Render("~/app/controllers/albumController.js")
#Scripts.Render("~/app/services/albumService.js")
There is an error in the console. It is
Error: [$injector:modulerr] Failed to instantiate module myApp due to: [$injector:unpr] Unknown provider: $routeProvider http://errors.angularjs.org/1.2.2/$injector/unpr?p0=%24routeProvider ...

It looks like you missed to include the ngRoute module in your dependency for myApp.
'use strict';
angular.module('myApp', ['ngRoute']).
config(['$routeProvider', function($routeProvider) {
//Your code
}]);

Related

Ajax.ActionLink alternative with mvc core

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>

Rails + Angular : Issue with directive

I have Rails 4.2.4 and Angular 1.4.8.
I am trying define a directive:
index.html:
<div ng-app='myApp' ng-controller='myController'>
<foo bar='bar'></foo>
</div>
app.js:
angular.module('myApp', ['templates']);
angular.module('myApp', ['templates']).directive('foo', function(){
return {
restrict: 'AE',
scope: {
bar: '='
},
templateUrl: 'bar.html'
}
});
angular.module('myApp').controller('myController', function($scope, $http){
$scope.bar = "XMan";
});
bar.html:
<h1> Hi {{ bar }}! </h1>
<ng-include src="'{{bar}}.html'"
XMan.html:
<p>Hello I'm XMan</p>
Here I am expecting my foo directive to render
<h1> Hi X Man! </h1>
<p> Hello I'm XMan </p>
but I am getting
<h1> Hi {{ bar }}! </h1>
<!-- ngInclude: undefined -->
What is wrong with my approach. Please guide me; I am very new to Angular.js.
I got a solution. We cannot bind ng-include src with scope variable.
Instead I used function call to get the source then it works!
That is I changed
<ng-include src="'{{bar}}.html'"
to
<ng-include src="barUrl()"
and added a controller scope function:
$scope.barUrl = function(){
return $scope.bar + '.html'
}

How to config routeProvider with MVC4 and WebAPI2

I have a small app that shows a calendar with some events.
When I click on one of the events I would like to show details of the selection below the calendar.
The events' urls are generated within a loop like this :
'url': '#/concerts/' + data[idx].id
and my config is the following:
var app = angular.module('EventsApp', ['ngRoute']);
app.config(['$routeProvider', '$locationProvider',
function ($routeProvider, $locationProvider) {
$routeProvider.
when('/concerts/:eventId', {
templateUrl: '/Views/Home/ng-view/details.html',
controller: 'EventCtrl'
}).
otherwise({
redirectTo: '/'
});
}]);
My Index.cshtml:
<div id="bodyApp" ng-app="EventsApp">
<div class="mainBody" ng-controller="EventCtrl as event" ng-init="event.getAllConcerts()">
<div class="search">
<input type="search" />
</div>
<div class="calendar">
<div id="calendar">
</div>
</div>
<div class="crudSection" ng-view>
</div>
</div>
At the moment I am just trying to get the details.html to be shown, but It doesn't redirect. The url is updated with the proper one but my view is not refreshed.
Also, How should the routeProvider config to do a webapi call without exposing the '/api/{controller}/{id}' info?
Edit.: I tried with the standard MVC route '/Home/{action} and a partialview and it works, but not a '/Home/{action}/{id}', so I think I'm missing something about how to combine both routings.
I found the solution in the following link:
AngularJS - How to use $routeParams in generating the templateUrl?
app.config(['$routeProvider', '$locationProvider',
function ($routeProvider, $locationProvider) {
$routeProvider.
when('/concerts/:eventId', {
templateUrl: function (params) { return '/Home/Details/' + params.eventId; },
controller: 'EventCtrl'
}).
otherwise({
redirectTo: '/'
});
}]);

Argument 'Controller' is not defined

I am making a little rails/angular project where I am trying to return a list of data from Instagram. Rails is just acting to serve files, and then will be deployed to heroku. The rest of the app is angular.
I keep getting the error
Argument 'MainController' is not a function, got undefined
So I know it has something to do with my controller. I have done a couple angular projects before and have them working, and I repeated my steps with this, but this error is driving me crazy and I cannot seem to figure out why it keeps coming up as my controller being undefined.
Here is my code;
app.js
var app = angular.module("hipstagram", ['ngResource']);
assets/javascripts/angular/services/main.js
app.factory('instagram', function($resource){
return {
fetchPopular: function(callback){
var api = $resource('https://api.instagram.com/v1/media/popular?client_id=:client_id&callback=JSON_CALLBACK',{
client_id: 'MY_CLIENT_ID'
},{
fetch:{method:'JSONP'}
});
api.fetch(function(response){
callback(response.data);
});
}
}
});
assets/javascripts/angular/controllers/main_ctrl.js
function MainController($scope, instagram){
$scope.layout = 'grid';
$scope.pics = [];
instagram.fetchPopular(function(data){
$scope.pics = data;
});
});
index.html.erb
<div ng-app="hipstagram" ng-controller="MainController">
<div class="bar">
</div>
<ul ng-show="layout == 'grid'" class="grid">
<!-- A view with big photos and no text -->
<li ng-repeat="p in pics">
<img ng-src="{{p.images.low_resolution.url}}" />
</li>
</ul>
<ul ng-show="layout == 'list'" class="list">
<!-- A compact view smaller photos and titles -->
<li ng-repeat="p in pics">
<img ng-src="{{p.images.thumbnail.url}}" />
<p>{{p.caption.text}}</p>
</li>
</ul>
</div>
From my troubleshooting, maybe angular is unable to access main controller, or I just named it wrong. If that is the case, any tips on how to get passed this would be great, I am stumped. Thanks for the help.
var app = angular.module("hipstagram", ['ngResource']);
app.controller("MainController", ["$scope", "instagram" ,function($scope, instagram){
$scope.layout = 'grid';
$scope.pics = [];
instagram.fetchPopular(function(data){
$scope.pics = data;
});
}]);
Instead of defining a global MainController function, do something like this:
app.controller('MainController', function ($scope, instagram){
$scope.layout = 'grid';
$scope.pics = [];
instagram.fetchPopular(function(data){
$scope.pics = data;
});
});
That registers the controller with the angular app so it can be used and dependency injected.

Selector [ng\:model="query"] did not match any elements

Why am I getting this error: Selector [ng\:model="query"] did not match any elements
I've read through this: AngularJS: End to End Testing Issue , but that link doesn't really apply in a .net env:
IDE: Visual Studio 2012
Project type: ASP.NET MVC4
File strucure:
Running CI tests through karma start e2e.conf.js in node.js command prompt
My karma conf:
basePath = '../../../';
files = [
ANGULAR_SCENARIO,
ANGULAR_SCENARIO_ADAPTER,
'angular/app/*.js',
'angular/Tests/e2e/*.js'
];
reporters = ['progress'];
port = 10876;
runnerPort = 10100;
colors = true;
logLevel = LOG_ERROR;
autoWatch = true;
browsers = ['Firefox'];
captureTimeout = 60000;
singleRun = false;
proxies = {
'/': 'http://localhost:60607/'
};
My e2e test:
describe('E2E: AMS', function () {
describe('Settings Users', function () {
beforeEach(function () {
browser().navigateTo('/#/settings/users');
});
it('filters the users list as the user types into the search box', function () {
expect(repeater('.users li').count()).toBe(2);
input('query').enter('abc');
expect(repeater('.users li').count()).toBe(1);
input('query').enter('efg');
expect(repeater('.users li').count()).toBe(1);
input('query').enter('ijk');
expect(repeater('.users li').count()).toBe(0);
});
});
});
My View:
<div data-ng-view="">
Add User: <br />
<input type="text" /> <button>Submit</button><br />
Search:
<input data-ng-model="query" type="text" />
Users <br />
<ul class="users">
<li data-ng-repeat="user in users | filter:query">
{{user.name}}
</li>
</ul>
</div>
and for grins, my route
angular.module('AMS', []).
config(['$routeProvider', function ($routeProvider) {
$routeProvider.
when('/login', { templateUrl: '/AccessControl/Login/', controller: settingsController }).
when('/dashboard', { templateUrl: '/Dashboard/Dashboard', controller: dashboardController }).
when('/settings', { templateUrl: '/Settings/Settings', controller: settingsController }).
when('/settings/users', { templateUrl: '/Settings/Users', controller: settingsController }).
otherwise({ redirectTo: '/dashboard' });
}]);
It looks like the angular e2e testing api is looking for "ng-model" in your view instead of the "data-ng-model" that you're using.
My understanding is that both are valid, but give that a try to see if that's the problem.
Not an answer, so I wont mark it as such, this is more of a work around.
I've decided to go with chutzpah in order to run the jasmine unit tests, and specflow.xunit for "e2e" testing. It works well in a .net environment and integration into teamcity seems straight forward.

Resources