With ASP.NET Webforms, I could drag and drop a control on a form and be able to access its properties:
if (number == 0)
AddButton.Enabled = false;
Although I cannot drag and drop a control on a ASP.NET MVC View template, can I change its attributes?
For instance:
disable a button in some conditions and be able to enable it if conditions change.
Be able to change the text of a button from "Next->" to "Finish"
etc.
There are (at least) two Methods to do this:
Method 1: The Simple Way
You would do this by adding logic in your view:
<input type="button" disabled=<%= Model.number <= 0 %> />
Where Model.number is the count of items passed to your view by your controller. If it's less than or equal to zero, disabled will be true.
The syntax may not be exact, I haven't tried this, but this is the path I would go down to do what you want.
This will work for the initial setting of the value; changing it without refreshing the page is a matter of using JavaScript, as other answers have pointed out.
Method 2: The overly complex but more 'MVC' way
If you want the logic in the controller rather than the view, you should set up a specific ViewModel object that you can add the logic to:
ViewModel
public class MyObjectViewModel
{
MyObject MyObject {get; private set; }
bool Enabled; {get; set; }
public MyObjectViewModel(MyObject obj)
{
MyObject = obj;
}
string IsEnabled
{
get
{
if (Enabled)
{
return "";
}
else return "disabled=disabled";
}
}
Controller
public ActionResult Show(int id)
{
MyObject myObject = repository.GetMyObjectById(id)
MyObjectViewModel movm = myObject;
movm.Enabled = myObject.number > 0;
return View(movm);
}
View
<input type="button" <%= Model.IsEnabled %> />
Again, the syntax and usage may be a little off, I'm prototyping this off the top of my head, and am not in a location where I can test this for you.
If you're interested in ViewModels, here are some good resources:
View Model Best Practices
ASP.NET MVC Tip# 50: Create View Models
I've updated it to return disabled=disabled using the string if it is actually disabled.
All client side behaviour is scripted through javascript. MVC default ships with jQuery for this (www.jquery.com).
I've outlined how you could go about your examples:
<input id="nextFinishBtn" type="button" value="Next ->"/>
Assume you want to change this to "Finish" if the user unchecks a checkbox named "Configure Advanced settings". which was true by default
<%= Html.CheckBox("DoAdvancedSettings", "true", new { onclick='changeNextButton()' }); %>
<script langauge="javascript">
function changeNextButton() {
$('#nextFinishBtn').val('Finish');
}
</script>
In general you can access any attribute of any element in jQuery using the .attr construct:
$('#nextFinishBtn').attr('disabled','disabled');
You call it with two parameters to set a value, and with just one to fetch the value. So to see if the button is disabled, you'd do:
if ($('#nextFinishBtn').attr('disabled')=='disabled') { alert('button is disabled'); }
ASP.NET MVC is a lightweight programming model. It does not create a control object model for you at the server the way plain ASP.NET does. Typically, you would use client-side javascript (possibly with help from JQuery) to manipulate the properties of controls that are put on the page by the markup in your view.
If you want to get a quick start with ASP.NET MVC, check out Sharp Architecture (open source). They have guidance and all sorts of goodies to help you get productive with ASP.NET MVC quickly.
Generally, you rarely want to do this. The MVC way is:
Let your controller populate the model with the objects needed to generate the HTML
Pass the model to the appropriate view
Let the view output the HTML based on the values of the objects in the model.
If you often find yourself in need of modifying HTML attributes after the HTML has been generated, you're probably not applying this pattern correctly.
I would check out the HTML helpers.
<%= Html.Button("AddButton","Button Text",HtmlButtonType.Submit,"SomeJavaScriptFunction()",new {disabled="disabled"} ) %>
The only real gotcha is the anonymous class at the end gets funny when you add attributes that are keywords. For example, to add a Css class you need an anonymous class that looks like this new {#class="myCssClassName"}.
As I replied to George Stocker, I've noticed that the disabled attribute can get only 1 value (disabled = "disabled"). Also, anything else disable the input control as well. For instance, disabled = true and disable = false will still disable the control.
It looks like (I'm not sure) having disabled attribute disables the control and not having it enables the control. So I decided to write a extension method to the HtmlHelper class.
public static class MyHelperClass
{
public static string InputDisable(this HtmlHelper html, string name, string myValue, bool isEnabled)
{
string show = "";
if(!isEnable)
show = "disabled = \"disabled\"";
return "<input type = \"submit\" value = \"" + myValue + "\"" + show + " />";
}
}
Now I can access the method this way
<% = Html.InputDisable("myInput", "My Button", false)%>
So the last param determines weather the input control is visible.
Now using the Goerge Stocker logical, I can define the value of isEnabled .
Thanks for all your answers
Related
I'm trying to move from the Old WebForms .NET approach to the newer MVC version. I just can't seem to find a solution to this issue.
In my current projects I use a lot of custom made User Controls. These controls will in almost all cases have several properties that are populated as parameters in the code behind of the parent 'aspx' page.
The User Control will have an 'ascx' page where all the html and controls exist. There will also be a 'ascx.cs' file attached to it where all the properties, methods and back-end logic occur.
What I can't seem to work out is how this logical process works in MVC? The .ascx file is similar to an MVC PartialView... that does make sense.
But where do you store all the backend logic for a PartialView?
How do I set multiple properties and construct the View based on these values?
I've seen some people suggesting you can still use .ascx files in MVC but I'm not sure this is the correct route to go down... certainly not the best practice route anyway?
I'll give a small example which may help:
country.aspx
<%# Register tagprefix="CUSTOM" tagname="Weather" src="~/controls/Weather.ascx" %>
<CUSTOM:Weather ID="Weather" runat="server"></CUSTOM:Weather>
country.aspx.cs
Weather.W_CountryCode = CountryCode;
Weather.W_CountryName = CountryName;
weather.ascx.cs
public string W_CountryCode { get; set; }
public string W_CountryName { get; set; }
Ok that is very basic structure of a control.
The control is embedded into the parent page.
Parameters are set in the code behind of the parent page.
The properties in the control will be used to collect the selected countries weather data from the database as well as running various other methods.
This easily reusable self contained code... I just can't see how you do the same thing in MVC? Where do you set the parameters... where is the code behind for the View stored?
Thanks in advance for any help
When you create a "page" in MVC, you have a controller action that builds a model and passes it to a view.
A "partial" page works exactly the same way - you have a controller action that builds a model and passes it to a view.
The only difference is that the action returns View() or PartialView().
When you want to re-use the partial, you can do so in two ways - load via the action or load via the partial. When you load via the action #Html.Action, you call the controller-action (perhaps with parameters) and that action builds the model and returns the (partial) view. When you load via #Html.Partial your view passes the model to the partial directly (ie not via a controller). Either is acceptable, it depends on how you are building the partial and whether you've already loaded some data or not etc.
So, for your example:
StaffViewModel.cs (partial)
public string W_CountryCode { get; set; }
public string W_CountryName { get; set; }
Staff.cshml (partial)
#model StaffViewModel
<div>Country: #Model.W_CountryCode / #Model.W_CountryName</div>
CountryViewModel.cs (view)
public IList<StaffViewModel> StaffList { get; set;}
Country.cshtml (view)
#model CountryViewModel
#foreach (var staff in Model.StaffList)
{
#Html.Partial("Staff", staff)
}
Controller.cs
public ActionResult Country()
{
var model = new CountryViewModel();
model.StaffList = new List<StaffViewModel>();
// populate staff list from DB etc here
return View(model);
}
So I want to have a status page that will show a different layout of the equipment depending on who's using it which will be a variable defined in the web.config. Should I be creating a separate controller per view? Some of the background functions should be similar but some will probably be different in the future. Or should I have the same cshtml file and hide html markup depending on who's using it or not?
I was thinking of doing something like:
#if(System.Configuration.ConfigurationManager.AppSettings["IsSuperUser"] == "true")
{
Status
}
else {
Status
}
Or is this a bad idea?
There are several options, it all depends on your needs and preferences.
Your code will work, however you must also double check permission in your controller! For example, your url will be "/SuperUser/Status" and "/User/Status". Now, what's stopping non-super user to type in "/SuperUser/Status" to the address bar?
One important rule, never trust the end users! Assume that they will not do what you intend them to do.
Given all, my preference would be to include a variable in your Model to identify the user level (super vs non super), then use that to determine the layout in your views. Remember, you can also change the layout of the view based on variable/expression.
#Layout = Model.IsSuperUser ? "_SuperLayout.cshtml" : "_RegularLayout.cshtml";
Sounds like a view concern. I would pass the config data through a dependency in the controller and render partials:
#if (Model.IsSuperUser)
{
#Html.Partial("_SuperUser")
}
else
{
#Html.Partial("_User")
}
The controller can then do something like:
public ActionResult Index()
{
var vm = new MyViewModel();
vm.IsSuperUser = _config.GetSuperUser();
return View(vm);
}
I am creating a dynamic menu depending on the user role in MVC C#, and (while I can load the HTML tags) the Action links in the menu are not rendering. I imagine this is due to the # at sign at the beginning:
if(userGroup.Equals("Administrators")){
menuItems+=#"<span class=""menuItem"">
#Html.ActionLink(""Add User"", ""RegisterNewUser"", ""Home"", null, new{#Class=""menuItemActionLink""})
</span>";
}
(...)
ViewData["menuItems"]=menuItems;
With a view retrieval:
#Html.Raw(#ViewData["menuItems"])
I have attempted variations of html encoding to no avail. The best solution I can think of is to use the at sign escape code, but I do not know what it is (I tried '\' as well as concatenation in the form of "#" + #"Html.ActionLink). Any ideas?
While I'm not sure why exactly it is that the actionlink is not being rendered properly (maybe the upper-case C in class?), the practice of sending html from the controller to the view is not a good idea. This conflates the roles of controller and view, and makes it more difficult for anyone expecting the distinction between these entities.
A better option might be to have a code block in the view such as:
#if (ViewData["admin"] != null){ ...code to render your admin menu ... }
and in your controller:
if(userGroup.Equals("Administrators")){ ViewData["admin"] = true; }
Depending on your needs, these snippets may change. You could test for true or false (instead of null), or add a string; the ViewData is pretty much what you make of it. But this at least maintains the distinction between HTML rendering at the View and process control at the controller.
#if(userGroup.Equals("Administrators")) {
menuItems += "<span class=\"menuItem\">" + Html.ActionLink("Add User", "RegisterNewUser", "Home", null, new { #class = "menuItemActionLink" }) + "</span>";
}
I have a JSON that brings me set of users-roles, which define for me what actions the user can perform or not.
The JSON loads in the client side. I want to hide elements on this page using serverside code and not clientside, cause this approach is safer and un open to hacks.
//.Net MVC Code
if (!userCanDelete){
//don't print the selector to the page
}
Is it possible in .Net MVC 2 to do that?
I do the same thing in my app, with a custom HTML helper.
Personally I get specific rights for specific guys and specific actions during the login.
I get this rights serverside (it's safer!) from a JSON.
I store this rights in the session and use it in my custom helper:
public static class HtmlHelperCustom
{
public static bool IsAccessibleToUser(this HtmlHelper helper, String element)
{
var user = (UserModel)HttpContext.Current.Session["currentUser"];
return user.rights.Contains(element);
}
}
Then in my View, I just call the helper with the element:
#{
if (Html.IsAccessibleToUser("urlUpdate"))
{
<a href="#Html.DisplayFor(modelItem => item.urlUpdate)" target="_blank">
<i class="icon-wrench" title="update"> </i>
</a>
}
}
You should get JSON serverside or modify my solution slightly.
I'm trying to design a view to accept donations, sell memberships, or sell tickets to events.
I have a dropdown list that displays "Make a donation", "Purchase membership", and the subsequent options are populated from the IList<Event> Model passed from the controller. I use javascript to determine which panel (membership, donation, or event) should be displayed based on the selection.
The problem I'm having is that once an event is selected, I need to be able to dynamically populate the Event panel with the properties of the selected event (without, of course, having to put the user through a browser refresh). I was told by someone that I should be able to use Ajax to accomplish this. Supposedly I could go to my server/home/GetEventById action to do this. However, I haven't been able to find any examples or any tutorials that would help me accomplish this.
Could anybody shed some light on this for me by means of how to go about this, or provide examples or tutorials that would help me?
Here is a code example of fetching some content by calling a controller method through ajax, and then populating a jQuery dialog with it. Hopefully this helps point you in the right direction.
The controller method:
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult GetItemsForJson()
{
var items = Repository.GetItems();
var result = Json(items);
return result;
}
And the jQuery to make it happen:
$('#dialog_link').click(function () {
$.getJSON("/Items/GetItemsForJson/", getItems);
});
function getItems(items) {
$("#itemlist").text("");
$.each(items, function (i, item) {
$("#itemlist").append("<li>" + item.Id + item.Name + "</li>");
});
}
Your question is a bit too broad. I assume you already implemented your Action in controller so we concentrate only on client side scripting.
Following should within $.ready:
$("#ddlSelectEvent").change(function() { // this will fire when drop down list is changed
var selection = $(this).attr("selected"); // text representation of selected value
$(".panels").hide();
$("#panel_" + selection).show(); // Assume the panel naming will be panel_MakeDonation and those...
// Now is time for ajax - load html directly
$.get("server/home/geteventbyId",
{id: "12345"},
function (data) { // callback when data is loaded
$("#panel_" + selection).html(data);
}
);
});
Above codes assume you populate content of panel with html. You might use JSON or other types depending on how you implement it.
http://docs.jquery.com/Ajax/jQuery.get#urldatacallbacktype
I'm not sure how MVC changes this, but here is how I do a callback with Ajax:
In the onchange event of the dropdownlist box you would call a java function that uses Ajax's PageMethod, something like this:
PageMethods.getVersions(LoadVersionsCallback);
The method you are calling in your .aspx.cs file has to be static, it can take parameters and looks something like:
[System.Web.Services.WebMethod]
public static string getVersions() {
StringBuilder sb = new StringBuilder();
... etc.
return sb.ToString();
}
The javascript function that you specified when you called the method will run when the method completes. It will be passed the results.
function LoadVersionsCallback(result) {
// do something with the results - I load a dropdown list box.
...etc.
}