When, if ever, is it appropriate to render an angular partial view using Razor?
I've generally tried to stay away from mixing server side rendering with client-side rendering when using Angular but I have found it convenient to sometimes:
Set variables/data that is known server-side
An example might be a client API key that you might want to use client-side without making an additional call to a backend for that information.
Here is an example of a template:
<div config="{ soundcloudApiKey: '#soundcloudApiKey' }">
</div>
Where #soundcloudApiKey is presumably available in a model server-side and config is a directive that brings data from the markup into your Angular code in case you want to use it elsewhere:
myModule.directive('config', function() {
return {
link: function(scope, elm, attrs) {
// you can save config to a service and use it elsewhere
var config = scope.$eval(attrs.config);
console.log(config.soundcloudApiKey);
}
};
});
Note: there is likely a cleaner way to get to this type of configuration data like via a login process or an explicit call for config settings but I've used this type of thing in the past and it works just fine!
Related
I'm writing a web application that will mainly use AngularJS for its modularity and testability and ASP.NET MVC server side technologies for model validation.
The idea is to load PartialViews in ng-views in order to asynchronously load only specific logic parts. So the question is: how to better pass server-side objects to AngularJS controllers? A silly hack that comes to mind is to print things in the PartialView such as:
window.myValue = 42;
and then get it back from the controller with the $window service injected:
$scope.myValue = $window.myValue
This is not feasible, though, as ng-view loadings strip out every <script> tag before inserting the partial content into the DOM.
I have noticed that there is the ng-init directive, should I rely on that one alone? Generally speaking, what are the best practices for making these two parts work with each other?
Thanks.
If you are unable to use a REST service (best option) you can easily do exactly what you suggest in your question but perhaps in more modular way. You can encode your view's model and place it in a global variable and then have a service that pulls this out:
Here is an example:
<body ng-controller="MainCtrl">
<div>ID: {{data.id}}</div>
<div>Name: {{data.name}}</div>
<script type='text/javascript'>
// mystuff = #Html.Raw(Json.Encode(Model)); Encode the model to json
mystuff = { id: 1, name: 'the name'};
</script>
</body>
app.controller('MainCtrl', function($scope, DataService) {
$scope.data = DataService.getData('mystuff');
});
app.factory('DataService', function($window) {
var service = {}
service.getData = function(data) {
if ($window[data] !== undefined)
return $window[data];
else
return undefined;
}
return service;
});
Demo: http://plnkr.co/edit/jqm2uT3kbDm2U30MCf48?p=preview
You'd might want to create a standardized model that you use in all of your pages. This is a bit hacky and a REST service is your best option as others have stated.
I ended up using the ng-init directive.
Luckily, I got to restructure the Web application and go for a single page, client-side, application based on Web services only.
lets say I want to a partial view like the following sample
#Html.RenderAction("ListDocuments", "MultiFileAttachments", new { id = jsparameter });
in the jsparameter I want to pass as a parameter that is the result of a javascript function. Maybe this is not logical because javascript runs on the client, but how can I achieve a similar functionality ? thank you in advance
If you need to call a part of a page based on some actions or values determined by actions on the client side, then you should think to use JavaScript (preferably jQuery) and Ajax.
Through jQuery-Ajax, you could easily call a Partial view through the Contoler and since it's a regular HTTP request, you would be able to pass all the parameters you need.
Using code as :
#Html.RenderAction("ListDocuments", "MultiFileAttachments", new { id = jsparameter });
is not possible since it is rendered by the server before it's sent to the client browser.
I am starting a new project, and keen to make use of the KnockoutJS + Web Api which are new to me, I have a good understanding of the Web Api, but Knockout is tough to get my head around at the moment.
This is my initial thoughts of how I want my app to work:
I have a standard MVC controller such as LeadsController
LeadsController has an Action called ListLeads, this doesn't actually return any data though, but just returns a view with a template to display data from Knockout.
The ListLeads view calls my api controller LeadsApiController via ajax to get a list of leads to display
The leads data is then mapped to a KnockoutJs ViewModel (I don't want to replicate my view models from server side into JavaScript view models)
I want to use external JavaScript files as much as possible rather than bloating my HTML page full of JavaScript.
I have seen lots of examples but most of them return some initial data on the first page load, rather than via an ajax call.
So my question is, how would create my JavaScript viewModel for Knockout when retrieved from ajax, where the ajax url is created using Url.Content().
Also, what if I need additional computed values on this ViewModel, how would I extend the mapped view model from server side.
If I haven't explained myself well, please let me know what your not sure of and I'll try and update my question to be more explicit.
I think your design is a good idea. In fact, I am developing an application using exactly this design right now!
You don't have to embed the initial data in your page. Instead, when your page loads, create an empty view model, call ko.applyBindings, then start an AJAX call which will populate the view model when it completes:
$(function () {
var viewModel = {
leads: ko.observableArray([]) // empty array for now
};
ko.applyBindings(viewModel);
$.getJSON("/api/Leads", function (data) {
var newLeads = ko.mapping.fromJS(data)(); // convert to view model objects
viewModel.leads(newLeads); // replace the empty array with a populated one
});
});
You'll want to put a "Loading" message somewhere on your page until the AJAX call completes.
To generate the "/api/Leads" URL, use Url.RouteUrl:
<script>
var apiUrl = '#Url.RouteUrl("DefaultApi", new { httproute = "", controller = "Leads" })';
</script>
(That's assuming your API route configured in Global.asax or App_Start\RouteConfig.cs is named "DefaultApi".)
The knockout mapping plugin is used above to convert the AJAX JSON result into a knockout view model. By default, the generated view model will have one observable property for each property in the JSON. To customise this, such as to add additional computed properties, use the knockout mapping plugin's "create" callback.
After getting this far in my application, I found I wanted more meta-data from the server-side view models available to the client-side code, such as which properties are required, and what validations are on each property. In the knockout mapping "create" callbacks, I wanted this information in order to automatically generate additional properties and computed observables in the view models. So, on the server side, I used some MVC framework classes and reflection to inspect the view models and generate some meta-data as JavaScript which gets embeded into the relevant views. On the client side, I have external JavaScript files which hook up the knockout mapping callbacks and generate view models according the meta-data provided in the page. My advice is to start out by writing the knockout view model customisations and other JavaScript by hand in each view, then as you refactor, move generic JavaScript functions out into external files. Each view should end up with only the minimal JavaScript that is specific to that view, at which point you can consider writing some C# to generate that JavaScript from your server-side view model annotations.
For the url issue add this in your _Layout.cshtml in a place where it is before the files that will use it:
<script>
window._appRootUrl = '#Url.Content("~/")';
</script>
Then you can use the window._appRootUrl to compose urls with string concatenation or with the help of a javascript library like URI.js.
As for the additional computed values, you may want to use a knockout computed observable. If that is not possible or you prefer to do it in .Net you should be able to create a property with a getter only, but this won't update when you update other properties on the client if it depends on them.
Before I start, I want to make it clear that my code is working properly, and this is more of a "general best practice" question. I'm using knockout.js to load in my ASP.NET MVC2 model into knockout's viewModel.
In my aspx page I have:
<script>
var model = <%= new JavaScriptSerializer().serialize(Model) %>;
// the above line will display in my page's "View Source". Is this bad? slow?
</script>
Then in my JavaScript include file at the top I have:
$(document).ready(function() {
var viewModel = new MyViewModel();
ko.applyBindings(viewModel);
viewModel.modelProp(model);
});
The code totally works fine, but the concern I have with this is that the JSON output is viewable in the "View Source" option from the browser in the HTML output. I'm curious about two things:
Does this also happen in ASP.NET MVC3? I'm using ASP.NET MVC2, hence I can't use #Html.Raw(Json.Encode(Model)); -- But does the MVC3 method result in the same issue?
Is this something I should be concerned about? Is it a security issue? Is it a performance issue? The page's source will be larger because I'm outputting the JSON into a JavaScript variable, no? Again, maybe it's not an issue in MVC3?
If I hear you correctly, you want to now if you should be concerned that people can see your json. I would not be concerned about that. In fact, not only can they see the json by viewing source, but they can also see it via a network sniffer like fiddler, httpwatch, or browser developer tools (F12). I'm not sure why you care if the json is visible because once it gets data bound to the UI, it will be there too.
As a side note, by loading your KO viewmodel from MVC, that means your viewmodel will only refresh its model data when you post. If you load it via an ajax call (to perhaps an MVC action since you use asp.net mvc) you could avoid that page refresh. Just another option.
My title sums this up pretty well. My first though it to provide a few data formats, one being HTML, which I can provide and consume using the Razor view engine and MVC3 controller actions respectively. Then, maybe provide other data formats through custom view engines. I have never really worked in this area before except for very basic web services, very long ago. What are my options here? What is this Web API I see linked to MVC4?
NOTE: My main HTML app need not operate directly off the API. I would like to write the API first, driven by the requirements of a skeleton HTML client, with a very rudimentary UI, and once the API is bedded down, then write a fully featured UI client using the same services as the API but bypassing the actual data parsing and presentation API components.
I had this very same thought as soon as the first talk of the Web API was around. In short, the Web API is a new product from the MS .NET Web Stack that builds on top of WCF, OData and MVC to provide a uniform means of creating a RESTful Web API. Plenty of resources on that, so go have a Google.
Now onto the question..
The problem is that you can of course make the Web API return HTML, JSON, XML, etc - but the missing piece here is the Views/templating provided by the Razor/ASPX/insertviewenginehere. That's not really the job of an "API".
You could of course write client-side code to call into your Web API and perform the templating/UI client-side with the mass amount of plugins available.
I'm pretty sure the Web API isn't capable of returning templated HTML in the same way an ASP.NET MVC web application can.
So if you want to "re-use" certain portions of your application (repository, domain, etc), it would probably be best to wrap the calls in a facade/service layer of sorts and make both your Web API and seperate ASP.NET MVC web application call into that to reduce code.
All you should end up with is an ASP.NET MVC web application which calls into your domain and builds templated HTML, and an ASP.NET Web API application which calls into your domain and returns various resources (JSON, XML, etc).
If you have a well structured application then this form of abstraction shouldn't be a problem.
I'd suggest developing your application in such a way that you use a single controller to return the initial application assets (html, javascript, etc) to the browser. Create your API / logic in WebAPI endpoint services and access those services via JavaScript. Essentially creating a single page application. Using MVC 4 our controller can return different Views depending on the device (phone, desktop, tablet), but using the same JavaScript all of your clients will be able to access the service.
Good libraries to look into include KnockoutJS, SammyJS , or BackBoneJS
If you do have a requirement to return HTML using the WebAPI e.g. to allow users to
click around and explore your API using the same URL then you can use routing\an html message handler.
public class HtmlMessageHandler : DelegatingHandler
{
private List<string> contentTypes = new List<string> { "text/html", "application/html", "application/xhtml+xml" };
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (request.Method == HttpMethod.Get && request.Headers.Accept.Any(h => contentTypes.Contains(h.ToString())))
{
var response = new HttpResponseMessage(HttpStatusCode.Redirect);
var htmlUri = new Uri(String.Format("{0}/html", request.RequestUri.AbsoluteUri));
response.Headers.Location = htmlUri;
return Task.Factory.StartNew<HttpResponseMessage>(() => response);
}
else
{
return base.SendAsync(request, cancellationToken);
}
}
}
For a full example check out:-
https://github.com/arble/WebApiContrib.MessageHandlers.Html
I've played with this idea before. I exposed an API through MVC3 as JSONResult methods on different controllers. I implemented custom security for the API using controller action filters. Then built a very AJAX heavy HTML front-end which consumed the JSON services. It worked quite well and had great performance, as all data transferred for the web app was through AJAX.
Frederik Normen has a good post on Using Razor together with ASP.NET Web API:
http://weblogs.asp.net/fredriknormen/archive/2012/06/28/using-razor-together-with-asp-net-web-api.aspx
One important constraint of a well designed REST service is utilizing "hypermedia as the engine of application state" (HATEOAS - http://en.wikipedia.org/wiki/HATEOAS).
It seems to me that HTML is an excellent choice to support as one of the media formats. This would allow developers and other users to browse and interact with your service without a specially built client. Which in turn would probably result in the faster development of a client to your service. (When it comes to developing actual HTML clients it would make more sense to use a json or xml.) It would also force a development team into a better designed rest service as you will be forced to structure your representations in such a way that facilitates an end users navigation using a browser.
I think it would be smart for any development team to consider taking a similar approach to Frederik's example and create a media type formatter that generates an HTML UI for a rest service based on reflecting on the return type and using conventions (or something similar - given the reflection I would make sure the html media format was only used for exploration by developers. Maybe you only make it accessible in certain environments.).
I'm pretty sure I'll end up doing something like this (if someone hasn't already or if there is not some other feature in the web api that does this. I'm a little new to Web API). Maybe it'll be my first NuGet package. :) If so I'll post back here when it's done.
Creating Html is a job for an Mvc Controller not for Web Api, so if you need something that is able to return both jSon and Html generated with some view engine the best option is a standard Mvc Controller Action methosd. Content Negotiation, that is the format to return, can be achieved with an Action Fiter. I have an action filter that enable the the controller to receive "hints" from the client on the format to return. The client can ask to return a view with a specific name, or jSon. The hint is sent either in the query string or in an hidden field (in case the request comes from a form submit). The code is below:
public class AcceptViewHintAttribute : ActionFilterAttribute
{
private JsonRequestBehavior jsBehavior;
public AcceptViewHintAttribute(JsonRequestBehavior jsBehavior = JsonRequestBehavior.DenyGet)
{
this.jsBehavior = jsBehavior;
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
string hint = filterContext.RequestContext.HttpContext.Request.Params["ViewHint"];
if (hint == null) hint = filterContext.RequestContext.RouteData.Values["ViewHint"] as string;
if (!string.IsNullOrWhiteSpace(hint) && hint.Length<=100 && new Regex(#"^\w+$").IsMatch(hint) )
{
ViewResultBase res = filterContext.Result as ViewResultBase;
if (res != null)
{
if (hint == "json")
{
JsonResult jr = new JsonResult();
jr.Data = res.ViewData.Model;
jr.JsonRequestBehavior = jsBehavior;
filterContext.Result = jr;
}
else
{
res.ViewName = hint;
}
}
}
base.OnActionExecuted(filterContext);
}
}
Now that it's been a little while through the Beta, MS just released the Release Candidate version of MVC4/VS2012/etc. Speaking to the navigation/help pages (mentioned by some other posters), they've added a new IApiExplorer class. I was able to put together a self-documenting help page that picks up all of my ApiControllers automatically and uses the comments I've already put inline to document them.
My recommendation, architecture-wise, as others have said as well, would be to abstract your application into something like "MVCS" (Model, View, Controller, Services), which you may know as something else. What I did was separate my models into a separate class library, then separated my services into another library. From there, I use dependency injection with Ninject/Ninject MVC3 to hook my implementations up as needed, and simply use the interfaces to grab the data I need. Once I have my data (which is of course represented by my models), I do whatever is needed to adjust it for presentation, and send it back to the client.
Coming from MVC3, I have one project that I ported to MVC4, which uses the "traditional" Razor markup and such, and a new project that will be a single page AJAX application using Backbone + Marionette and some other things sprinkled in. So far, the experience has been really great, it's super easy to use. I found some good tutorials on Backbone + Marionette here, although they can be a bit convoluted, and require a bit of digging through documentation to put it all together, it's easy once you get the hang of it:
Basic intro to Backbone.js: http://arturadib.com/hello-backbonejs/docs/1.html
Use cases for Marionette views (I found this useful when deciding how to create views for my complex models): https://github.com/derickbailey/backbone.marionette/wiki/Use-cases-for-the-different-views