I have a standard ASP.Net template, which has a _Layout where my menu is generated. I have multiple data contexts representing different databases throughout my application and all works fine.
I want to add a count as a bootstrap badge next to one of the items in the _Layout. To do this I need to pass in db.TicketDal.Count(). What is the best way to do this directly into the layout. I did try passing the data in a ViewBag entry from the home controller but then when I go to different controllers that doesn't display. I could modify each controller but that seems the wrong way to do it. I suspect I am overthinking this but any advice would be appreciated.
You must create a BaseController. There you can get the var TicketCount = db.TicketDal.Count(); or some List<MenuItems> let's say and any other controllers must inherit from BaseController so in any of them and in any of their methods you would have the same instance of List<MenuItems> or TicketCount
Or you can create a helper class.
public static class BaseKnowledgeHelper
{
public static int GetTicketCount()
{
return db.TicketDal.Count();
}
}
And call that everywhere: #BaseKnowledgeHelper.GetTicketCount()
I am going to suggest what i suggested on a similar post like this.
I would not create a BaseController, this would load and query the DB every single time (which could be ok) and the UI thread is dependent on the DB call.
What I would do is implement an Ajax call to get this information and then put it on your _Layout
Acton Method
public ActionResult GetTicketCount()
{
//Code to fetch the data for the count
return Json(count, JsonRequestBehavior.AllowGet);
}
Ajax on the View
$(function () {
$.ajax({
type: "GET",
url: '#Url.Action("GetTicketCount","WhatEverController")',
success: function (data) {
// You can now do something with the data which contains the count
// e.g. Find the div where the count is suppose to be and insert it.
}
});
});
This way you are not having to wait for any DB calls to finish, the downside is that the count will not appear at the exact same time as your DOM loads. But you could always make it more friendly looking by using something like Spin.js
Related
I have a UserController that has a Destroy function. It is a rather complex function because it demands to destroy all user's data. I have another action, from the Admin panel that deletes all data from a specific set of users.
Since I don't want to replicate the code from the UserController, I would like to call the Destroy function from UserController for each User to destroy its data.
How should I proceed?
Thanks in advance.
Why not move this functionality to a common class method which can be accessed from both the controllers as needed ?
public class UserManager
{
public void Destroy(List<int> userIdsToDestroy)
{
foreach(var userId in userIdsToDestroy)
{
//Execute code to destroy
}
}
}
and from your action methods, you can call it like
var mgr = new UserManager();
var badUsers = new List<int> { 1,2,3};
mgr.Destroy(badUsers);
Update the badUsers variable value as needed based on from where you are calling it.
Shared functionality like this would ideally be in a business layer, and both controllers would call that code. If it's a little app, you could just create a separate folder structure for shared code. Larger projects would have a business layer dll.
Why not make the Destroy() method as a Non-Action method then like
[Non-Action]
public void Destroy(User user)
{
// code goes here
}
You can as well make this Destroy() function as part of your business layer logic instead of handling this in controller. In that case, you call it from anywhere.
If you want it to be #controller, you can as well consider usig [ChildActionOnly] action filter attribute.
I'm newbie in web development.
Consider site that user can be logged in (e.f Facebook log-in)
Assuming I know if a user is logged in or not (I'm on my way to find how ;) - is it possible that on the same view (.cshtml) - part of the elements will be hidden if user anonymous or will be revealed if user is logged-in? you know - something like nice attributes or conditions (in short - to put the logic on the same view and not to manage two .cshtml)
I'm personally not a fan of having if statements in my view's as they can easily begin to get cluttered, especially if you are using roles.
This is the way I prefer to do it to avoid that.
Create an html helper containing the logic like this abstracted away:
namespace System.Web.Mvc
{
public static class HtmlHelperExtensions
{
public static MvcHtmlString UserMessage(this HtmlHelper htmlHelper)
{
string welcomeFormat = "Welcome, {0}";
var isAuthenticated = htmlHelper.ViewContext.HttpContext.User.Identity.IsAuthenticated;
var name = htmlHelper.ViewContext.HttpContext.User.Identity.Name;
var message = isAuthenticated ? string.Format(welcomeFormat, name) : string.Format(welcomeFormat, "anonymous!");
return new MvcHtmlString(message);
}
}
}
Call this method within my view like this:
<p>#Html.UserMessage()</p>
If you are new to web development the code for the Helper extension might look a bit overwhelming but you only end up writing that once and the code you use to call it elsewhere is a lot simpler and re-usable.
Here is an article about Html helpers for more info:
http://www.codeproject.com/Articles/649394/ASP-NET-MVC-Custom-HTML-Helpers-Csharp
Update
Forgot to mention this technique too, which again avoids the if statements.
Create two partial views say _auth.cshtml & _unauth.cshtml.
Create an action that checks if the user is authenticated & returns the relevant partial i.e
public ActionResult FooContent()
{
if (User.Identity.IsAuthenticated)
{
return PartialView("_auth");
}
else
{
return PartialView("_unauth");
}
}
Then call the action from within your view like this:
#Url.Action("FooContent", "Contoller");
This can also be used to check roles and return different partials.
Yes, views can have logic.
Here's an example of code that displays different content to the user depending on if they are logged in or not.
#if (User.Identity.IsAuthenticated)
{
<p>Welcome, #User.Identity.Name!</p>
}
else
{
<p>Welcome, anonymous!</p>
}
I have a route:
routes.MapRoute("ResetPasswordConfirm", "reset-password-confirm", new { controller = "Membership", action = "ResetPasswordConfirm" });
and the code
public ActionResult ResetPasswordConfirm(int userid, string key)
{
// ...
}
in my application. So that i have url to be executed like this:
http://localhost/reset-password-confirm?userid=1&key=bla_bla_something
That is absolutely okay, until someone decides to go to
http://localhost/reset-password-confirm
...and look what will happen. ASP.NET will generate predictable error:
The parameters dictionary contains a null entry for parameter 'userid' of non-nullable type 'System.Int32'...
It could be done also by a search robot trying to grab all the possible urls. It's okay, but generates a lot of errors during usage of application in the wild. I want to avoid that, but feel uncomfortable with writing a stub for every possible case for such kind of errors.
Is there any elegant way of doing that? Thanks.
Another way is to handle global errors, just set <customErrors mode="On"></customErrors> on your web.config and create an Error.cshtml on your Shared view folder. MVC3 templates actually include that page.
On the other hand, if you want to be more specific, you should try Action Filters, that's a cool way to handle errors.
[HandleError(View = "YourErrorView", ExceptionType=typeof(NullReferenceException))]
public ActionResult ResetPasswordConfirm(int? userid, string key)
{
if (!userid.HasValue)
throw new NullReferenceException();
// ...
}
Use nullables for your parameters, i.e.:
public ActionResult ResetPasswordConfirm(int? userid, string key)
I am new to MVC, so hopefully my question will be straight forward. I am thinking of a scenario where the user submits a form (that is a partial view) and it undergoes server validation. I am wondering how I will know the result of the validation on the client side (javascript) after the form is submitted. For example, if validation fails I will obviously want to return the partial view again with validation messages set, but if it passes validation I may not necessarily want to return the partial view. I may want to return a json object with a message or hide a div or something. I want to be able to determine the validation result on the client. Is something like that possible? Or can I approach this a different way?
The tricky part with AJAX is that the client and server both have to agree on what's supposed to come back from the server in any circumstance. You have a few options:
Your server will always return HTML, and jQuery will always replace the editor content with the HTML that comes back. If the model is invalid, you return a PartialView result. If the model is valid, you return a <script> tag that tells the page what it needs to do (e.g. close a dialog, redirect to a different page, whatever). jQuery will automatically run any script it finds in the results when it tries to insert them into the DOM.
Your server will always return a JSON object representing what happened. In this scenario, your client-side javascript code has to be complex enough to take the results and modify your page to match. Under normal circumstances, this will mean that you don't get to take advantage of MVC's validation features.
Same as 2, except that you use a custom utility method to render the partial view you want into a string, and you make that entire string part of the JSON that comes back. The javascript code then just has to be smart enough to check whether the JSON shows a valid or invalid result, and if the result is valid, replace the contents of your editor area with the partialview HTML that is returned as part of the JSON object you got back.
Same as 3, except you develop an event-based architecture where all your AJAX requests will always expect to get back a JSON object with one or more "events" in it. The AJAX code can then be consolidated into one method that hands the events off to an Event Bus. The event bus then passes the event information into callbacks that have "subscribed" to these events. That way, depending on what kind of events you return from the server, you can have different actions occur on the client side. This strategy requires a lot more up-front work to put in place, but once it's done you can have a lot more flexibility, and the client-side code becomes vastly more maintainable.
Partial views would not have a Layout page. You may use this code to check if the view is rendered as partial view.
#if (String.IsNullOrWhiteSpace(Layout))
{
// Do somthing if it is partial view
}
else
{
// Do somthing if it is full page view
}
If you are using the MVC Data Annotations for validating your Model, then the controller will have a property called ModelState (typeof(ModelStateDictionary) which as a property of IsValid to determine if your Model passed into the Controller/Action is valid.
With IsValid you can return a Json object that can tell your Javascript what to do next.
Update
Here is a really basic example (USES jQuery):
[SomeController.cs]
public class SomeController : Controller
{
public ActionResult ShowForm()
{
return View();
}
public ActionResult ValidateForm(MyFormModel FormModel)
{
FormValidationResults result = new FormValidationResults();
result.IsValid = ModelState.IsValid;
if (result.IsValid)
{
result.RedirectToUrl = "/Controller/Action";
}
this.Json(result);
}
}
[FormValidationResult.cs]
public class FormValidationResults
{
public bool IsValid { get; set; }
public string RedirectToUrl { get; set; }
}
[View.js]
$(document).ready(function()
{
$("#button").click(function()
{
var form = $("#myForm");
$.ajax(
{
url: '/Some/ValidateForm',
type: 'POST',
data: form.serialize(),
success: function(jsonResult)
{
if (jsonResult.IsValid)
{
window.location = jsonResult.RedirectToUrl;
}
}
});
});
});
The populated catList is always Count=0 when the code jumps to CreateProduct() so I take it it does not get delivered.
Considering RouteValueDictionary does not do this ? Any other way?
public ActionResult GetCats(int CatID)
{
List<Category> catList = new List<Category>();
if (CatID >= 0 )
{
catList = context.Categories.Where(x => x.PCATID == CatID).ToList();
}
return RedirectToAction("CreateProduct", "Admin", new { catList });
}
public ActionResult CreateProduct(List<Category> catList) { }
You are actually trying to use controllers to do data access.
Move the "GetCats" data retrieval into your business layer (Service object, Repository, whatever suits you).
Then, CreateProduct will need to be there twice (2 signatures). One with no parameters in which you are going to call "GetCats" from your business layer and send it to the view.
The other implementation is going to be the flagged with the HttpPostAttribute and will contain in parameters all the necessary information to create a cat.
That's all. Simple and easy.
You could place any items that you need in TempData then call RedirectToAction.
RedirectToAction simply returns a "302" code to the browser with the URL to redirect to. When this happens your browser performs a GET with that URL.
Your RouteValues need to be simple. You can't really pass complex object or collections using a route value.
if you don't care about the browser url changing you could just
return CreateProduct(catList)