how to pass complicated data between mvc and javascript - javascript

I'm trying to build a UI with two dropdowns. The first one is "category", the second one is "sub-category". I can build a static list of "category" in my razor view. I want the "sub-category" item list to be dynamically updated when "category" is changed. I'm trying to pass all category information from server side to client side since the list is not big and there is no security issue. But I cannot find a good way to format my data and transfer it to client side. I can generate a json object with all of my category trees using the following code:
ExpandoObject catToSubcatMap = new ExpandoObject();
foreach (var cat in repository.Categories)
{
var subcats = repository.SubCategories.Where(s => s.ParentID == cat.CategoryID);
List<Object> subcatNameList = new List<object>();
foreach(var subcat in subcats)
{
subcatNameList.Add(new { Name = subcat.Name });
}
AddProperty(catToSubcatMap, cat.Name, subcatNameList);
}
Session["CatToSubcatMap"] = JsonConvert.SerializeObject(catToSubcatMap, Newtonsoft.Json.Formatting.Indented, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
The json itself looks perfect. But when I tried to read the value from my jQuery function, it failed:
var sss = '#Session["CatToSubcatMap"]';
It seems like there are too many special characters in the json string. My generic question is: how should I format and pass complicated data between server and client. Using Viewbag or Session, which one is preferred?
Thanks
Chris

You can do what you are trying to do; what you have should be formatted correctly, but you just have to include Html.Raw.
var sss = #(
Html.Raw(Session["CatToSubcatMap"].ToString())
);
Raw will essentially write out directly to the response without encoding the contents, which is what likely was happening.

Related

SAPUI5 Create OData entity with dates - generates incorrect request payload that ends in CX_SXML_PARSE_ERROR

We are trying to create an entity that has date attributes via an odata service. Backend is an sap system. This entity has only 3 key attributes plus a bunch of other attributes. We have identified that dates in the keys are the root cause of the problem.
Keys:
Pernr type string,
begda type datetime
endda type datetime.
The code below, (which does not work), has been severely simplified when trying to troubleshoot the issue. At the moment, it reads an entity from an entity set and immediately tries to create one with exactly the same data.
Code:
var oODataModel = new sap.ui.model.odata.ODataModel("/sap/opu/odata/sap/Z_PERSONAL_DATA_SRV/");
//Test entity to be saved
var entity = null;
//Handler for read error
var handleReadE = function (oEvent){
alert("error");
};
//Handler for read success
var handleRead = function (oEvent){
//Get the data read from backend
entity = oEvent.results[0];
//Try to create a new entity with same data
oODataModel.create('/PersDataSet', entity, null, function(){
alert("Create successful");
},function(oError){
alert("Create failed", oError);
});
};
oODataModel.read("/PersDataSet", null, [], true, handleRead, handleReadE);
In the gateway error log, an xml parsing error appears. In this log, we can see the request data and it can be seen that the dates are transported with String types. These dates are defined in the service as DateTimes so the request is rejected.
Example:
<m:properties>
<d:Pernr m:type="Edm.String">00000001</d:Pernr>
<d:Endda m:type="Edm.String">9999-12-31T00:00:00</d:Endda>
<d:Begda m:type="Edm.String">1979-05-23T00:00:00</d:Begda>
When the entity is read, the backend does not send any type information. It sends like the following example:
<m:properties>
<d:Pernr>72010459</d:Pernr>
<d:Endda>9999-12-31T00:00:00</d:Endda>
<d:Begda>1876-07-21T00:00:00</d:Begda>
And, indeed, if we try to save the same info without the type=".." it works. So the problem are the incorrect types ODataModel.create adds to the xml.
My question is:
Can I tell ODataModel.create to not add this type info? It is not doing a good job inferring the types.
Can anyone share an example reading and writing dates through odata?
Thank you very much in advance.
the data returned from oODataModel.read is raw, before you post you need to parse it
var handleRead = function (oEvent){
//Get the data read from backend
entity = oEvent.results[0];
var newEntity = jQuery.extend({},entity);
delete newEntity.__metadata;
newEntity.Begda = new Date(entity.Begda);
newEntity.Endda = new Date(entity.Endda);
//Try to create a new entity with same data
oODataModel.create('/PersDataSet', newEntity, null, function(){
why not use json instead of xml?
Thanks all for the help.
We got this working accounting for the following:
The problem of the wrong types appended to the attributes comes from the read itself. The object returned by read has a __metadata attribute which describes the values. In this object the dates are set with type=edm.string, even when the service says they are DateTime. To me this is a bug of the .read function.
When trying to use the same object to save, create sees the __metatada on the entry and uses those values, producing type edm.string type for the dates. This caused the request to be rejected. Manually changing these __metadata.properties...type to Edm.DateTime makes it work.
In the end, we did the following:
Dates are parsed manually from the Odata response, creating a js Date
object from the strings in format "yyyy-mm-ddT00:00:00", to make it work with control bindings. When we want to save, the reverse is done.
The object to be created is a new object with
only the attributes we care (no __metadata)

How can I add some simple dynamic elements to an otherwise static page?

I'm revamping a site that allows booking of events that run each year. Each event has its own page which is currently entirely static: each page consists of a heading, description, and a list of dates sorted by venue. Each year when new dates become available, someone has to go in and manually change the HTML for each one, an obviously laborious task.
I'd like to automate the process somewhat by having, say, a CSV file that stores the dates (which could be added to piecemeal), and then the page grabs the relevant dates from there when it loads. I have no experience with server-side stuff, but I have a little knowledge of jQuery, and I have a feeling I should be able to do this with AJAX or the like - but how?
I think that ivoszz's idea is the best in your case. If you want to get a database and PHP, you will need a way to get your data into the database itself, which opens a whole new can of worms. Sure, database + server-side frontend is the industry standard, but I feel that it is oversized for your requirements.
It is easier to learn how to display JSON with jQuery, when reading it from a simple text file. You only need to write this code once.
Then, whenever there is a change, you can use a simple workflow: You use Excel to enter the events, using a prerecorded format. Then you export the Excel file as .csv. Use a small program to read the CSV and serialize it to JSON. Copy the ouput to a predetermined location on the server. Everything is ready.
Should somebody else have to update the site in your absence, all they need is Excel, the conversion tool (which is tiny), and the server password. I am posting the code for the conversion tool at the end of this answer.
Alternatively, you can use the code to create an ASP .NET WebForms project. Instead of serializing the objects created by the code, you can make an .aspx page and display the data on it using server-side code. However, this has some disadvantages.
.net webforms has a steeper learning curve than JavaScript, and the resulting code will probably use server-side controls, which are hard to style with CSS unless you know how to do it right.
if you are using an inexpensive hosting package instead of your own server, you have to make sure that the provider has the needed .net version on their server. Also, it will probably eat up more space because of all the libraries normally included in .net web projects.
if the structure of the input data never changes, you can compile the converter once and start treating it as a black box. Any changes to the way stuff is displayed can be made in the JSON-reading code and debugged directly in your browser. For a .net solution, you have to keep an installation of Visual Studio around.
.net webforms is not a future-proof skill. Microsoft has created a new, more convenient Web technology, .NET MVC, and I wouldn't suggest starting learning the older tech now. On the other hand, it is not a good idea to make this project MVC if you already have a bunch of existing static pages, because MVC cannot mix static and dynamic pages easily. You'll probably be able to use the content, but will have to rewrite the whole routing system and replace every single internal link.
Here is the code for a C# appliaction which will convert the csv to JSON. When compiled, place the .exe in the same directory as your csv, called DataSource.csv. Doubleclick it. It will produce a new file called autoOutput.json in the same directory. Each line in the .csv file has to be built in the event name; venue; date; cost; format. You can add comments or similar in the Excel to the right of cost, they will be discarded. The order of the lines does not matter. As long as an event name is unique, all venues and dates which start with it will be interpreted as belonging to that event. As long as the combination of event name and venue is unique, all dates will be interpreted as being about that event at that venue.
I'm not doing anything with the information about which lines could not be read because they were too short. You can append it to the file, or exchange its contents with the warning. Then the person who does the conversion will have to fiddle with the csv until there are no warnings, or you can try to leave it in the file, but ignore it when loading for dispalying.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Web.Script.Serialization;
namespace ElendilEvents2JSON
{
public class Event
{
public String Name { get; set; }
public List<EventInVenue> venues { get; set; }
}
public class EventInVenue
{
public String VenueName { get; set; }
public List<EventInstance> Dates { get; set; }
}
public class EventInstance
{
public String When { get; set; }
public String Cost { get; set; }
}
class Program
{
static void Main(String[] args)
{
//read the file
List<int> unreadable;
List<Event> events = readFile(#".\SourceData.csv", out unreadable);
//write the file using the normal JSON serializer. Will output just everything as a single line. If the data structure is changed, it will output in the new structure.
string autoOutput;
JavaScriptSerializer serializer = new JavaScriptSerializer();
autoOutput = serializer.Serialize(events);
File.WriteAllText(#".\autoOutput.json", autoOutput);
}
public static List<Event> readFile(string path, out List<int> unreadableLines)
{
//get the contents out of the file
var lines = System.IO.File.ReadLines(path);
// split each line into an array of strings
var csv = lines
.Select(line => line.Split(';'))
.ToArray();
//will hold all events
List<Event> events = new List<Event>();
//will hold the numbers of all lines which were OK
List<int> unreadable = new List<int>();
//read each line, if you want to skip header lines, change the zero
for (int lineCounter = 0; lineCounter < csv.Length; lineCounter++)
{
string[] line = csv[lineCounter];
if (line.Length >= 4)
{
string eventName = line[0];
Event currentEvent;
//if we haven't yet created the event, create it now and add it to the dictionary
if (!events.Select(ev => ev.Name).Contains(eventName))
{
currentEvent = new Event { Name = eventName };
//the venues of the new event are still empty
currentEvent.venues = new List<EventInVenue>();
events.Add(currentEvent);
}
else currentEvent = events.Where(ev => ev.Name == eventName).Single();
// the same as above: we have the event now, if the current venue isn't yet on its list, enter it, else use the old one
string venueName = line[1];
EventInVenue currentVenue;
if (!currentEvent.venues.Select(ven => ven.VenueName).Contains(venueName))
{
currentVenue = new EventInVenue { VenueName = venueName };
currentVenue.Dates = new List<EventInstance>();
currentEvent.venues.Add(currentVenue);
}
else currentVenue = currentEvent.venues.Where(ven => ven.VenueName == venueName).Single();
string date = line[2];
string cost = line[3];
EventInstance currentEventInstance = new EventInstance { When = date, Cost = cost };
currentVenue.Dates.Add(currentEventInstance);
}
else
//if the line was too short
unreadable.Add(lineCounter + 1);
}
unreadableLines = unreadable;
return events;
}
}
}
The easiest way to do this would be with PHP and a mySQL database. You can add / overwrite the database with a CSV file, but in the long run, you would be better off developing a simple input form to update the database, rather than going through the task of overwriting / updating the mysql database manually, with the CSV file.
You don't need to start learning PHP to do such simple thing. If you know jQuery a little bit, simply add JSON file to your server, eg. events.json with this structure:
[
{
"event": "Event name",
"description": "description of the event",
"dates": [
{
"date": "20131028",
"place": "Dublin"
}, {
"date": "20131030",
"place": "London"
}
]
}, {
... another event here with the same structure...
}
]
load it with jquery.get, use some templating library (eg. underscore) and make simple template inside the page to display events and details. Finally you will have only 2 pages (or maybe only one), home.html for displaying the list of events and event.html to display details about the event.
Now editing events.json adds and changes events on the home page and details pages. This is just a rough example, it needs to be customized according to your requirements.
Ajax is a technology that alow a conversation between a client side script and a server side one. So in order to use it you will have to study some server side stuff. JQuery is a client-side script which means it only runs on the client machine at the browser.
I would recommend you to start with php it is simplier to learn and to use. And just to read a file is way to easy to learn as you want.

sp.js get all lists by content type

I have combed through the sp namespace docs and not found much to go on.
I found this snippet from http://www.c-sharpcorner.com/Blogs/12134/how-to-get-the-list-content-types-using-csom-in-sharepoint-2.aspx
//// String Variable to store the siteURL
string siteURL = "http://c4968397007/";
//// Get the context for the SharePoint Site to access the data
ClientContext clientContext = new ClientContext(siteURL);
//// Get the content type collection for the list "Custom"
ContentTypeCollection contentTypeColl = clientContext.Web.Lists.GetByTitle("Custom").ContentTypes;
clientContext.Load(contentTypeColl);
clientContext.ExecuteQuery();
//// Display the Content Type name
foreach (ContentType ct in contentTypeColl)
{
Console.WriteLine(ct.Name);
}
Console.ReadLine();
which will get a a certain lists content type.
My thought is get all lists, then get all their content types, then use their id/title to query the lists for data.
It seems like a ton of work to do in a display template.
Am I on the right path or is there something I'm missing? Any sp wiz out there care to weight in on the new search/js architecture?
You may want to use a JavaScript library like SharepointPlus or the popular SPServices.
I think the syntax of SharepointPlus is simplier and the code would be something like:
$SP().lists(function(list) {
for (var i=0; i<list.length; i++) {
// list[i]['Name'] contains the name of the list
$SP().list(list[i]['Name']).get(/* something */)
}
});
You said something about the content types. So you may also want to look at the info() function and check the field with the name "ContentTypeId".
FYI I created this SharepointPlus library.

Serialize MVC model to JSON

I am trying to do a very simple task: get an MVC model, and send it back to server as JSON. I tried
#Html.Raw(Json.Encode(Model));
When debugging the JS, I see that the date objects on the serialized JSON look like: /date (00064321)/ and when passing the serialized JSON to the server, the dates are null on the server-side. Anyone understand what is going on?
Instead of JSON encoding the model directly you have to create an anonymous object converting the date-time properties to strings.
Ex.
var meeting = new Meeting
{
Name = "Project Updates",
StartDateTime = DateTime.Now
};
Passing directly the model..
#Html.Raw(Json.Encode(meeting))
produces
{"Name":"Project Updates","StartDateTime":"\/Date(1338381576306)\/"}
and
#Html.Raw(Json.Encode(new {
Name = meeting.Name,
StartDateTime = meeting.StartDateTime.ToString()
}))
produces
{"Name":"Project Updates","StartDateTime":"5/30/2012 6:09:36 PM"}
as expected.

How to iterate through objects in ViewData via javascript on the page/view?

First off, I am fairly new to MVC and jQuery. I apologize if my question or terminology is incorrect.
I currently have a view in my MVC application that displays a list of addresses. On the same page, I also have a map where I wish to map these locations.
I am trying to find the 'proper' way of getting the list of address objects to the javascript in the view so that it may be iterated through and mapped.
I have seen some solutions which require a getJSON call to the controller from the javascript code. I wish to avoid this solution since it requires another trip to the database and webserver. All of the information that I need to render the addresses on the map is already being presented to the View via ViewData.
I have also seen a solution in which the javascript could access the data passed into the view via ViewModel.Data, however this example was working on a single object, as opposed to a list.
I would appreciate it if anyone had any tips or resources available.
Thanks
Just render the data into your javascript. Say you have a list of address objects like this:
public class Address
{
public string Line1 { get; set; }
public string City { get; set; }
}
// in your controller code
ViewData["Addresses"] = new List<Address>(new Address[] { new Address() { Line1="bla", City="somewhere"}, new Address() {Line1="foo", City="somewhereelse"}});
Render it into your javascript like this:
<script type="text/javascript">
var addresses = new Array(
<% for (int i = 0; i < ViewData["Addresses"].Count; i++) { %>
<%= i > 0 : "," : "" %>({line1:"<%= addr.Line1 %>", city:"<%= addr.City %>"})
<% } %>);
</script>
This basically creates a JSON formatted array with your address objects in javascript.
UPDATE: If you want to do this automatically using the framework code instead of writing your own code to serialize to JSON, take a look at the JavaScriptSerializer. Here's a howto from the great ScottGu on doing this: Tip/Trick: Building a ToJSON() Extension Method using .NET 3.5
Technically, ViewData is not render to output HTMl thus will not be sent to client browser. The only way you can access to ViewData is render it to an object in the HTML like array or something like:
var cityList = new Array();
function addCity(cityId, cityName) {
var city = new Object();
city.CityID = cityId;
city.CityName = cityName
cityList .push(city);
}
<% foreach (Something.DB.City item in ViewData["Cities"] as List<City>)
{ %>
addCity(item.Id, item.Name);
<% } %>
This's the way I usually do when I need to render data for javascript
You could format the data in JSON on the server (as a string). Assign this to your your ViewData. In your View then, assign the ViewData to a javascript variable.
<script type="text/javascrpt">
var objList = <%= ViewData["objectListJSON"]; %>;
for (var obj in objList)
{
...
}
</script>

Categories

Resources