I've developed a web application built using ASP.NET Core Web API and Angular 4. My module bundler is Web Pack 2.
I would like to make my application crawlable or link sharable by Facebook, Twitter, Google. The url must be the same when some user tries to post my news at Facebook. For example, Jon wants to share a page with url - http://myappl.com/#/hellopage at Facebook, then Jon inserts this link into Facebook: http://myappl.com/#/hellopage.
I've seen this tutorial of Angular Universal server side rendering without tag helper and would like to make server side rendering. As I use ASP.NET Core Web API and my Angular 4 application does not have any .cshtml views, so I cannot send data from controller to view through ViewData["SpaHtml"] from my controller:
ViewData["SpaHtml"] = prerenderResult.Html;
In addition, I see this google tutorial of Angular Universal, but they use NodeJS server, not ASP.NET Core.
I would like to use server side prerendering. I am adding metatags through this way:
import { Meta } from '#angular/platform-browser';
constructor(
private metaService: Meta) {
}
let newText = "Foo data. This is test data!:)";
//metatags to publish this page at social nets
this.metaService.addTags([
// Open Graph data
{ property: 'og:title', content: newText },
{ property: 'og:description', content: newText }, {
{ property: "og:url", content: window.location.href },
{ property: 'og:image', content: "http://www.freeimageslive.co.uk/files
/images004/Italy_Venice_Canal_Grande.jpg" }]);
and when I inspect this element in a browser it looks like this:
<head>
<meta property="og:title" content="Foo data. This is test data!:)">
<meta property="og:description" content="Foo data. This is test data!:)">
<meta name="og:url" content="http://foourl.com">
<meta property="og:image" content="http://www.freeimageslive.co.uk/files
/images004/Italy_Venice_Canal_Grande.jpg"">
</head>
I am bootstrapping the application usual way:
import { platformBrowserDynamic } from '#angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
platformBrowserDynamic().bootstrapModule(AppModule);
and my webpack.config.js config looks like this:
var path = require('path');
var webpack = require('webpack');
var ProvidePlugin = require('webpack/lib/ProvidePlugin');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var CopyWebpackPlugin = require('copy-webpack-plugin');
var CleanWebpackPlugin = require('clean-webpack-plugin');
var WebpackNotifierPlugin = require('webpack-notifier');
var isProd = (process.env.NODE_ENV === 'production');
function getPlugins() {
var plugins = [];
// Always expose NODE_ENV to webpack, you can now use `process.env.NODE_ENV`
// inside your code for any environment checks; UglifyJS will automatically
// drop any unreachable code.
plugins.push(new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': JSON.stringify(process.env.NODE_ENV)
}
}));
plugins.push(new webpack.ProvidePlugin({
jQuery: 'jquery',
$: 'jquery',
jquery: 'jquery'
}));
plugins.push(new CleanWebpackPlugin(
[
'./wwwroot/js',
'./wwwroot/fonts',
'./wwwroot/assets'
]
));
return plugins;
}
module.exports = {
devtool: 'source-map',
entry: {
app: './persons-app/main.ts' //
},
output: {
path: "./wwwroot/",
filename: 'js/[name]-[hash:8].bundle.js',
publicPath: "/"
},
resolve: {
extensions: ['.ts', '.js', '.json', '.css', '.scss', '.html']
},
devServer: {
historyApiFallback: true,
stats: 'minimal',
outputPath: path.join(__dirname, 'wwwroot/')
},
module: {
rules: [{
test: /\.ts$/,
exclude: /node_modules/,
loader: 'tslint-loader',
enforce: 'pre'
},
{
test: /\.ts$/,
loaders: [
'awesome-typescript-loader',
'angular2-template-loader',
'angular-router-loader',
'source-map-loader'
]
},
{
test: /\.js/,
loader: 'babel',
exclude: /(node_modules|bower_components)/
},
{
test: /\.(png|jpg|gif|ico)$/,
exclude: /node_modules/,
loader: "file?name=img/[name].[ext]"
},
{
test: /\.css$/,
exclude: /node_modules/,
use: ['to-string-loader', 'style-loader', 'css-loader'],
},
{
test: /\.scss$/,
exclude: /node_modules/,
loaders: ["style", "css", "sass"]
},
{
test: /\.html$/,
loader: 'raw'
},
{
test: /\.(eot|svg|ttf|woff|woff2|otf)$/,
loader: 'file?name=fonts/[name].[ext]'
}
],
exprContextCritical: false
},
plugins: getPlugins()
};
Is it possible to do server side rendering without ViewData? Is there an alternative way to make server side rendering in ASP.NET Core Web API and Angular 2?
I have uploaded an example to a github repository.
There is an option in Angular to use HTML5 style urls (without hashes): LocationStrategy and browser URL styles. You should opt this URL style. And for each URL that you want to be shared o Facebook you need to render the entire page as shown in the tutorial you referenced. Having full URL on server you are able to render corresponding view and return HTML.
Code provided by #DávidMolnár might work very well for the purpose, but I haven't tried yet.
UPDATE:
First of all, to make server prerendering work you should not use useHash: true which prevents sending route information to the server.
In the demo ASP.NET Core + Angular 2 universal app that was mentioned in GitHub issue you referenced, ASP.NET Core MVC Controller and View are used only to server prerendered HTML from Angular in a more convenient way. For the remaining part of application only WebAPI is used from .NET Core world everything else is Angular and related web technologies.
It is convenient to use Razor view, but if you are strictly against it you can hardcode HTML into controller action directly:
[Produces("text/html")]
public async Task<string> Index()
{
var nodeServices = Request.HttpContext.RequestServices.GetRequiredService<INodeServices>();
var hostEnv = Request.HttpContext.RequestServices.GetRequiredService<IHostingEnvironment>();
var applicationBasePath = hostEnv.ContentRootPath;
var requestFeature = Request.HttpContext.Features.Get<IHttpRequestFeature>();
var unencodedPathAndQuery = requestFeature.RawTarget;
var unencodedAbsoluteUrl = $"{Request.Scheme}://{Request.Host}{unencodedPathAndQuery}";
TransferData transferData = new TransferData();
transferData.request = AbstractHttpContextRequestInfo(Request);
transferData.thisCameFromDotNET = "Hi Angular it's asp.net :)";
var prerenderResult = await Prerenderer.RenderToString(
"/",
nodeServices,
new JavaScriptModuleExport(applicationBasePath + "/Client/dist/main-server"),
unencodedAbsoluteUrl,
unencodedPathAndQuery,
transferData,
30000,
Request.PathBase.ToString()
);
string html = prerenderResult.Html; // our <app> from Angular
var title = prerenderResult.Globals["title"]; // set our <title> from Angular
var styles = prerenderResult.Globals["styles"]; // put styles in the correct place
var meta = prerenderResult.Globals["meta"]; // set our <meta> SEO tags
var links = prerenderResult.Globals["links"]; // set our <link rel="canonical"> etc SEO tags
return $#"<!DOCTYPE html>
<html>
<head>
<base href=""/"" />
<title>{title}</title>
<meta charset=""utf-8"" />
<meta name=""viewport"" content=""width=device-width, initial-scale=1.0"" />
{meta}
{links}
<link rel=""stylesheet"" href=""https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/0.8.2/css/flag-icon.min.css"" />
{styles}
</head>
<body>
{html}
<!-- remove if you're not going to use SignalR -->
<script src=""https://code.jquery.com/jquery-2.2.4.min.js""
integrity=""sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=""
crossorigin=""anonymous""></script>
<script src=""http://ajax.aspnetcdn.com/ajax/signalr/jquery.signalr-2.2.0.min.js""></script>
<script src=""/dist/main-browser.js""></script>
</body>
</html>";
}
Please note that the fallback URL is used to process all routes in HomeController and render corresponding angular route:
builder.UseMvc(routes =>
{
routes.MapSpaFallbackRoute(
name: "spa-fallback",
defaults: new { controller = "Home", action = "Index" });
});
To make it easier to start consider to take that demo project and modify it to fit with your application.
UPDATE 2:
If you don't need to use anything from ASP.NET MVC like Razor with NodeServices it feels more natural to me to host Universal Angular app with server prerendering on Node.js server. And host ASP.NET Web Api independently so that Angular UI can access API on different server. I think it is quite common approach to host static files (and utilize server prerendering in case) independently fro API.
Here is a starter repo of Universal Angular hosted on Node.js: https://github.com/angular/universal-starter.
And here is an example of how UI and web API can be hosted on different servers: https://github.com/thinktecture/nodejs-aspnetcore-webapi. Notice how API URL is configured in urlService.ts.
Also you could consider to hide both UI and API server behind reverse proxy so that both can be accessed through same public domain and host and you don't have to deal with CORS to make it work in a browser.
Based on your linked tutorials you could return the HTML directly from the controller.
The prerendered page will be available at http://<host>:
[Route("")]
public class PrerenderController : Controller
{
[HttpGet]
[Produces("text/html")]
public async Task<string> Get()
{
var requestFeature = Request.HttpContext.Features.Get<IHttpRequestFeature>();
var unencodedPathAndQuery = requestFeature.RawTarget;
var unencodedAbsoluteUrl = $"{Request.Scheme}://{Request.Host}{unencodedPathAndQuery}";
var prerenderResult = await Prerenderer.RenderToString(
hostEnv.ContentRootPath,
nodeServices,
new JavaScriptModuleExport("ClientApp/dist/main-server"),
unencodedAbsoluteUrl,
unencodedPathAndQuery,
/* custom data parameter */ null,
/* timeout milliseconds */ 15 * 1000,
Request.PathBase.ToString()
);
return #"<html>..." + prerenderResult.Html + #"</html>";
}
}
Note the Produces attribute, which allows to return HTML content. See this question.
Related
I am using angularjs with Asp.net MVC to check the write access of a folder for users. If the user has the write access then I want to show a div which has a link. I have a Div in SampleView.Html and I have a method which checks for user's write access in MVC Controller called ReportController.cs. what will be the code for Angular Controller that I can use to pass value from MVC controller to Angularjs View?
SampleView.html:
<div id="DivPackerTemplate" class="cp-btn cp-btn-primary pull-right"><a ng-href="\\Samplefolder" >Edit Template</a></div>
ReportController.cs:
public void AccessPackerPlanTemplate(string folderPath)
{
string path = #"\\sample";
string NtAccountName = #"sampleuser";
DirectoryInfo di = new DirectoryInfo(path);
DirectorySecurity acl = di.GetAccessControl(AccessControlSections.All);
AuthorizationRuleCollection rules = acl.GetAccessRules(true, true, typeof(NTAccount));
//Go through the rules returned from the DirectorySecurity
foreach (AuthorizationRule rule in rules)
{
//If we find one that matches the identity we are looking for
if (rule.IdentityReference.Value.Equals(NtAccountName, StringComparison.CurrentCultureIgnoreCase))
{
//Cast to a FileSystemAccessRule to check for access rights
if ((((FileSystemAccessRule)rule).FileSystemRights & FileSystemRights.WriteData) > 0)
{
//Show the link
{
DivPackerTemplate.Visible = false; \\This is not working is there a alternative for this?
}
}
}
}
If your using angular you should make SampleView.Html a directive and inject a service that can call your mvc AccessPackerPlanTemplate method to get the information or better yet create an angular rule service that can wrap and all your rule logic and cache results.
Step 1: create the directive to wrap DivPackerTemplate
Directive doc -> https://docs.angularjs.org/guide/directive
Step 2: create an angular service that wraps your calls to rules logic (rules should be in webapi but you can use regular mvc actions if you must) -> https://docs.angularjs.org/guide/services
Then you simply wire them up by injecting your rules service into the directive and using it to populate the template data, caching is optional.
In the angular world you do not "pass value's" from the server into a view, your views use angular services to "pull values" into views/directives
I'm not an angular expert but something along these lines, omitted the rules service implementation ($rules) as angular services are fairly easy to create.
angular.module('moduleA', [])
.controller('SimpleDirectiveController', ['$rules','$scope',function($rules,$scope) {
$scope.show = function() {
return $rules.yourmethodtogetrulesresult();//for this case return either 'hidden' or 'visible'
}; }])
.directive('PackerTemplate', function() {
return {
template: function($scope) {
var templatevar = '<a ng-href="\\Samplefolder" visibility=\'[XX]\'>Edit Template</a>'
return templatevar.replace('[XX]',$scope.show())
}
};
});
From your MVC application you can return an asp view. Inside the view you can use your angular controller like this:
#model dynamic
#{
Layout = "";
}
<div ng-controller="PackerListController as vm">
<h1 class="page-header">
<button ng-click="vm.editTemplate()">Edit Template</button>
</h1>
</div>
HTML:
<a href="mysite.com/uploads/asd4a4d5a.pdf" download="foo.pdf">
Uploads get a unique file name while there real name is kept in database. I want to realize a simple file download. But the code above redirects to / because of:
$routeProvider.otherwise({
redirectTo: '/',
controller: MainController
});
I tried with
$scope.download = function(resource){
window.open(resource);
}
but this just opens the file in a new window.
Any ideas how to enable a real download for any file type?
https://docs.angularjs.org/guide/$location#html-link-rewriting
In cases like the following, links are not rewritten; instead, the
browser will perform a full page reload to the original link.
Links that contain target element Example:
link
Absolute links that go to a different domain Example:
link
Links starting with '/' that lead to a different base path when base is defined Example:
link
So in your case, you should add a target attribute like so...
<a target="_self" href="example.com/uploads/asd4a4d5a.pdf" download="foo.pdf">
We also had to develop a solution which would even work with APIs requiring authentication (see this article)
Using AngularJS in a nutshell here is how we did it:
Step 1: Create a dedicated directive
// jQuery needed, uses Bootstrap classes, adjust the path of templateUrl
app.directive('pdfDownload', function() {
return {
restrict: 'E',
templateUrl: '/path/to/pdfDownload.tpl.html',
scope: true,
link: function(scope, element, attr) {
var anchor = element.children()[0];
// When the download starts, disable the link
scope.$on('download-start', function() {
$(anchor).attr('disabled', 'disabled');
});
// When the download finishes, attach the data to the link. Enable the link and change its appearance.
scope.$on('downloaded', function(event, data) {
$(anchor).attr({
href: 'data:application/pdf;base64,' + data,
download: attr.filename
})
.removeAttr('disabled')
.text('Save')
.removeClass('btn-primary')
.addClass('btn-success');
// Also overwrite the download pdf function to do nothing.
scope.downloadPdf = function() {
};
});
},
controller: ['$scope', '$attrs', '$http', function($scope, $attrs, $http) {
$scope.downloadPdf = function() {
$scope.$emit('download-start');
$http.get($attrs.url).then(function(response) {
$scope.$emit('downloaded', response.data);
});
};
}]
});
Step 2: Create a template
Download
Step 3: Use it
<pdf-download url="/some/path/to/a.pdf" filename="my-awesome-pdf"></pdf-download>
This will render a blue button. When clicked, a PDF will be downloaded (Caution: the backend has to deliver the PDF in Base64 encoding!) and put into the href. The button turns green and switches the text to Save. The user can click again and will be presented with a standard download file dialog for the file my-awesome.pdf.
Our example uses PDF files, but apparently you could provide any binary format given it's properly encoded.
If you need a directive more advanced, I recomend the solution that I implemnted, correctly tested on Internet Explorer 11, Chrome and FireFox.
I hope it, will be helpfull.
HTML :
<i class="fa fa-file-excel-o"></i>
DIRECTIVE :
directive('fileDownload',function(){
return{
restrict:'A',
scope:{
fileDownload:'=',
fileName:'=',
},
link:function(scope,elem,atrs){
scope.$watch('fileDownload',function(newValue, oldValue){
if(newValue!=undefined && newValue!=null){
console.debug('Downloading a new file');
var isFirefox = typeof InstallTrigger !== 'undefined';
var isSafari = Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0;
var isIE = /*#cc_on!#*/false || !!document.documentMode;
var isEdge = !isIE && !!window.StyleMedia;
var isChrome = !!window.chrome && !!window.chrome.webstore;
var isOpera = (!!window.opr && !!opr.addons) || !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0;
var isBlink = (isChrome || isOpera) && !!window.CSS;
if(isFirefox || isIE || isChrome){
if(isChrome){
console.log('Manage Google Chrome download');
var url = window.URL || window.webkitURL;
var fileURL = url.createObjectURL(scope.fileDownload);
var downloadLink = angular.element('<a></a>');//create a new <a> tag element
downloadLink.attr('href',fileURL);
downloadLink.attr('download',scope.fileName);
downloadLink.attr('target','_self');
downloadLink[0].click();//call click function
url.revokeObjectURL(fileURL);//revoke the object from URL
}
if(isIE){
console.log('Manage IE download>10');
window.navigator.msSaveOrOpenBlob(scope.fileDownload,scope.fileName);
}
if(isFirefox){
console.log('Manage Mozilla Firefox download');
var url = window.URL || window.webkitURL;
var fileURL = url.createObjectURL(scope.fileDownload);
var a=elem[0];//recover the <a> tag from directive
a.href=fileURL;
a.download=scope.fileName;
a.target='_self';
a.click();//we call click function
}
}else{
alert('SORRY YOUR BROWSER IS NOT COMPATIBLE');
}
}
});
}
}
})
IN CONTROLLER:
$scope.myBlobObject=undefined;
$scope.getFile=function(){
console.log('download started, you can show a wating animation');
serviceAsPromise.getStream({param1:'data1',param1:'data2', ...})
.then(function(data){//is important that the data was returned as Aray Buffer
console.log('Stream download complete, stop animation!');
$scope.myBlobObject=new Blob([data],{ type:'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'});
},function(fail){
console.log('Download Error, stop animation and show error message');
$scope.myBlobObject=[];
});
};
IN SERVICE:
function getStream(params){
console.log("RUNNING");
var deferred = $q.defer();
$http({
url:'../downloadURL/',
method:"PUT",//you can use also GET or POST
data:params,
headers:{'Content-type': 'application/json'},
responseType : 'arraybuffer',//THIS IS IMPORTANT
})
.success(function (data) {
console.debug("SUCCESS");
deferred.resolve(data);
}).error(function (data) {
console.error("ERROR");
deferred.reject(data);
});
return deferred.promise;
};
BACKEND(on SPRING):
#RequestMapping(value = "/downloadURL/", method = RequestMethod.PUT)
public void downloadExcel(HttpServletResponse response,
#RequestBody Map<String,String> spParams
) throws IOException {
OutputStream outStream=null;
outStream = response.getOutputStream();//is important manage the exceptions here
ObjectThatWritesOnOutputStream myWriter= new ObjectThatWritesOnOutputStream();// note that this object doesn exist on JAVA,
ObjectThatWritesOnOutputStream.write(outStream);//you can configure more things here
outStream.flush();
return;
}
in template
<md-button class="md-fab md-mini md-warn md-ink-ripple" ng-click="export()" aria-label="Export">
<md-icon class="material-icons" alt="Export" title="Export" aria-label="Export">
system_update_alt
</md-icon></md-button>
in controller
$scope.export = function(){ $window.location.href = $scope.export; };
I inherited some code and I am trying to figure the right url to a webapi controller but my knowledge of mvc web api is lacking.
I have inline script that is making an ajax post like this:
$('#saveNewEducation').on('click', function () {
var educationAdd = {
'educationId': $('#newEducation').val(),
'startDate': $('#newEducationDate').val(),
'identificationId': $('#identificationId').val(),
'educationNote': $('#newEducationNote').val(),
'examinerId': $('#newExaminer').val()
};
$.post('#Url.HttpRouteUrl("DefaultApi", new { controller = "EmployeeApi", educationName = "educationCreate" })', educationAdd)
.done(function (data, textStatus, jqXhr) {
if (jqXhr.status == 200) {
$('#save-education').modal('show');
} else {
$('#fail-save-employee').modal('show');
}
})
.fail(function (jqXhr) {
var education = $("#new-education");
if (jqXhr.status == 409) {
$('#future-save-employee').modal('show');
} else {
if (jqXhr.status == 400) {
clearErrors(education);
var validationErrors = $.parseJSON(jqXhr.responseText);
$.each(validationErrors.ModelState, function (i, ival) {
remoteErrors(education, i, ival);
});
} else {
$('fail-save-employee').modal('show');
}
}
});
I don't like inline script and I have created a seperate js file where I want to make this call from.
I need help with
I need help figuring out the right url to the api controller so that i can use it in the script file.
I tried
Reading this article I tried the following:
$.post('/DefaultApi/EmployeeApi', educationAdd)
This gave me a
404 not found error.
in the inline script the url is like this:
$.post('#Url.HttpRouteUrl("DefaultApi", new { controller = "EmployeeApi", educationName = "educationCreate" })', educationAdd)
WebApiConfig.cs file:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional });
method I am trying to access in EmployeeApi controller:
public IHttpActionResult EducationPost(EmployeeEducation model, string educationName){}
How can I do this?
Resolving the URL
Generally in MVC applications, you would resolve this by using the Url.Action() helper to resolve the proper URL provided its Controller, Action and RouteValues:
// Supply the Action, Controller and any route values that you need
$.post('#Url.Action("EducationPost","EmployeeApi", new { educationName = "educationCreate"})', function(){
// Do something here
});
However, Web API also features the Url.Link() helper that might be useful as well and works in a similar manner except based on the route itself :
$.post('#Url.Link("DefaultApi", new { controller = "EmployeeApi", action = "EductationPost", educationName = "educationCreate" })', function(){
// Do something here
});
When using External Javascript Files
As you would imagine, these techniques won't work when using external Javascript files. What I generally recommend in these situations is to consider using a data-* attribute in HTML to store the URL and then reference that within your event handler that will trigger the AJAX call :
<button id='call-ajax' data-post-url='#Url.Action(...)' />
<script>
$(function(){
$('#call-ajax').click(function(e){
// Read the attribute and use it
$.post($(this).attr('data-post-url'), function(){
// All done
});
});
});
</script>
You could obviously accomplish this same basic idea through the use of variables or hidden elements, but the same idea basically holds true as far as actually accessing it goes.
Have a look at this answer:
How to send razor created Url to js file
This user offers 3 possible solutions.
global js variable
custom "data-" attribute
hidden input
I am using ng-grid in an ASP.NET web application to display data from a WCF Service. In this Plunker example the data is stored in a JSON file and then converted by the Angular module.
var app = angular.module('myApp', ['ngGrid']);
app.controller('MyCtrl', function($scope, $http) {
$scope.setPagingData = function(data, page, pageSize){
var pagedData = data.slice((page - 1) * pageSize, page * pageSize);
$scope.myData = pagedData;
$scope.totalServerItems = data.length;
if (!$scope.$$phase) {
$scope.$apply();
}
};
$scope.getPagedDataAsync = function (pageSize, page, searchText) {
setTimeout(function () {
var data;
if (searchText) {
var ft = searchText.toLowerCase();
$http.get('largeLoad.json').success(function (largeLoad) {
data = largeLoad.filter(function(item) {
return JSON.stringify(item).toLowerCase().indexOf(ft) != -1;
});
$scope.setPagingData(data,page,pageSize);
});
} else {
$http.get('largeLoad.json').success(function (largeLoad) {
$scope.setPagingData(largeLoad,page,pageSize);
});
}
}, 100);
};
$scope.getPagedDataAsync($scope.pagingOptions.pageSize, $scope.pagingOptions.currentPage);
$scope.gridOptions = {
data: 'myData',
enablePaging: true,
showFooter: true,
enableCellSelection: true,
enableCellEdit: true,
enableRowSelection: false,
totalServerItems:'totalServerItems',
pagingOptions: $scope.pagingOptions,
filterOptions: $scope.filterOptions
};
});
Plunker
This piece of code was interesting because it read dicrectly from a JSON file and dynamically bound it to the ng-grid. I was hoping that I would be able to use the ASP.NET (C#) codebehind to pass a JSON file to the AngularJS code and let it do all of the work to parse it.
It seems like you cannot do this, so what is the best way to pass data to AngularJS from ASP.NET?
Thanks in advance.
The question you're really asking is, how do I pass some server-side information to Javasript? Angular is irrelevant here.
Solution 1: just write it to a client-side variable. Something like:
If you're using MVC:
var myJson = '#ViewBag.MyJson';
var json = JSON.parse(myJson);
If you're using WebForms:
var myJson = '<% Response.Write(MyJson); %>';
var json = JSON.parse(myJson);
Solution 2: define a server-side method that returns your json string, and call it from the client side using Ajax. Then parse it as previously. There's a decent example here.
Solution 3: can't you write to your Json file from the server side, before loading it into Angular?
You could create an Angular directive that handles parsing, and then pass it a JSON file name in your ASP.NET page, like this:
// ASP.NET page
<%
JSONFile.Text = "path/to/json/file.json";
%>
<!DOCTYPE html>
<html>
<head runat="server">
// including our parse-json directive as "metadata" in the head
<parse-json data-json-file="<%JSONFile%>"></parse-json>
</head>
<body>
...
...
</body>
</html>
And then your angular directive could look something like this:
app.directive('parseJson', ['$http', function ($http) {
return {
restrict: 'E',
scope: {
jsonFile: '#'
}
link: function (scope) {
$http.get('my/url/' + scope.jsonFile)
.success(function (data) {
// your parsing logic would go here.
});
}
};
}]);
The data prefix on the data-json-file attribute is the HTML5 standard for custom data attributes.
So, to summarize the code above. In our ASP.NET page we are including a custom tag (our angular directive) named <parse-json> in the <head> of our html page (it doesn't have to be there, but it is sorta metadata so I figured it fit). We are setting the attribute data-json-file to equal the path of the JSON file on our server. When angular loads on the client, it will request this JSON file, and then from there you can do whatever you want with it (in your case, parse it).
Hope this helps!
i use from kendo grid but my grid dont fill with values and when show page grid dont load.
i use kendo 2014 and asp.net 2012.
my api controller code :
public class ValuesControllerApi : ApiController
{
public List<File> Get()
{
GuaranteeEntities ef = new GuaranteeEntities();
var file = ef.Files.Where(c => c.UpdaterUserInfo == "Guarantee").ToList();
return file;
}
}
and my html Code is :
<div id="employeesGrid">
<script>
$(function () {
$("#employeesGrid").kendoGrid({
dataSource: new kendo.data.DataSource({
transport: {
read: "/api/ValuesControllerApi"
}
})
});
});
$(function () {
$("#employeesGrid").kendoGrid({
columns: [
{ field: "Name" , title:"test" },
{ field: "Family", title: "test test" }
],
dataSource: new kendo.data.DataSource({
transport: {
read: "/api/ValuesControllerApi"
}
}),
sortable: true
});
});
</script>
</div>
Try testing your API in the browser alone.
localhost:12345/api/ValuesControllerApi (change your debugging url as required)
Does that work? Chances are, it doesn't. The reason is because WebApi uses a default pattern for finding the controller end point. You can find more info here.
But to spare you the time, take note of this line:
To find the controller, Web API adds "Controller" to the value of the {controller} variable.
What this means is by default, WebApi assumes all controller classes end in "Controller" when trying to route to an endpoint. In your case, you have named your API ValuesControllerApi which ends in "Api". Remove the "Api" from the class name and it should work.
So, your class name should look like this: ValuesController : ApiController
and you call it like this: api/Values
I think that your controller action won't give Kendo Grid the results in the format it expects. You need to send it a DataSourceRequest. There is a simple example here you may check.
Also this documentation article should shed some light on the issue too.
Hope it helps!