ASP.NET MVC Partial View in an AngularJS directive - asp.net-mvc

I'm currently working on an ASP.NET MVC project to which some AngularJS was added - including some AngularJS directives.
I need to add to an AngularJS directive a MVC partial view. Obviously,
#Html.Partial("_PartialView", {{name}})
doesn't work.
So far all my searches online provided no help.
Any idea how I could render a partial view inside an Angular directive?
Thanks!

Angular exists strictly on the client side whereas MVC views exist on the server side. These two cannot interact directly. However, you could create an endpoint in which your partial view is returned as HTML. Angular could call this endpoint, retrieve the HTML, and then include it inside a directive.
Something like this:
app.directive("specialView", function($http) {
return {
link: function(scope, element) {
$http.get("/views/partials/special-view") // immediately call to retrieve partial
.success(function(data) {
element.html(data); // replace insides of this element with response
});
}
};
});

app.directive("myDirective", ['', function () {
return {
restrict: 'A',
scope: {
foo: '='
},
templateUrl: '/home/_myDirectivePartialView',
}]
} }]);
Just need to use templareURL and specify the route to get the partial view.

Related

Binding from mvc model to angular2 component (Components in mvc project)

I could use some help, figure out how to pass model data from mvc application to a angular2 component running inside mvc.
Lets say I have a cs.html file that has an component
<my-app></my-app>
This will load the angular2 component. I need to generate some binding to keep mvc models intact with my angular2 models.
First of all, I'm trying to pass a model to the component via the Input property.
CSHTML file:
In the top of my cshtml file, I have:
#model MainModel
<script>
var model = #Html.Json(Model.Form.PassengerModel);
</script>
I want to pass this model to my angular2 component.
What I have tried are:
<my-app passengerModel="model"></my-app>
Angular2 component:
import { Component, Input } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './Content/Scripts/angular2components/app.component.html',
})
export class AppComponent {
#Input() passengerModel: PassengerModel;
constructor() {
console.log("Model loaded?: " + this.passengerModel);
}
}
export class PassengerModel {
constructor(
public Adults: number,
public Children: number
) { }
}
The problem is that the model is undefined always. Is there any way to pass a model in to the component?
The problem you have outlined above is that your binding is not correct for the context you are attempting to use it in.
<my-app passengerModel="model"></my-app>
The above line is telling Angular to bind passengerModel inside your my-app component to a property on the host component named model. This means the page (component) which hosts your my-app component should be a component with a property named model. You have created a global variable which is not in the scope of your host component. Angular2 specifically isolates the scope of each component so that you do not accidentally introduce unwanted side effects.
Save yourself some pain an anguish and embrace Angular fully. Ditching your MVC Page Controllers and moving to WebApi service calls will yield better results and save you the need to translate the model manually among other issues you will run into going down the mixed route.
Considerations:
#Html.Json will ultimately cause your data to be exposed directly in your script tag. This could be a security risk if the data is sensitive and if you start using MVC Model bindings in the page alongside Angular bindings they will fight each other.
Basically these approaches are diametrically opposed as ASP.NET MVC is a server side approach and Angular is a client side approach. Mixing them in the same application will always be awkward at best.
WebApi gives you the JSON serialization more or less for free. Your MVC model is automatically serialized to JSON by the framework when returning an HttpAction. If you are trying to avoid converting your ASP.NET MVC views to Angular Components then I understand. You may not have a choice but if you do I would steer clear of mixing these two.
[HttpGet]
[AcceptVerbs["GET"]
[Route("passengers/{id:int}")]
public IHttpActionResult GetPassenger(int id)
{
// For illistration...
try
{
var passengerModel = PassengerService.LoadPassenger(id);
return Ok(passenger);
}
catch(Exception e)
{
return InternalServerError(e);
}
}
https://angular.io/docs/ts/latest/tutorial/toh-pt6.html
// I would normally put this in the environemnt class..
private passengerUrl = 'api/passengers'; // URL to web api
constructor(private http: Http) { }
getPassenger(id: number): Promise<Passenger> {
return this.http.get(`this.passengerUrl/${id}`)
.toPromise()
.then(response => response.json() as Passenger)
.catch(this.handleError);
}
private handleError(error: any): Promise<any> {
console.error('An error occurred', error); // for demo purposes only
return Promise.reject(error.message || error);
}

Angular 2: how to get params from URL without using Routing?

I'm trying to get the URL parameters into my component to search using an externsre service. I read I can use angular2 routes and get params using RouteParams but I don't think that's what I need because the first page of my angular application is a Razor view.
I'll try to explain better.
My URL looks like:
http://dev.local/listing/?city=melbourne
I'm loading my root component in a razor view with something like:
<house-list city = "#Request.Params["city"]"></house-list>
then on my root component I have:
export class AppComponent implements OnInit {
constructor(private _cityService: CityService) { }
response: any;
#Input() city: any;
ngOnInit() {
this.getHouses();
}
getHouses() {
this.__cityService.getHousesByCity(this.city)
.subscribe(data => this.response = data);
}
}
So I was expecting that the 'city' in my component gets the string passed from the razor view but it's undefined.
What am I doing wrong? Is there a way of getting the params without using the routing? If not, what is the right way of using razor?
Angular doesn't provide special support for this. You can use How can I get query string values in JavaScript?
From what I have seen Location is planned to be reworked to provide support for get query params even without using the router.

Ajax request not fired form partial page when the main page also has an ajax request

In my _layout.cshtml I have a partial view that includes js trough requirejs.
<script data-main="/Scripts/Search" src="/Scripts/require.js"></script>
In this js file I use the following to populate a knockout vm.
$.getJSON("/Search/Index", function (data) {
self.availableCities(data.AvailableCities);
});
This works well on all pages except when my main view also has an ajax request.
<script data-main="/Scripts/Index" src="/Scripts/require.js"></script>
$.getJSON("/Property/All", function (data) {
self.properties(data);
});
Here is my require config, it is the same for the partial and the main view.
require.config({
baseUrl: "/Scripts",
paths: {
"text": "text",
"knockout": "knockout-3.3.0",
"jquery": "jquery-2.1.3.min"
},
shim: {
"jquery": { exports: "$" }
}
});
When the main page has an ajax request only this request is fired, I am not sure how to fix this. It looks like a configuration issue, tested it in both Firefox an Chrome so it does not appear to be browser specific.
It turns out having multiple <script data-main="/Scripts/Search" src="/Scripts/require.js"></script> tags in one page isn't such a bright idea.
I figured it out after some more research,
this question has a good solution if you run into a similar problem.
Basically you need one 'main.js' file and add the other page components via the require logic.
Edit:
Doing this may result in the following knockout error:
Error: You cannot apply bindings multiple times to the same element.
To fix this I have used the following binding handler:
ko.bindingHandlers.stopBinding = {
init: function () {
return { controlsDescendantBindings: true };
}
};
To enable this binding handler on containerless elements use the following:
ko.virtualElements.allowedBindings.stopBinding = true;
Apply the following binding around the partial view. To prevent the main-page from binding to the elements in the partial.
<!-- ko stopBinding: true-->
<!-- /ko -->
Finally use ko.applyBinings on the partialview like this:
ko.applyBindings(new partialViewModel(), document.getElementById("your-partial"));

AngularJs routing with Asp.Net Mvc

I'm trying to build a SPA with Asp.Net MVC. for this I'm using angularJs routing .
This is my project hierarchy.
My Layout.cshtl code
<html lang="en" ng-app="ProjectTrackingModule">
<head>
<script src="~/Scripts/jquery-2.1.0.min.js"></script>
<script src="~/Scripts/angular.min.js"></script>
<script src="~/Scripts/angular-route.min.js"></script>
<script src="~/Scripts/app.js"></script>
</head>
<body>
Home
Projects
<div ng‐view style="margin‐left: 10%; margin‐right: 10%;">
</div>
//... footer
</body>
</html>
My app.Js code is as follow:
var app = angular.module('ProjectTrackingModule', ['ngRoute', 'ui.bootstrap']);
app.config(function ($routeProvider) {
$routeProvider
.when("/Home", {
templateUrl: "/Views/Home/Home.cshtml",
controller:"HomeController"
})
.when("/Projects", {
templateUrl: "/Views/ProjectManagement/ProjectDetails.cshtml",
controller: "ProjectsController"
})
.otherwise({redirectTo:"/Home"})
});
I want to load my Home.Cshtml partial view inside ng-view. But when I run my application, It only showing Home partial view.
also when I click on Project, then it should render ProjectDetails.cshtml inside ng-view.
code inside ProjectDetails.cshtml
<div ng-controller="ProjectsController">
<h2>ProjectDetails</h2>
</div>
I think you have some misonceptions about Angularjs routing concepts.
MVC Routing :
ASP.NET routing enables you to use URLs that do not have to map to specific files in a Web site. Because the URL does not have to map to a file, you can use URLs that are descriptive of the user's action and therefore are more easily understood by users.
Angular Routing :
Angular.js routing using MVC framework does not use MVC routing.
Comparable parts are:
Model ===== ng-module
controller ===== ng-controller
view ===== ng-view
So you can't call the MVC Controller in your angularjs route config. Does this make sense?
Also please think about some of the differences between cshtml and html.
Angular routing and MVC routing are totally different because:
Angular routing is use client side
MVC routing is used server side
The above texts are for your understanding only.
I think this discussion will help you :
How to use ASP.NET MVC and AngularJS routing?
Update :
The href is wrong in Anchor tag.
Its should be href="#/Home", not href="#Home"
So please change your code
Home
Projects
Lets understand the routing in angular first. lets say your url says
www.example.com/Home/Index -- this points to your MVC HomeController and Index ActionMethod. Now what mvc does, it returns you the first View only.
say you have an anchor Load the New View. Clicking this will result in a new Url www.example.com/Home/Index#/angular-route. This url will still hit the same MVC HomeController and ActionMethod.
But you have an additional route in angular
`var app = angular.module('ProjectTrackingModule', ['ngRoute', 'ui.bootstrap']);
app.config(function ($routeProvider) {
$routeProvider
.when("/angular-route", {
templateUrl: "/Html/GetHtml?type=angular-route",
controller:"AngularController"
})
.otherwise({redirectTo:"/Home"})
});`
Here in the code section templateUrl: "/Html/GetHtml?type=angular-route",
Html is MVC Controller and GetHtml is ActionMethod. This ActionMethod returns you the new view that you want according to the angular route, that's why we are sending a parameter type too to help us decide.
controller:"AngularController" says that angular will call its controller after the page is returned from you mvc controller. It's Angular's Controller and it has nothing to do with your MVC controller.
you declare angular controller like this:
app.controller('AngularController',function($scope){
alert("my js controller is called");
});
Hope this helps you to find a solution.
Have a simple example can apply to your project. Example our MVC application has four pages as Home, Contact, About and User, we want to create a angularjs template for each pages, so how we do routing for it? how to make angularjs controller for it?
Basic code as following:
Routing:
$routeProvider
.when('/', { // For Home Page
templateUrl: '/AngularTemplates/Home.html',
controller: 'HomeController'
})
.when('/Home/Contact', { // For Contact page
templateUrl: '/AngularTemplates/Contact.html',
controller: 'ContactController'
})
.when('/Home/About', { // For About page
templateUrl: '/AngularTemplates/About.html',
controller: 'AboutController'
})
.when('/Home/User/:userid', { // For User page with userid parameter
templateUrl: '/AngularTemplates/User.html',
controller: 'UserController'
})
.otherwise({ // This is when any route not matched => error
controller: 'ErrorController'
})
}]);
Controller:
var app = angular.module('MyApp'); app.controller('UserController', ['$scope', '$routeParams', function ($scope, $routeParams) {
$scope.Message = "This is User Page with query string id value = " + $routeParams.userid;}]); ...
Full article with download code for it at Angularjs routing asp.net mvc example
You cannot directly point on the .cshtml file but you can point to a templateUrl that points to an MVC route.
Considering you are using the default MVC route {controller}/{action}/{id?}, for example:
var app = angular.module('ProjectTrackingModule', ['ngRoute', 'ui.bootstrap']);
app.config(function ($routeProvider) {
$routeProvider
.when("/Home", {
templateUrl: "/Home/Home", //points to an mvc route controller=Home, action=Home
controller:"HomeController"
})
.otherwise({redirectTo:"/Home"})
});
But in order for it to work the MVC Action should be in the controller also.
public class HomeController : Controller
{
[HttpGet]
public ActionResult Home()
{
return View();
}
}
also your anchor tags points to an incorrect href, it should have the hashbang (#)
Home
Projects
Simple way to do it would be like follows:
MyAngularApp.js
var myAngularApp = angular.module('MyAngularApp', ['ngRoute']);
myAngularApp.config(function ($routeProvider) {
$routeProvider
.when("/myprojects", {
templateUrl: "MyAspMvcCtrl/GetTemplate?id=myprojects",
controller:"MyAngularController"
})
...
.otherwise({redirectTo:"/myprojects"})
});
myAngularApp.controller("MyAngularController", function ($scope) {
...
// Do something
$scope.mydata = {
id = 1234,
msg = "Hello"
};
});
ASP.Net MVC Controller
public class MyAspMvcCtrlController : Controller
{
[HttpGet]
public ActionResult GetTemplate(string id)
{
switch (id.ToLower())
{
case "myprojects":
return PartialView("~/Views/ProjectManagement/ProjectDetails.cshtml");
default:
throw new Exception("Unknown template request");
}
}
}
Layout.cshtml
<html ng-app="myAngularApp">
...
<body>
...
<div ...>
...
My Projects
...
</div>
<div ng-view></div>
...
</body>
</html>
ProjectDetails.cshtml
<div ...>
<h3>ID: {{mydata.id}}</h3>
<p>Message: {{mydata.msg}}</p>
</div>
AngularJs Routing Does not working with cshtml files !!
if you want to use angularjs routing with mvc view (cshtml) file use both routing:
angular routing + mvc routing
your routing code should be like this:
.when("/Home", {
templateUrl: "/Home/Home",
controller:"HomeController"
});
where First Home is Controller Name
and Second Home is The Action Name at the Mvc Controller.

Difference in RedirectToAction and ActionLink causing .post() not to work

I have application that redirect user to Index page of some controller from account controller using RedrirectToAction(), after login.
RedirectToAction("Index", "MyController");
It redirects me to //MyApp/MyController/
I also have navigation on MasterPage view, I use ActionLink:
#Html.ActionLink("Index", "SomeOtherController")
... (other links)
That redirects me to //MyApp/SomeOtherController
Problem is in **/** character on the end of the first route. I have partialView on master page that onClick calls jQuery .post().
function SomeFunction(id) {
$.post("Controller/Action", { id: id },
function () {
... some code
});
}
But when I call that function after redirect from login it trys to access this route:
/MyController/Controller/Action
that doesn't extist. If I change my post call to
$.post("../Controller/Action", ...
it works fine, but then doesn't work for nav links becouse they don't have **/** on the end of route.
What should I do? How to get unique paths from RedirectToAction and ActionLink, with or without **/** on the end?
NOTE:
I can use <a></a> for navigation on master page and enter path with **/** on the end, but I would rather use ActionLink
The key here is that you need MVC to generate your routed URLs for you to pass into your jQuery functions.
1.If your jQuery code is nested within your View, the you can do the following:
function SomeFunction(id) {
$.post('#Url.RouteUrl("Action", "Controller")', { id: id },
function () {
... some code
});
}
2.If your jQuery code is located in an external file (such as myScripts.js), then you will need to somehow pass the MVC generated route to your jQuery function. Since this is not tied to an element directly, you probably would be best served to set this as a hidden element in your view.
View
<input type="hidden" id="jsRoute" value="#Url.RouteUrl("Action", "Controller")"/>
JS
function SomeFunction(id) {
$.post('$("#jsRoute").val()', { id: id },
function () {
... some code
});
}

Resources