How do I populate Google Map annotations from MVC model - c#

I am making good progress using a Google Map.
The example I am following uses a hard coded JSON array to create annotations
// You can make markers different colors... google it up!
marker.setIcon('http://maps.google.com/mapfiles/ms/icons/green-dot.png')
// a sample list of JSON encoded data of places to visit in Liverpool, UK
// you can either make up a JSON list server side, or call it from a controller using JSONResult
var data = [
{ "Id": 1, "PlaceName": "Liverpool Museum", "OpeningHours":"9-5, M-F","GeoLong": "53.410146", "GeoLat": "-2.979919" },
{ "Id": 2, "PlaceName": "Merseyside Maritime Museum ", "OpeningHours": "9-1,2-5, M-F", "GeoLong": "53.401217", "GeoLat": "-2.993052" },
{ "Id": 3, "PlaceName": "Walker Art Gallery", "OpeningHours": "9-7, M-F", "GeoLong": "53.409839", "GeoLat": "-2.979447" },
{ "Id": 4, "PlaceName": "National Conservation Centre", "OpeningHours": "10-6, M-F", "GeoLong": "53.407511", "GeoLat": "-2.984683" }
];
// Using the JQuery "each" selector to iterate through the JSON list and drop marker pins
$.each(data, function (i, item) {
var marker = new google.maps.Marker({
'position': new google.maps.LatLng(item.GeoLong, item.GeoLat),
'map': map,
'title': item.PlaceName
});
However, I am new to MVC. Please can someone clarify the 2 approaches mentioned in the comment? I have all the points in a list of C# objects. This list is in my model object for the page. AlertsDashboardModel which contains a list - Alerts
I would probably use the list in the model directly, but I am curious how the other approach would work
I am using MVC 5 with Razor
Paul

The second approach is alluding to loading the data once the page has loaded (or as a result of a user interaction on the page like pressing a button) which then sends an ajax request to the server which returns the data:
Client:
$(function () {
//fetch data points from server on page load
$.get("/YourController/GetDataPoints", function(data) {
$.each(data, function (i, item) {
//rest omitted
});
}
Server: (in your Controller)
public JsonResult GetDataPoints()
{
return new JsonResult { Data = ...your list here };
}
UPDATE
If you want to do just the first method of embedded the list as javascript on the server side do this in your razor view:
<script>
var data = #Html.Raw(JsonConvert.SerializeObject(Model.DataPoints));
</script>
Regarding security you need to ensure that everything in that list comes from a trusted source and there is no unvalidated strings that could get injected into the page (if your data point class contains just int you will be fine)

Related

Parse JSON from C# to AngularJS

I have an array of file names:
[HttpPost]
public JsonResult GetJSONFilesList()
{
string[] filesArray = Directory.GetFiles("/UploadedFiles/");
for (int i = 0; i < filesArray.Length; i++)
{
filesArray[i] = Path.GetFileName(filesArray[i]);
}
return Json(filesArray);
}
I need this in AngularJS as a list of objects so I can ng-repeat it out and apply filters. I'm unable to figure out how to get the JSON from the MVC controller to AngularJS.
I've tried the following to make it visible to the view for angular to grab, but I don't know how to make the ng-init see the function to return the list. It erros on "SerializeObject(GetJSONFilesList())" saying it doesn't exist in current context.
<div ng-controller="MyController" data-ng-init="init(#Newtonsoft.Json.JsonConvert.SerializeObject(GetJSONFilesList()),
#Newtonsoft.Json.JsonConvert.SerializeObject(Model.Done))" ng-cloak>
</div>
EDIT:
I've tried using http.get.
Test one:
alert('page load');
$scope.hello = 'hello';
$http.get('http://rest-service.guides.spring.io/greeting').
then(function (response) {
$scope.greeting = response.data;
alert($scope.greeting);
});
alert($scope.hello);
The alert in the http.get never fires, the other alerts do however.
Test two:
$http({
url: '/Home/testHello',
method: 'GET'
}).success(function (data, status, headers, config) {
$scope.hello = data;
alert('hi');
});
[HttpPost]
public string testHello()
{
return "hello world";
}
This causes the angular to break and nothing in the .js works.
Test three
alert('page load');
$scope.hello = 'hello';
$scope.GetJSONFilesList = function () {
$http.get('/Home/testHello')
.success(function (result) {
$scope.availableFiles = result;
alert('success');
})
.error(function (data) {
console.log(data);
alert('error');
});
alert('hi');
};
alert($scope.hello);
[HttpPost]
public string testHello()
{
return "hello world";
}
Alerts nothing from within it, other alerts work.
Fixed:
After some googling, I've found that using .success and .error are deprecated and that .then should be used. So by using .then this resulted in the C# being hit via debug.
Then after using console.log on the returned value found that to have anything be returned I needed to return the value from C# using "return Json(myValue, JsonRequestBehavior.AllowGet); "
And by viewing the object in the console in Chrome by using console.log, I could see my values were in the data part of the returned object.
It was stored in data as an array (as I was passing an array).
I could then get the data out of there by assigning the returned value.data to a scope and could call that in the view {{result[1]}} etc.
return Json(filesArray, JsonRequestBehavior.AllowGet);
$scope.fileList;
$http.get("/Home/GetFileList").then(function (result) {
console.log(result)
$scope.fileList = result.data;
})
Imagine that you divide your front end in three layers (MVC or MVVM) whatever you want.
When you need info from server, the best practice is to separate the logic that makes the request and the logic that manipulates the data.
More info about how to make the request you can find it reading about REST APIS in Consuming a RESTful Web Service with AngularJS.
Normally one of the layers requires the use of services and you can have your controllers and your services (the place where you get the raw data from the server and you make the request. For that you need to use the $http service from angularjs.
$http: The $http service is a core AngularJS service that facilitates communication with the remote HTTP servers via the browser's XMLHttpRequest object or via JSONP.
So basically it shows you how to make get, post and put requests. One example from the documentation is :
// Simple GET request example:
$http({
method: 'GET',
url: '/someUrl'
}).then(function successCallback(response) {
// this callback will be called asynchronously
// when the response is available
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
Pay attention to the url because there is the place where you let your request knwow which method is going to be hit on the server to take the action. If your request is succesful, then you can use the parameter called response. From there, you can do whatever you want. If you decide to make that request part from your controller, you can assign it directly to a variable on your scope. Pay attention if you need to serialize the data. Something like
$scope.myResponseName = response.name ;
The first documentation link from above shows this example which does exactly what I tell you.
angular.module('demo', [])
.controller('Hello', function($scope, $http) {
$http.get('http://rest-service.guides.spring.io/greeting').
then(function(response) {
$scope.greeting = response.data;
});
});
After all the mentioned above, pay attention to what you want to display. Are you going to display the elements of an object array? The use on your HTML the ng-repeat directive. Are you going to display just a variable (No array nor object) then you use need to use an angular expression {{ }}
In summary:
By making an HTTP request, hit the correct method on server.
Make sure you are sending the JSON correctly and that the data is correct.
Retrieve the data on your response.
Assign the data to a variable on your scope and serialize the data if needed.
Display the data correctly depending if it is within an array, if it´s an object or if its just a variable.
I hope the explanation makes sense and check the documentation if you need more info.
You can build your viewmodel so that it contains the data you'd like to serialize and then pass it to angularJS in your view as follows:
<div ng-controller="MyController" data-ng-init="init(#JsonConvert.SerializeObject(myArrayData),
#Newtonsoft.Json.JsonConvert.SerializeObject(Model.Done))" ng-cloak>
and then in your angular controller have a function to receive the data as follows:
$scope.init = function (myArrayData) {
//do something with data
};
The above assumes you're trying to pass data from mvc to angularjs on page load. If you're trying to hit a controller and get data back to angularjs upon some event such as a button click, then you can write an angularjs function similar to the following (this will be an asynchronous request):
app.controller('MyController', function ($scope, $http, $window) {
$scope.ButtonClick = function () {
var post = $http({
method: "POST",
url: "/SomeController/SomeAjaxMethod",
dataType: 'json',
data: { path: $scope.Path},
headers: { "Content-Type": "application/json" }
});
post.success(function (data, status) {
//do something with your data
});
post.error(function (data, status) {
$window.alert(data.Message);
});
}
}
and your controller action would look something like:
[HttpPost]
public JsonResult SomeAjaxMethod(string path)
{
string[] filesArray = Directory.GetFiles(path);
for (int i = 0; i < filesArray.Length; i++)
{
filesArray[i] = Path.GetFileName(filesArray[i]);
}
return Json(filesArray);
}
other answers say to use .success in the angular function, .success and .error are deprecated, instead .then should be used.
Working result:
MVC:
public JsonResult GetFileList()
{
//form array here
return Json(myArray, JsonRequestBehavior.AllowGet);
}
The function needs to be of type JsonResult, and the returned value of Json using JsonRequestBehavior.AllowGet.
AngularJS:
$scope.fileList;
$http.get("/Home/GetFileList").then(function (result) {
console.log(result)
$scope.fileList = result.data;
})
This is in my AJS controller, using .then instead of .success. If you use console.log the result returned from the mvc controller and view it in the browser inspect you'll see the object with lots of other info and the values you want are in the .data section of the object.
So to access the values you need to do result.data. In my case this gives me and array. I assign this to a scope. Then in my view I can access the values by doing {{fileList[1]}} etc. This can also be used in ng-repeat e.g:
<div ng-repeat="file in fileList">
{{fileList[$index]}}
</div>
Each value in the array in the repeat can be accessed using $index which is the number of the repeat starting at 0.

.NET Web Api 2 server side paging issue with Kendo "open source version"

I have a Web API "ApiController" that I would like to get Kendo to do server side paging.
This is not MVC this is Web API v2.
The controller is returning json data to the caller.
The kendo grid works fine when serverPaging: = false.
The kendo grid does work when the serverPaging: = true but it does not show the correct total number of items and it does not have the correct number of pages displayed at the bottom of the grid.
I used this as the example http://bitoftech.net/2013/11/25/implement-resources-pagination-asp-net-web-api/ but the Get method with the page / page size parameters never gets called.
The Get method without parameters does get called but the request url looks like the following. localhost:9000/api/products/?{"take":2,"skip":0,"page":1,"pageSize":2}
My best guess is that the response needs something to tell the grid the number of records left but I can't find any good examples on how to tell the grid the total number of records.
I did add "X-Pagination" to the header with the total count and total pages but I don't think the kendo grid is using the information.
P.S.
I know that Telerik has code for paging with Web API but I can't afford / too cheap for the new Kendo so I'm using the open source kendo.
EDIT: 4-11-14
So I finally got my original problem solved where the total number of items was not showing. While still using WEB API 2 and JSON. "NOT ODATA! I'm still working on the ODATA test but that is for another post ;)".
basically in my GET method in the controller I had to return the following:
return new
{
TotalCount = totalCount,
TotalPages = totalPages,
Results = results
};
This is based on the example posted above. But in the kendo datasource schema I added the following:
schema: {
data: function (data) {
return data.Results || data;
},
total: function (data) {
if (data.TotalCount) {
return data.TotalCount;
}
return result.PageSize || result.length || 0;
},
I never did get the Get(int page = 0, int pageSize = 10) from the example to work but I was able to parse the query string to pull out the "skip" and "take" for paging to work.
In order to get the total records to come down correctly, you have to pass an addition query $inlinecount=allpages. I found useful information at http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/supporting-odata-query-options. Specifically, I used the solution where the Get method in your controller looks something like:
public PageResult<Product> Get(ODataQueryOptions<Product> options)
{
IQueryable results = options.ApplyTo(_products.AsQueryable());
return new PageResult<Product>(
results as IEnumerable<Product>,
Request.GetNextPageLink(),
Request.GetInlineCount());
}
That makes the result look something like:
{
"Items": [
{ "ID":1,"Name":"Hat","Price":"15","Category":"Apparel" },
{ "ID":2,"Name":"Socks","Price":"5","Category":"Apparel" },
// Others not shown
],
"NextPageLink": "http://localhost/api/values?$inlinecount=allpages&$skip=10",
"Count": 50
}
There are a couple things you then need to do in your kendo datasource.
var source = new kendo.data.DataSource({
type: "odata",
schema: {
data: function (data) {
if (data.Items) {
return data.Items;
}
return [data];
},
total: function (data) {
return data.Count;
}
}});
I removed a lot of configuration options for brevity, but hopefully that pulls enough details together to be helpful.

Populate the chart with values returned by controller

I have created a C# asp.net MVC application. The controller is displayed below. I am using high charts and i want to populate the chart with what is returned by the controller.
There are 2 fields below, Mon and Tue, and i need to populate the hard coded values in the javascript below to display the values returned by the controller;
Note: I am not sure if the controller method works :( (I'm a beginner), but i am more concerned about how to populate the chart with the values returned by the controller.
My C# controller;
public string timeHour()
{
var m = new MyModel();
m.theTime = getAllTime(); // get all time
return new JavaScriptSerializer().Serialize(m);
}
The High Chart that i have it in the view;
$(function () {
$('#container').highcharts({
chart: {
type: 'areaspline'
},
title: {
text: 'some title'
},
legend: {
layout: 'vertical',
align: 'left',
verticalAlign: 'top',
x: 150,
y: 100,
borderWidth: 1,
backgroundColor: '#FFFFFF'
},
xAxis: {
categories: [
'Mon',
'Tue'
],
plotBands: [{ // visualize the weekend
from: 4.5,
to: 6.5,
color: 'rgba(68, 170, 213, .2)'
}]
},
plotOptions: {
areaspline: {
fillOpacity: 0.5
}
},
series: [{
name: 'John',
data: [3, 4]
}]
});
});
First off, I would change the Controller's return type from string to JsonResult. To have your Controllers return ActionResults is a good MVC-style convention, and it makes your code a little more descriptive.
[HttpGet]
public JsonResult timeHour()
{
var m = new MyModel();
m.theTime = getAllTime(); // get all time
return Json(m, JsonRequestBehavior.AllowGet)
}
I believe that GET and POST requests should both work for you. It sort of looks like getallTime() is idempotent (although the code isn't shown here), so it looks like a GET request should work. This can be done in jQuery using the .get() method:
$(function () {
$.get("timeHour", null, function(result) {
console.log("If the GET request is successful, the Controller will return the HighCharts data:");
console.log(result);
}, "json");
});
You're going to need to make sure that the JSON returned to the client is in a format that Highcharts likes. Now that the Controller is successfully called (via an AJAX GET request) and the Highcharts data is returned, your second question can be answered:
how to populate the chart with the values returned by the controller
... via the Highcharts "series.data" documentation. If you read this, I think you can figure out how you'd need to change the MyModel object to suit your needs.

Trying to return json and populate selectlist

Im trying to return a Json result from my controller and populate a selectlist using jQuery.
But the code dont even hit the Json method in my controller.
My selectlist
<select id="MyList"></select>
My javascript
<script type="text/javascript">
$(document).ready(function () {
$.getJSON("#Url.Action("GetProductJson", "Product")", null, function (data) {
$("#MyList").addItems(data);
});
});
$.fn.addItems = function (data) {
return this.each(function () {
var list = this;
$.each(data, function (index, itemData) {
var option = new Option(itemData.Text, itemData.Value);
list.add(option);
});
});
};
</script>
My Json method in ProductController
[HttpGet]
public JsonResult GetProductJson()
{
var list = new List<SelectListItem>
{
new SelectListItem { Value = "1", Text = "Aron" },
new SelectListItem { Value = "2", Text = "Bob" },
new SelectListItem { Value = "3", Text = "Charlie" },
new SelectListItem { Value = "4", Text = "David" }
};
return Json(list);
}
You should enable JSON for GET requests which is disabled by default. This happens by passing a second argument to the Json method:
return Json(list, JsonRequestBehavior.AllowGet);
Now go ahead and install FireBug. If you had done that prior to posting this question on StackOverflow you could have inspected the AJAX request in your browser and could have seen that the server returns 500 status code and when you inspected the response you would have seen the exact error message and not only that - you would also have seen a suggestion on how to fix it. The suggestion is basically what I posted here in my answer. Thus you wouldn't even had the need to post your question as you would be able to solve it by yourself. I cannot possibly imagine people doing web development without a tool such as FireBug or Chrome Developer Tools. It's like trying to build a house with your own bare hands and without any tools.
you need to add JsonRequestBehavior.AllowGet in your json method
from Phil haack post JsonHijacking
By default, the ASP.NET MVC framework does not allow you to respond to
an HTTP GET request with a JSON payload. If you need to send JSON in
response to a GET, you'll need to explicitly allow the behavior by
using JsonRequestBehavior.AllowGet as the second parameter to the Json
method. However, there is a chance a malicious user can gain access to
the JSON payload through a process known as JSON Hijacking. You do not
want to return sensitive information using JSON in a GET request.

How do I perform an Ajax Modal Popup based on a "data-" property?

Basically, I have a dynamically created list and I have no idea in advance how many items there will be.
I need each one to launch a slightly different Ajax function on clicking.
I am currently using JQM for the modal boxes (happy to switch if someone knows something better).
The following code works fine for making all .ajaxpopup items launch the same page :
$().ready(function () {
$('#dialog').jqm({ ajax: "/QuestionManager/AjaxPopup/1", trigger: ".ajaxpopup" });
$(".ajaxpopup").click(function (e) {
e.preventDefault();
});
However, I need each item to launch a different page (1/2... I don't know the ID in advance).
I really like Adam's answer about adding a data-itemid tag to the element, but, I just can't seem to actually make this work.
I do not know if this is a JQM limitation or due to the way it is initiated.
The closest I have come is:
$(document).on("click", "a", function () {
var itemId = $(this).data("itemid");
$('#dialog').jqm({ ajax: "/QuestionManager/AjaxPopup/"+itemId, trigger: ".ajaxpopup" });
});
I have also replaced the dialog line with alert(itemId), which is giving the correct result, so, I know I am along the right path - I just can't seem to get this done!
Can anyone help?
You can wire dyanmically added content to respond to events via jQuery's live and on functions.
So if I'm understanding your particular case, you want those text nodes, when clicked, to launch a jqm modal? And you want your model's itemId to be a part of it?
First, add the itemId to your text via a data attribute:
#foreach (var item in Model.Routines)
{
<text data-itemid='#item.itemId'></text>
} //(sorry if the razor syntax is off a bit - that's not my expertise
And then:
$(document).on("click", "text", function() {
var itemId = $(this).data("itemid");
var textnodeText = $(this).text();
$('#dialog').jqm({ ajax: "/QuestionManager/_AjaxCreateQuestionInitial/" + textnodeText,
trigger: itemId });
});
EDIT
Based on your comment, if you have this html:
Add Data
You can handle the click event like this:
$(document).on("click", "a", function() {
var itemId = $(this).data("itemid");
//now run your ajax call
});
Why not create an JsonResult providing Action on your Contoller, that would provide list of items you need? Once that is in place I would iterate over the JSON array and prodce the boxes.
assuming your data would be (an example - this needs to be produced by your controller)
var data = [
{"Id": 1, "Name": "A"},
{"Id": 2, "Name": "B"},
{"Id": 3, "Name": "C"}
];
Your javascript (jquery) loop to handle your boxes would be
$.each(data, function(i, item) {
//replace with your dialog code
alert(data[i].Id+" "+data[i].Name);
});​

Categories