Optimal way to forward data selected from an ActionResult to another - asp.net-mvc-4

I'm not confident that the title is well put so feel free to criticize. :)
I have a controller that returns a page on which the user can click on some options creating a list of items (dynamically built up on the client using JS). As the user is satisfied, they can click on a button and then...
...currently, some DIVs are hidden/displayed, converting the (same) page to a read-only viewer of the selection.
...optimally, another ActionResult should be invoked presenting the information.
My biff is that I can't decide on a good way to transfer the data from one page to another: query string is one option, storing/retrieving to/from DB is an other. I'm not happy with any of these.
What would be a smooth and recommended way to transfer the data to a new view withing the same controller?

$.ajax({
url: '#(Url.Action("Action", "Controller"))',
type: 'post',
data: {
id: id,
data1: data1
},
success: function (result) {
if (result.Success) {
}
});
A very simple way is like the above where you define as many fields as you want and as long as the input parameters match they will be received on the controller
public ActionResult Action(string id, string data1){...
if you want to get more complicated you can build lists and arrays with the json data and then it is usually a good idea to stringify it.
var data = {};
data.id = 'id';
data.list = [];
data.list.push({ name: 'name', location: 'location', etc })
then in the ajax call
data: Json.stringify(data),
again, as long as the names match the controller will receive it. Hope this helps
Edit:
Json.stringify is a tool that is used for sending the data. I don't know all of the details of what it does but it is recommended to use for more complex data. The example here I used for sending a model back to the controller but you mentioned not wanting to create a model. I believe to receive this data on the controller side you need to have input parameters that match what is defined in data. From what I have above list is a complex type so your controller would be something like this.
Public ActionResult Action(string id, List<ComplexType> list){...

Related

AngularJS/Ionic How to correctly get data from database to display in view?

What is the correct/best approach to get data from a database for something like calendar and its events.
I think there are two possible ways:
1) Should I get all data from the database and save it in a $scope object, so that I access the SQLite db only once and work with the object later on
2) Should I only get the data for the currently selected day of the calendar, and if I want to display another day, I do another sql query.
EDIT: the database is local, so network perfomance is no matter
I think it depends on what is more important for you and how big the data is.
If you are working with a local database you have keep in mind that you always have to do async operations to get data from your database. So you always have a little time (depending on the device performance) where the promises have to get resolved even if its on local db.
My ionic application follows the concept 2 of your options without using any cache. The trick to render correct data is to resolve relevant data before entering the view.
My stateprovider looks like this:
.state('app.messages', {
url: "/messages",
views: {
'menuContent': {
templateUrl: "pathToTemplate.html",
controller: 'ExampleMessageListCtrl'
}
},
resolve: {
messages: function($q,DatabaseService) {
var deferred = $q.defer();
//All db functions are async operations
DatabaseService.getMessageList().then(function (messageList) {
deferred.resolve(messageList);
}, function (err) {
deferred.reject(err);
});
return deferred.promise;
}
},
//Disable cache to always get the values from db
cache:false
In the controller you can access the messages variable via injection:
.controller('ExampleMessageListCtrl', function ($scope,messages) {
var loadMessages = function() {
$scope.messages = messages;
};
...
});
The benefits of this concept with resolving the data before entering the state is that you always get the data which is inside the db without rendering frames with empty data inside, which comes from default data inside the scope variables.
I think options 1 is the right option if you want to display data very quickly. The challenge in this case is to hold your cached data synchronized with the data in the database without loosing much performance, which I think is not very easy.

Getting results from api

I am trying to do a domain availability search using an API from free domain API.
After i create an account, it shows:
**Make a REST request using this URL:**
http://freedomainapi.com/?key=11223344&domain=freedomainapi.com
And looking in the documentation page, it has only:
Request http://freedomainapi.com?key=YOUR_API_KEY&domain=DOMAIN_NAME
Result:
{
"status": "success",
"domain": "freedomainapi.com",
"available": false
}
I am very new to APIs...
What I need is to show a domain search box, and when the user enters, it should return with result.
It claims to show domain suggestions as well. I hope it will also work.
Using jquery and a jsonp proxy
http://jsfiddle.net/mp8pukbm/1/
$.ajax({
type: 'GET',
url: "https://jsonp.nodejitsu.com/?callback=?",
data: {url: 'http://freedomainapi.com?key=14ejhzc5h9&domain=freedomainapi.com'},
dataType: "jsonp",
success: myfn
});
function myfn(data) {
console.log(data);
}
you have to use the proxy because cross domain json is not permitted
EDIT:
i made an update to show the result in a div (stringified)
http://jsfiddle.net/mp8pukbm/2/
EDIT #2: i created a test key on that site, you have to use your own
EDIT #3: and there's your combo: http://jsfiddle.net/mp8pukbm/4/
Assuming that you will use java script for showing the search box, you can use AJAX feature of java script (or jQuery or Dojo) ... All you need to do is a "GET" request that like you can pasted and you will get the result back on the response object. To try out the API you can use "Postman" application in Chrome. https://chrome.google.com/webstore/detail/postman-rest-client/fdmmgilgnpjigdojojpjoooidkmcomcm?hl=en
In the response object of the AJAX call you will get a JSON object which you can parse and display the result.
Normally when we use REST we need to differentiate one REST call from another.
Assuming this url
http://freedomainapi.com/checkAvailability?key=YOUR_API_KEY&domain=DOMAIN_NAME
In Application layer we need to write an interface
#GET
#Path("/checkAvailability")
#Produces({MediaType.APPLICATION_JSON})
public ReturnObject getDomainAvailability(#QueryParam("key") String key,
#QueryParam("domain") String doaminName );
Once interface is done you need to write your implementation class.
This class will intract with business layer and perform search task and based on
result collected will create ReturnObject.
ReturnObject => will contain status, domain and availability
On screen
$.ajax({
type: "GET",
url: 'root/checkAvailability',
success: function(jsonData)
{
// read json and perform operation
}
,
error: function (error)
{
// handle error
}
});
If you are using JAVA as backend then you can use gson to parse the result, which is a json. After parsing you can read the values from result and display accordingly :)
Any API is a way to extend a given software. (Might be a website or an application)
In both ways there is a certain way to communicate with the software. In your example freedomainapi.com allows you to fetch if given domain is avaiable. There is no such thing as a suggestion tho, atleast i cannot find any suggestions at all.
Given output is a message format know as JSON. It can be easily interpreted by many major Languages such as Java, Javascript and PHP.
Given String might be easily interpreted as a Map consisting of a status (String), a domain (string) and avaiable (boolean)
A domain availability search could not be easier, assuming K is your key, D is your search input (Domain):
Download http://freedomainapi.com/checkAvailability?key=K&domain=D as input
Parse JSON from input as json
return json["status"] == "success" and json["avaiable"]
Depending on your language you might need to use methods to access properties of json, but that does not influence the basic usage of this api.
on user enters, it calls click_button function and I am assuming your result displaying div id is "main_container" you can give domain suggestions by passing related DOMAIN_NAME s as arguments to click_button function
function click_button(DOMAIN_NAME){
$.ajax({
url : 'http://freedomainapi.com?key=YOUR_API_KEY&domain=DOMAIN_NAME',
type: 'GET',
crossDomain: true,
contentType: "application/json; charset=utf-8",
success: function(data) {
data=JSON.parse(data);
if(data['available']){
$('#main_container').html($('#main_container').html()+'<br>'+DOMAIN_NAME+': Available');
else{
$('#main_container').html($('#main_container').html($('#main_container').html()+'<br>'+DOMAIN_NAME+': Not Available');
}//success
});//ajax
}
hope it helpful !

Unwrapping breeze Entity properties

I'm very new to breeze/knockout, but I'm 99% of the way to doing what I need to do. I'm using the Hot Towel template, and I'm successfully retrieving a list of items via breeze. The entity (ITBAL) is a database first Entity Framework entity. When I look at the JSON coming back in Fiddler, I see the correct data. The problem is that all of the properties of data.results are dependentobservables, and not the raw values themselves.
We have a custom grid control that is trying to display the data.results array. Because it is not expecting observables, it is simply displaying "function dependentobservable" instead of the value.
I tried to unwrap the object, but keep getting the circular reference error. I don't know why that is, since ITBAL isn't associated with anything.
The data as Fiddler reports it:
[{"$id":"1","$type":"WebUIHtml5HotTowel.Models.ITBAL, WebUIHtml5HotTowel","IBITNO":"A100 ","IBWHID":"1 ","IBITCL":"50","IBITSC":"3 ","IBSUSP":" ","IBVNNO":"100 ","IBPRLC":" ","IBSCLC":" ","IBCCCD":"P","IBPICD":" ","IBSAFL":"Y","IBSTCS":399.99000,"IBUSCS":0.00000,"IBAVCS":414.95214,"IBLCST":7.00000,"IBLCCC":20.0,"IBLCDT":110923.0,"IBLSCC":20.0,"IBLSDT":130111.0,"IBLXCC":19.0,"IBLXDT":990102.0,"IBMXO1":2100.000,"IBMXO2":0.000,"IBMXO3":0.000,"IBMNO1":5.000,"IBMNO2":0.000,"IBMNO3":0.000,"IBFOQ1":0.000,"IBFOQ2":0.000,"IBFOQ3":0.000,"IBOHQ1":327.000,"IBOHQ2":0.000,"IBOHQ3":0.000,"IBAQT1":1576.000,"IBAQT2":0.000,"IBAQT3":0.000,"IBBOQ1":50.000,"IBBOQ2":0.000,"IBBOQ3":0.000,"IBPOQ1":448.000,"IBPOQ2":0.000,"IBPOQ3":0.000,"IBIQT1":1446.000,"IBIQT2":0.000,"IBIQT3":0.000,"IBRMD1":10.000,"IBRMD2":0.000,"IBRMD3":0.000,"IBRYD1":10.000,"IBRYD2":0.000,"IBRYD3":0.000,"IBISM1":0.000,"IBISM2":0.000,"IBISM3":0.000,"IBISY1":0.000,"IBISY2":0.000,"IBISY3":0.000,"IBAMD1":0.000,"IBAMD2":0.000,"IBAMD3":0.000,"IBAYD1":0.000,"IBAYD2":0.000,"IBAYD3":0.000,"IBMMD1":0.000,"IBMMD2":0.000,"IBMMD3":0.000,"IBMYD1":0.000,"IBMYD2":0.000,"IBMYD3":0.000,"IBSMD1":1.0,"IBSMD2":0.0,"IBSMD3":0.0,"IBSYD1":1.0,"IBSYD2":0.0,"IBSYD3":0.0,"IBBLME":335.000,"IBBLYO":2680.000,"IBBLLY":1441.000,"IBNMTY":8.0,"IBNMLY":11.0,"IBQSMD":21.000,"IBQSYD":21.000,"IBQSLY":20.000,"IBISMD":16318.19,"IBISYD":16318.19,"IBISLY":45714.87,"IBCSMD":373.46,"IBCSYD":373.46,"IBCSLY":67.00,"IBDQMD":0.000,"IBDQYD":0.000,"IBDQLY":0.000,"IBDSMD":0.00,"IBDSYD":0.00,"IBDSLY":0.00,"IBDCMD":0.00,"IBDCYD":0.00,"IBDCLY":0.00,"IBNOMD":18.0,"IBNOYD":18.0,"IBNOLY":18.0,"IBPKMD":15.0,"IBPKYD":15.0,"IBPKLY":14.0,"IBINUS":" ","IBIAID":0.0,"IBSAID":0.0,"IBCQT1":1527.000,"IBCQT2":0.000,"IBCQT3":0.000,"IBFCST":"Y","IBDRSH":" ","IBWMIU":"JP","IBFL15":" ","IBUS20":" ","IBLPR1":0.00000,"IBLPR2":0.00000,"IBLPR3":0.00000,"IBLPR4":0.00000,"IBLPR5":0.00000,"IBLPCD":" ","IBABCC":"B","IBPRCL":0.0,"IBQBCL":" ","IBACDC":"Y","IBTDCD":" ","IBDOUM":" ","IBTP01":0.0,"IBTP02":0.0,"IBTP03":0.0,"IBTP04":0.0,"IBLMCC":20.0,"IBLMDT":130513.0,"IBTMPH":"Y","IBCOMC":" ","IBCOMF":0.00000,"IBITCT":" ","IBEOQT":0.000,"IBITCM":0.0,"IBBRVW":" ","IBPTID":" ","IBQTLT":0.0000,"IBCTY1":"AUS","IBCTY2":"AUS","IBTXCD":"1","IBREVS":"Y","IBITXC":" ","IBMNOQ":0.000,"IBSTUS":0.000,"IBUS30":" ","IBPSLN":" ","IBPLIN":"N","IBUPDP":"Y","IBDFII":"2011-08-11T00:00:00.000","IBLHRK":"A","IBPLNC":" "}]
My Controller:
[BreezeController]
public class ItemInquiryController : ApiController
{
readonly EFContextProvider<AplusEntities> _contextProvider = new EFContextProvider<AplusEntities>();
[System.Web.Http.HttpGet]
public string Metadata()
{
return _contextProvider.Metadata();
}
[HttpGet]
public IQueryable<ITBAL> ItemBalances(string itemNumber, string warehouse)
{
return _contextProvider.Context.ITBALs.Where(i => i.IBITNO == itemNumber && i.IBWHID == warehouse)
.OrderBy(i => i.IBWHID)
.ThenBy(i => i.IBITNO);
}
}
The relevant portion from the viewmodel:
var manager = new breeze.EntityManager("api/ItemInquiry");
var store = manager.metadataStore;
var itbalInitializer = function (itbal) {
itbal.CompositeKey = ko.computed(function () {
return itbal.IBITNO() + itbal.IBWHID();
});
};
store.registerEntityTypeCtor("ITBAL", null, itbalInitializer);
var index = "0" + (args.pageNum * args.pageSize);
var query = new breeze.EntityQuery("ItemBalances")
.withParameters({ itemNumber: "A100", warehouse: "1" })
.take(args.pageSize);
if (index > 0) {
query = query.skip(index);
}
manager.executeQuery(query).then(function (data) {
vm.itbals.removeAll();
var itbals = data.results;//[0].Data;
itbals.forEach(function (itbal) {
vm.itbals.push(itbal);
});
vm.totalRecords(1);
itemBalancesGrid.mergeData(vm.itbals(), args.pageNum, parseInt(vm.totalRecords()));
}).fail(function (e) {
logger.log(e, null, loggerSource, true, 'error');
});
I figure I must be missing something fairly simple, but it is escaping me.
UPDATE: I removed the BreezeController attribute from the ApiController, and it works correctly.
Jon, removing the [Breeze] attribute effectively disables breeze for your application so I don't think that is the long term answer to your problem.
If you don't actually want entities for this scenario - you just want data - than a Breeze projection that mentions just the data to display in the grid would seem to be the best choice. Projections return raw data that are not wrapped in KO observables and are not held in the Breeze EntityManager cache.
If you want the data as cached entities and you also want to display them in a grid that doesn't like KO observable properties ... read on.
You can unwrap a KO'd object with ko.toJS. However, the grid is likely to complain about circular references (or throw an "out of memory" exception as some grids do) ... even if the entity has no circular navigation paths. The difficulty stems from the fact that every Breeze entity has an inherent circularity by way of its entityAspect property:
something.entityAspect.entity //'entity' points back to 'something'
Because you are using Knockout for your model library and because you say ITBAL has no navigation properties ("is not related to anything"), I think the following will work for you:
manager.executeQuery(query).then(success) ...
function success(data) {
var unwrapped = ko.toJS(data.results).map(
function(entity) {
delete entity.entityAspect;
return entity;
});
vm.itbals(unwrapped);
vm.totalRecords(1); // huh? What is that "parseInt ..." stuff?
itemBalancesGrid.mergeData(vm.itbals(), args.pageNum, parseInt(vm.totalRecords()));
})
ko.toJS is a Knockout function that recursively unwraps an object or collection of objects, returning copies of values. Then we iterate over the copied object graphs, deleting their entityAspect properties. The array of results is stuffed into the vm.itbals observable and handed along.
You can imagine how to generalize this to remove anything that is giving you trouble.
Extra
What the heck is vm.totalRecords? I sense that this is supposed to be the total number of matching records before paging. You can get that from Breeze by adding .inlineCount() to the breeze query definition. You get the value after the query returns from the data.inlineCount property.
Do you really need vm.itbals()? If all you do here is pass values to the grid, why not do that and cut out the middle man?
The following success callback combines these thoughts
function success(data) {
var unwrapped = ko.toJS(data.results).map(
function(entity) {
delete entity.entityAspect;
return entity;
});
itemBalancesGrid.mergeData(unwrapped, args.pageNum, data.inlineCount);
})

Variable in controller is lost when passing between views

I am currently stuck with an issue in my MVC 4 application. I have private variable in controller, that holds instance of a simple class:
private InstallationStatus status = null;
When data get submitted on a view, it gets filled like this:
InstallationStatus installStatus = Install();
if (installStatus != null)
{
status = installStatus;
TempData["installPercent"] = 0;
return View("InstallationProgress", status);
}
This part works as intended, variable is set to the instance as it should be.
After that view periodically checks another variable (using ajax):
<script type="text/javascript">
$(document).ready(function () {
var progress = 0;
$("div.status-message").text("Progress: " + progress + "%");
var statusUpdate = setInterval(function () {
$.ajax({
type: 'GET',
url: "/AppStart/GetInstallProgress",
datatype: "application/html; charset=utf-8",
success: function (data) {
progress = parseInt(data);
if (progress >= 100) {
clearInterval(statusUpdate);
var data = $(this).serialize();
$.ajax({
type: 'POST',
url: "#Url.Action("CompletedStatus", "AppStart")",
success: function () {
window.location = "/Login/Login"
}
});
}
$("div.status-message").text("Progress: " + progress + "%");
}
});
}, 2000);
});
</script>
When it calls "CompletedStatus" action on the controller, variable "status" on the controller is null (the instance set previously is not there?
How do I ensure that its value will persist? It seems to me like whole instance of controller gets lost, but that doesnt really matter to me - the source for "status" is webservice and once I get the instance of InstallationStatus, I cant get it again - I need to keep it.
I tried using TempData to store it but since there can be more than one step between storing it and retrieving it TempData proved unreliable.
The final process is:
Request installation status and navigate to view for installation progress (status will be received when progress will finish).
navigate to view where I will by updating installation progress
using javascript whenever I get callback from server with info about
progress
when installation finishes (status is returned) pass that status to
another view
In the example above I have some dummy code-behind, so the status is returned immediately, but that has no impact on the problem I have.
Currently I can do 1 and 2 and I can call the final view, but I cant pass the status in there because I dont have it on controller anymore and TempData are not reliable for this (sometimes it is still there, sometimes it is not).
Any help will be greatly appreciated.
When it calls "CompletedStatus" action on the controller, variable
"status" on the controller is null (the instance set previously is not
there?
How do I ensure that its value will persist?
private InstallationStatus status = null;
It won't unless it's a static value and that would be a very bad thing to do. Remember that variable values (private members' values) are only scoped within the http request. If you do another request then that's a totally whole new scope for your private variables.
I tried using TempData to store it but since there can be more than
one step between storing it and retrieving it TempData proved
unreliable.
That's because TempData will not have the value you expect it to have once you do another request. One good example of using this TempData is when you want to pass/move some values between a POST and GET, that is when you do a POST and do a redirect. TempData does not fit your case.
Now for a possible solution to your scenario, a good question is: is the installation process called once? Is it unique per user? If it is, which I highly suspect it is, then you need to uniquely identify each request. You can simply use a GUID to identify each request. Save that into your database (better than saving in a session) along with some other information like the status of the installation. Pass that guid back to your client and let them pass it back to the controller and retrieve an update on the status of the installation.

IN MVC 4 how do you pass data to your controller without using the query string

We have a form that displays user information for a list of users. We have an action link to go to a details view for the user to update information. Our application is a mixture of ASP.Net 4.0 and MVC. We normally use encryption to mask variables we use in the query string, but MVC chokes when we attempt to encrypt the query string. We are using the Microsoft Enterprise 5.0 Cryptogrophy class.
How would we go about either encrypting the query string or passing the data without using the query string at all?
We are using MVC 4 with Razor.
We are currently doing something like this:
#Url.Action("Edit", "User", new {id = user.Id}
BTW, I am new to MVC, so there may be an easy answer to this that I am just not aware of.
It would be really nice if we could not use the query string at all.
The simple answer: POST. Use a form or an AJAX call to send the values as POST data, and have a controller method named the same as the existing one, but marked with the [HttpPost] attribute (also, mark the existing one as [HttpGet]). The arguments for this new POST method can be whatever you like; they may get turned into strings when they get POSTed (especially if you use AJAX) but MVC is smart enough to convert them back again provided they're named the same. So, a form that's something like this:
#using (Html.BeginForm("Edit", "User", FormMethod.Post, new { id = "mainForm" }))
{
<input id="userId" type="text" />
<input type="submit" />
}
will correspond neatly to a controller method like this:
[HttpPost]
public ActionResult Edit(int userId)
{
//do whatever
}
provided you've got the routes registered properly. If your GET method is working, then the same route will work here, as long as it doesn't do anything problematic with URL parameters.
That same method will also accept the submission from an AJAX call that looks something like this:
$.ajax({
type: "POST",
url: '#Url.Action("Edit", "User")',
data: { userId: #user.Id },
success: function (data) {
//do whatever
}
});
I agree with anaximander, but if you're going to that much effort to secure the query string information, forms just move the information to HTML fields instead of query string parameters. If you need to keep your existing implementation you could look into inheriting from the default ModelBinder, and provid your own custom implementation to convert from encrypted query string to unencrypted query string before you hand it over to the base class's implementation.