I've had a look over a couple of the other questions on the site and cant find anything that exactly answers what I'm looking to do. To help I'll give a bit of background.
I've recently started experimenting with ASP.NET and MVC4. As my first real attempt at building something useful I am building a web application that will allow me to record and track my workouts in the gym. I have got the basis of all my models/controllers/views etc. The part I am having trouble with is the actual layout of the page to record workouts. Each Workout is made up of a list of Sets (The sets contain information like Exercise, Weight, No of Repetitions etc.... Now the way I want this to work on the WebApp is for a user to be able to hit a button for adding a set. This will then load a a section below without a page re-load that allows them to enter information about that set. They can hit the same button again to record a second set so on and so forth....
They will then hit a save button and I need to loop through each "Set" that has been added and save the information to a database.
I think it should be possible, just not exactly sure how to achieve it. The way I would do it in a standard Windows Application is using UserControls, I am thinking maybe Partial Views in ASP.NET and MVC?
Any ideas guys?
Any more questions let me know.
We did something similar with MVC5, maybe you can figure something out from here.
We used AJAX and PartialViews, at first the page loads we load a table with some initial content, and an Add Option button. When the user hits the button, we increment the current count and add another row to the table via an action which returns a partial view.
<script>
var currentCount = 1;
$(document).ready(function () {
$("#AddOptionButton").click(function () {
var CountryOptions = {};
CountryOptions.url = "AddOption";
CountryOptions.type = "POST";
CountryOptions.datatype = "text/html";
CountryOptions.data = JSON.stringify({count: currentCount });
CountryOptions.contentType = "application/json";
CountryOptions.success = function (html) {
$("#attributesList tr:last").after(html);
currentCount = currentCount + 1;
};
$.ajax(CountryOptions);
});
});
</script>
<table id="attributesList">
<tr>
<th>#</th>
<th>Name</th>
</tr>
<tr>
<td>1.</td>
<td><input type="text" name="optionInput[0]" value="Option 1" /></td>
</tr>
</table>
<input type="button" name="AddOptionButton" id="AddOptionButton" value="Add 1 More" />
<input type="submit" value="Submit Form" />
Our partial view will get the current count as input and returns something like below
<tr id="NewRow1">
<td>2.</td>
<td><input type="text" name="optionInput[1]" value="New Option Value"/></td>
</tr>
Note that we are getting an array of optionInput when the form is submitted. We can easily save the data by looping through the inputs.
You said "without a page re-load". You can do that by using AJAX. It sounds like you have to do much fundamental education to reach your goals. I want to suggest you to work into JavaScript and understand the difference between client-side and server-side in the world of web development. In addition it is important to know the behaviour of HTTP (requests, responses, etc..).
After that, you are able to load content from the server asynchronously using and ajax request. You can do that easily using jQuery. Example here:
$.ajax({
type: 'post',
url: '/Controller/Action',
data: {CustomerName: 'James', AdditionalData: ... },
dataType: 'JSON',
success: function() {
// do anything after request success
}
});
With JavaScript you are able to create an communication between server and client and manipulate the DOM of the client.
Related
The client should be able to send everything with a button, so I can do the following:
Send everything as form fields named like invoiceId_detail_text where invoiceId would be the id of the invoice, detail the part, and text the kind of field.
Example:
<form id="invoices" name="invoice">
#foreach(var Invoce in Model.Invoices) {
<div>
<input type="number" id="#String.Format("{0}_amount", Invoice.InvoiceId)"/>
</div>
/*More Input Fields for the Invoice*/
<div>
<button type="submit"></button>
</div>
}
</form>
Or I can put every invoice separated in its own form, and send them by ajax, every form success I send the next one and so on.
Example:
#foreach(var Invoice in Model.Invoices) {
string formName = String.Format("{0}_form", Invoce.InvoceId);
<form id="#formName" class="invoiceForm" id="#formName" action="#Url.Action("EditIndividualInvoice","InvoicingEdit")">
<input type="hidden" name="InvoiceId" value="#Invoice.InvoiceId"/>
<div>
<input type="number"/>
</div>
/*More Input Fields for the Invoice*/
</form>
}
<button type="button" onclick="SendAllForms();">Send</button>
I did some research between some of my co-workers and many of them told me to just go with a post with all the invoices at the same time and then access every field by item id because I don't have knowledge of how many invoices are going to be sent, but i can get all of them by class on JQuery so it shouldn't be a problem.
I also read this article from a UX point of view and it keeps me thinking about it.
By doing it with AJAX the cliend could be able to see a loading bar and it would be great, but if he closes the page the request would be incomplete and it could be complicated.
Which option could be better and why and what are the security implications of using one over another?
I think the solution depends principally of the behavior that you wish.
By doing it with AJAX the cliend could be able to see a loading bar and it would be great, but if he closes the page the request would be incomplete and it could be complicated.
In the 2 solutions, if the user closes the page after clicking the button, this will not cancel the save. Once the request sent, it cannot be cancelled. So you shouldn't have "security" problems whatever the choosen solution.
The only thing you need to be careful, is to disable all AJAX buttons during an AJAX request, to avoid the user sending several request in the same time (that can provoke saving inconsistency data).
The classic solution is generally most simple to implement.
The AJAX solution can be most complicated to implement (more code), but provides a friendliest behavior.
I am working on a ASP.net application, where i need to select a value in aspx file from the following code:
<td align="right"> Signal </td>
<td> <select id = "signal" name = "signal">
<%
SqlDataReader Dr = Signal();
while (Dr.Read())
{
Response.Write("<option> " + Dr["SignalID"].ToString() + " </option>");
}
Dr.Close();
%>
</select>
</td>
<td align = "right"> Data </td>
<td> <input type="textarea" id = "data"/> Data need on the basis of above selected data </td>
Where Signal() method is returning the some data. For example it returns :
2005, 2006, 2007
Raw code of the above data are:
<select>
<option value="2005">Volvo</option>
<option value="2006">Saab</option>
<option value="2007">Mercedes</option>
</select>
But what i am facing a problem, is that i want this selected variable back to same aspx.cs file without submitting the form. So that i can show the data into the text area.
For example:
User open the some.aspx page, then a form will occur on the page. By default it shows 2005 and on the basis of that info text area will load.
When user will select 2006, and on the basis of that data text area loaded dynamically.
I know that we can do with the Ajax and Jquery, but i am not good enough to work with the Jquery and Ajax.
Solution 1: Use the DynamicPopulate control from the Microsoft AJAX Toolkit - very easy to implement
Solution 2: Use jQuery AJAX API as you've pointed out.
Either solution will require a WebMethod.
[System.Web.Services.WebMethod(BufferResponse=false)]
public string GetTextAreaData(int selectedIndex)
{
//implementation code
}
Solution 1 is rediculously simple, I won't even post an example. Just following the link above and you should be all set. But I will say this, read up on performance tweaks related to the ToolkitScriptManager, like setting it to release mode, combining scripts, etc..
The second solution is pretty easy too, but requires you to parse the results yourself (sort of). jQuery has come a long way from prior years and it's not as scarey anymore.
Create an OnChange event handler (JS method) and attach it to your drop down list. Within the handler, call something like this: (note, this is untested code and it may not work with a simple copy/paste, but you get the idea).
function GetTextAreaData() {
var data = this.selectedIndex; // or whatever you want to send to the server
var options = {
type: "POST",
url: "Default.aspx/GetTextAreaData",
data: "{selectedIndex:" + data + "}",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function(msg) {
$('#textAreaId').val(msg);
}
};
$.ajax(options);
}
I have few jquery tabs on a usercontrol that loads a separate user control under each. Each user control is unique. It all works fine right now but the overall page response is too slow. In order to improve performance I am trying to load few user controls under these tabs on demand (that is on click of tab). Possibly without post back... ajaxish.
Can anyone guide me?
I tried to follow this tutorial and this one too but did not have any success. I have attached the code for parent usercontrol.
<ul id="tabs">
<li class="active">Rewards</li>
<li id="liCoupons">Coupons</li>
<li id="liLibrary">Library</li>
<li id="liProducts">Favorite</li>
<li id="liPreferences">Preferences</li></ul><ul id="tabPanes" class="rewardsTabs">
<li>
<div class="wrapper active">
<uc:Rewards ID="wellness" runat="server" />
</div>
</li>
<li id="liCoupons">
<div class="wrapper">
<uc:Coupon runat="server" />
</div>
</li><li id="liLibrary">
<div class="wrapper">
<uc:Library runat="server" />
</div>
</li><li id="liProducts">
<div class="wrapper">
<uc:Products runat="server" />
</div>
</li>
<li>
<div class="wrapper">
<div class="preferences">
<uc:Preferences runat="server"/>
</div>
</div>
</li>
The second link you mentioned should work. You don't need to define any user controls in your markup.
<ul id="tabs">
<li class="active">Rewards</li>
<li id="liCoupons">Coupons</li>
<li id="liLibrary">Library</li>
<li id="liProducts">Favorite</li>
<li id="liPreferences">Preferences</li>
</ul>
<div id="results" class="wrapper"></div>
Each tab click will do an ajax call
$.ajax({
type: "POST",
url: "Default.aspx/WebMetodToCall",
data: data, // I wouldn't prefer passing webmethod name here
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (msg) {
$('#result').html(msg.d);
},
failure: function (msg)
//error
}
});
to the web methods.
[WebMethod]
public static string Rewards()
{
return RenderControl("~/controls/rewardsControl.ascx");
}
[WebMethod]
public static string Coupons()
{
return RenderControl("~/controls/couponsControl.ascx");
}
...
Each method will render only the requested control. Also in your method you can keep or extract the viewstate depending on your needs. After rendering, the webmethod should pass back the html string to be injected into the placeholders.
If you tried this and were successful rendering one control at a time but still seeing slowness then you have some back end issues while getting the data. If your controls are data heavy I would recommend doing some server side caching.
Hope this helps.
Does your user controls rely on post-backs and view-state for there working? It will be relative easy to fetch the user control HTML to be displayed in the tab using AJAX but then post-back on that control will send the entire data to the actual page (that may not have the user control loaded). So the basic outline would be
Track the active tab using hidden variable or view-state.
Load the user control based on active tab in the early page cycle. The best bet would be init stage (not that view-state won't be available here, so you have to store active tab in hidden variable and access it via Request.Forms collection).
Give each user control a ID and it should be different from tab to tab. ID is very important for loading the view-state.
If you get corrupted view-state errors at tab switching then you need to first load the user control for the previous tab and then at later page stage (say load/prerender), unload it and load new user control for active tab.
You can use a placeholder or panel control within each tab pane to load the user control in the correct location.
Needless to say, on change of jquery tab, you need to submit your form using post-back script. After every post-back, you need to have a script to re-initialize tabs and selecting active tab.
For better user experience, put entire thing into an UpdatePanel.
perhaps use an anchor that points to the service defined below. For instance,
<li></li>
/// <summary>
/// Service used by ajax for loading social media content
/// </summary>
[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class ControlService
{
/// <summary>
/// Streams html content
/// </summary>
/// <param name="type">type of control</param>
/// <returns>html stream of content</returns>
[OperationContract]
[WebGet(UriTemplate = "Content?cType={cType}")]
public Stream GetContent(string cType)
{
var tw = new StringWriter();
var writer = new Html32TextWriter(tw);
var page = new Page();
var control = page.LoadControl(cType);
control.RenderControl(writer);
writer.Close();
var stream = new MemoryStream(Encoding.UTF8.GetBytes(tw.ToString()));
WebOperationContext.Current.OutgoingResponse.ContentType = "text/html";
WebOperationContext.Current.OutgoingResponse.Headers.Add("Cache-Control", "no-cache");
return stream;
}
}
You will need to make an Ajax call in order to make this.
now you have options to call AJAX:
1 - Call via SOAP web service (ASP AjaxScriptManager referencing will be needed for every web method).
2- Call via WCF Service as the previous answer.
3 - Call via Rest service.
4- Call via Jquery ajax method but the request must going to external page like "Actions.aspx" so when you call your method an HTTPRequest will be made into Actions page then it will have the returned data within its response. $.Ajax(url,data,action,successMethod); // this is the fastest way I tried them all.
Here is what you should to do:
1- on the change tab event call your method by using the appropriate Ajax calling method from the above options.
2- from the success method use the returned data but it's better for you to use eval(data) for the DataTime objects.
here is some example explains how to make this call:
var helper = {
callAjax: function(sentData, successFun) {
jQuery.ajax({
url: "/Actions.aspx",
type: "Get",
data: sentData,
cache: true,
dataType: "json",
success: successFun
});
}
};
helper.callAjax('request=getCities&countryID=' + countryID, function (args) {
var result = eval(args); // this object will contain the returned data
var htmlData = '';
for (var i=0;i<result.length;i++){
// write your HTML code by jQuery like this
htmlData += '<div>' + result[i].title + '</div>';
}
$('#tab3').html(htmlData);
});
now at the Actions.ASPX code use the following:
protected void Page_Load(object sender, EventArgs e)
{
object _return = new
{
error = "",
status = true
};
JavaScriptSerializer _serializer = new JavaScriptSerializer();
if (!Page.IsPostBack)
{
string str = Request.QueryString["request"].ToString();
switch (str.ToLower())
{
case "getcities":
int countryID = Convert.ToInt32(Request.QueryString["countryID"].ToString());
_return = JQueryJson.Core.City.getAllCitiesByCountry(countryID).Select(_city => new
{
id = _city.ID,
title = _city.Name
});
_serializer = new JavaScriptSerializer();
Response.ClearContent();
Response.ClearHeaders();
Response.ContentType = "text/json";
Response.Write(_serializer.Serialize(_return));
break;
}
// etc........
}
If you adjust it a little with jquery, this should work:
http://blogs.msdn.com/b/sburke/archive/2007/06/13/how-to-make-tab-control-panels-load-on-demand.aspx
Or you just use the asp.net tabs.
You should go for the second link using jquery and webmethod. That way you will actually populate the tabs on demand without making you page heavy.
In my opinion, the fastest solution to your problem (but not necessarily the best long-term) is to wrap all your UserControls in a .aspx page. In this situation, you'd just have to move your parent UserControl markup to a new .aspx page, and call it via AJAX.
Assuming that you called this page something like Menu.aspx, and further assuming that you don't need any data passed into this page (that is, it can track all of its own data internally), your jQuery AJAX call would look something like this:
function GetMenu ($placeholder) {
$.ajax({ type: "POST", contentType: "application/json; charset=utf-8", dataType: "json",
url: "Menu.aspx",
done: function (result) {
$placeholder.html(result.d);
},
fail: function () {
$placeholder.html("Error loading menu.");
}
});
}
Some notes:
done and fail will replace success and error in jQuery 1.8, so any jQuery AJAX you use should plan for this transition.
I wrapped this in a function largely because I prefer to put AJAX calls inside JS classes and functions, and then reference those objects. With a menu, it's unlikely you'd have several different pages loading the same data via AJAX (since this will be on some sort of master page, I'm guessing), but it's always good to get yourself in the habit of doing these things.
Depending on your feelings about tightly-coupled HTML/JavaScript, you could replace $placeholder above with a callback function. Calling that from your the page where your menu resides would look something like:
$(document).ready(function () {
GetMenu(MenuCallback);
});
function MenuCallback(menuHtml) {
$("#menu").html(menuHtml); // I'm assuming the ID of your ul/span/div tag here.
}
Some people (myself included) use the $ prefix to differentiate between JavaScript and jQuery variables. So here, $placeholder is a jQuery variable.
You might be able to re-write this $.ajax call as a type: "GET", which would be a little bit more efficient, but I'm not sure if the UserControls would cause problems in that regard. Normally, if I'm loading an entire .aspx page via AJAX, I use GET instead of POST. You don't have to change much to try it out: just switch out the type property and change result.d to result.
I think the best solution is to implement client call back
http://msdn.microsoft.com/en-us/library/ms178210.aspx
When user clicks on some tab,onclick event calls js func with name of tab as parameter, than that tab calls server code with same parameter.
Than in code you load controls you want depending which tab is clicked.
Now you need to render controls into html and send tham back to js function.
Now you have controls in js function, find where you want to insert code an insert it.
that should work in theory and it is not to complicated :))
an asnwer (not mine) to this question is probably useful to you:
Asynchronous loading of user controls in a page
it states that there are problems with this with needing a form on the user control to post back, but that should be ok to have independent forms with ajax post. you'll have to think about what happens when posting the form(s) on the page, but shouldn't be insurmountable. shouldn't be any reason you couldn't just make it the ashx handler you have mentioned.
For the site I'm currently working on, I have a list of products which I need to display in a paged list. The list needs to be used on several different pages, each of which has their own rules for how to retrieve their list of products. The list pages need to refresh with AJAX. I'm using LINQ-2-SQL to talk to the database, and MVC3/Razor as the view engine.
So far so good.
What I need help with is figuring out how to implement this. I'll explain what I've done so far, and what isn't working, and I hope someone can give me some direction of the best way to get this working, whether it be bug fixes, missing options, or a redesign. Note that the setup described above is immutable, but everything else can be altered.
For the first set of data, I have Index.cshtml. This will be a list of all products. There will be other sets (such as a list of all products in a category, but I can do the selection for that just fine), but this is the primary test case.
Currently, I have an object to represent the state of the grid: PagingData. The details of it aren't really important, but it takes an IEnumerable when first instantiated, it stores itself in HttpContext.Current.Session between requests, and it has a function that returns an IEnumerable of the products that are supposed to be on the current page. I tried it as an IQueryable<>, but that didn't work.
Currently, I am getting an IQueryable.ToList() and setting it as the data for a DataPager that's used as the Model of a Partial view called _ProductList.cshtml. _ProductList primarily consists of a pager control (another partial) and a foreach loop across the Model to display a partial for each Product.
_ProductList.cshtml:
#model PagingData
<script type="text/javascript">
$(function() {
$('#productList a.pagerControl').live('click', function() {
$('#productList').load(this.href);
return false;
});
</script>
<div id="productList">
#Html.Partial("_Pager", Model)
#foreach (var item in Model.ProductsOnPage)
{
#Html.Partial("_ProductListGridDetail", item);
}
</div>
_Pager uses: #Html.ActionLink(page.ToString(), "_ProductListSetPage", new { newPage = page }, new { #class = "pagerControl" }) to provide the links to change pages (the page variable is the number of the page to draw, from a loop).
This solution works, kindof. The problem I'm having with it is that the only way to update the PagingData with the new page is via a Controller, and each method of modifying the pager (page, # of products per page, format, sort) will need its own controller because I can't overload them. This also means _Pager produces URLs like http://localhost:52119/Product/_ProductListSetPage?newPage=3 instead of http://localhost:52119/Product.
I've tried Ajax.ActionLink(), and wrapping the whole thing in an Ajax.BeginForm(), but neither seemed to work at all. I do have the jquery.unobtrusive-ajax.js library included.
Is this approach feasible? Should I replace the PagingData object with something else entirely? I do not want the paging data in the URL if it's at all possible to avoid it.
If you don't want the page in the url you could use a <form> instead of link, like this:
#using (Html.BeginForm("Index", "Product")
{
#Html.Hidden("newPage", page)
<input type="submit" value="#page" />
}
Which should generate a form for each page with a hidden field containing the actual page number, for example:
<form action="/Product" method="post">
<input type="newPage" value="3" />
<input type="submit" value="3" />
</form>
Now all that's left is to AJAXify this form:
$(function() {
$('#productList form').live('submit', function() {
$.post(this.action, $(this).serialize(), function(result) {
$('#productList').html(result);
});
return false;
});
});
Which would invoke the Index action on ProductController:
public ActionResult Index(int? newPage)
{
var model = ... fetch the products corresponding to the given page number
return PartialView(model);
}
How can I get getTimezoneOffset(); value from the client side in codebehind(aspx.cs file) on Page_load event?
I don't believe you can do this directly. I would have thought you could grab the Date from the Request.Headers and calculate this, but, at least in my environment, using my browser, the Date header is not accessible.
The only possible solution I can think of, since client info is typically limited to what's sent in the headers or in a form request would be to use Javascript to grab the headers, populate a form field (maybe a HiddenField) and trigger a postback.
I googled it and came up with this response, which shows pretty much how to do it the way I was thinking you'd need to - slightly differently than I would have done it, but close enough.
http://www.velocityreviews.com/forums/t70226-client-timezone.html
The code for the answer on that link is here:
<td>
<input type="button" value="getclientutc" onclick="GetClientUTC()">
<input type="hidden" id="hdClientUTC" runat="server">
</td>
:
function GetClientUTC()
{
var now = new Date()
var offset = now.getTimezoneOffset();
document.Form1.hdClientUTC.value = offset
}
</script>