I use a JavaScript library called Alertify to show simple, pretty messages, like "note created", or whatever. Often I want to send the user to a new page and then show an Alertify message.
I'm trying to find a way to do this on the fly, without tons of configuration.
Currently I do this by creating an AlertifyMessages
object:
public class AlertifyMessages
{
public string LogMessage { get; set; }
public string LogCallback { get; set; }
public string SuccessMessage { get; set; }
public string SuccessCallback { get; set; }
public string ErrorMessage { get; set; }
public string ErrorCallback { get; set; }
public AlertifyMessages() { }
public AlertifyMessages(string logMessage = null, string logCallback = null,
string successMessage = null, string successCallback = null,
string errorMessage = null, string errorCallback = null)
{
LogMessage = logMessage;
LogCallback = logCallback;
SuccessMessage = successMessage;
SuccessCallback = successCallback;
ErrorMessage = errorMessage;
ErrorCallback = errorCallback;
}
}
and passing it to an action method:
public ActionResult Test()
{
// RedirectToAction([action], [controller], [routeValues])
return RedirectToAction("Details", "Notes",
new AlertifyMessages(logMessage: "Lorem ipsum"));
}
and in the destination action method:
public class NotesController : Controller
{
// GET
public ActionResult Details(AlertifyMessages alertify)
{
return View(new NoteDetailsViewModel(alertify));
}
}
which, finally, displays the messages in the view:
@if (Model.Alertify.LogMessage != null)
{
@:@Html.Alertify("log", Model.Alertify.LogMessage, Model.Alertify.LogCallback)
}
@if (Model.Alertify.SuccessMessage != null)
{
@:@Html.Alertify("success", Model.Alertify.SuccessMessage, Model.Alertify.SuccessCallback)
}
@if (Model.Alertify.ErrorMessage != null)
{
@:@Html.Alertify("error", Model.Alertify.ErrorMessage, Model.Alertify.ErrorCallback)
}
The helper method Alertify
just adds the JavaScript to call alertify.js, but you can see it here
Besides being a nightmare in so many ways, this doesn't even work unless the AlertifyMessage
is all I need to pass. I could make some kind of complicated ToString()
override to resolve this, but otherwise attempting to pass, say, an ID as well as an AlertifyMessages
like so
return RedirectToAction("Details", "Notes",
new { id = 1, alertify = new AlertifyMessages(logMessage: "Lorem ipsum")});
yields a URL like
https://localhost/Notes/Details/1?alertify=Project.Namespace.AlertifyMessages
This is way too complicated. There must be a better way.
Edit: I'd like a way to specify the message in the calling action method (the one performing the redirect), and nowhere else. Is there a way to do this other than adding code to every single method and view?
2 Answers 2
You may start making your model simpler:
public sealed class AlertifyMessageModel
{
public string Message { get; set; }
public string Callback { get; set; }
}
Note that you do not need all those ugly ctor parameters:
public ActionResult Test()
{
return RedirectToAction("Details", "Notes",
new AlertifyMessageModel { Message = "Lorem ipsum" });
}
You may also want to use nameof(Notes.Details)
instead of hard-coded "Details"
string. Usually I also have an helper method GetControllerName(nameof(NotesController))
or a static field NotesController.Name
but it's verbose and not everyone likes it.
Now let's go to your View. We need to add a property to the model to hold the type of notification:
public sealed class AlertifyMessageModel
{
public string Message { get; set; }
public string Callback { get; set; }
public string Type { get; set; } = "log"
}
Then we can simply write:
if (Model.Alertify.Message != null)
{
@:@Html.Alertify(Model.Alertify.Type, Model.Alertify.Message, Model.Alertify.Callback)
}
Next step is to change Type
to enum
:
public NotificationType Type { get; set; } = NotificationType.Log;
For your URL problem you should change something more, you may serialize the whole object as JSON and post it in the request body instead of in the URL. I don't see code around that then I can't really suggest a nice method. Note that you may also override ToString()
to return a parseable representation of AlertifyMessageModel
(think about type converters).
-
\$\begingroup\$ Could you elaborate on that last bit? What code would you need to see? \$\endgroup\$Sinjai– Sinjai2017年08月30日 13:55:40 +00:00Commented Aug 30, 2017 at 13:55
-
\$\begingroup\$ The reason
AlertifyMessages
has so many properties is that there could theoretically be more than one message. Although that limits me to one of each, so if you have a better way, I'd love to hear it. Also, please see my edit. \$\endgroup\$Sinjai– Sinjai2017年08月30日 14:03:46 +00:00Commented Aug 30, 2017 at 14:03 -
\$\begingroup\$ How you would use
new { id = 1, alertify = new...
thing. If alert messages are always present you may consider to drop that anonymous class in favor of a typed modelclass NotesDetailsModel : AlertifyMessageModel
(BTW you should avoid anonymous models, if possible). If there might be many messages then simply use a collection of them! About the edit: that doesn't really fit CR if you do not already have some code. If you can make it clear (I can't understand the problem) it's a better question for SO. \$\endgroup\$Adriano Repetti– Adriano Repetti2017年08月30日 14:10:37 +00:00Commented Aug 30, 2017 at 14:10 -
\$\begingroup\$ If I understand your problem correctly (I'm not sure): if same code is present in many views/controllers then you should have a base controller class + _Layout.cshtml... \$\endgroup\$Adriano Repetti– Adriano Repetti2017年08月30日 14:13:08 +00:00Commented Aug 30, 2017 at 14:13
-
\$\begingroup\$ They're not always present, but I'd like there to always be an option to add one. There is a view model
NoteDetailsViewmodel
, but it is generated within the destination method by using the ID. Are you saying to pass that model toRedirectToAction
? What's an anonymous model? \$\endgroup\$Sinjai– Sinjai2017年08月30日 14:14:55 +00:00Commented Aug 30, 2017 at 14:14
One option is to use TempData, which is like ViewBag but not a part of the URL, and therefore only lasts for one request. A collection of AlertifyMessage
s can be added to a base controller class inherited by all other controllers. Then you can use action filters to transfer that property from the base class to TempData, and from TempData to the controller in the case of a redirect.
You end up with something like this:
public class AlertifyMessages
{
public List<AlertifyMessage> Messages { get; private set; } = new List<AlertifyMessage>();
public void Add(AlertifyType type, string message, string callbackUrl = null)
{
Messages.Add(new AlertifyMessage(type, message, callbackUrl));
}
}
public class AlertifyMessage
{
public AlertifyType Type { get; set; }
public string Message { get; set; }
public string CallbackUrl { get; set; }
public AlertifyMessage(AlertifyType type, string message, string callbackUrl)
{
Type = type;
Message = message;
CallbackUrl = callbackUrl;
}
}
public enum AlertifyType
{
Log,
Error,
Success
}
public class AlertifyAttribute : ActionFilterAttribute
{
private const string alertify = "Alertify";
public override void OnResultExecuting(ResultExecutingContext context)
{
BaseController bc = (BaseController)context.Controller;
bc.TempData[alertify] = bc.AlertifyMessages;
}
public override void OnActionExecuting(ActionExecutingContext context)
{
BaseController bc = (BaseController)context.Controller;
bc.Messages = (bc.TempData[alertify] == null) ? new AlertifyMessages() : (AlertifyMessages)bc.TempData[alertify];
// Faster? Better? Harder? Stronger?
//bc.Messages = (AlertifyMessages)bc.TempData[alertify] ?? new AlertifyMessages();
}
}
[Alertify]
public class BaseController : Controller
{
public BaseController()
{
AlertifyMessages = new AlertifyMessages();
}
public AlertifyMessages AlertifyMessages { get; set; }
}
Usage:
public class MyController : BaseController
{
public ActionResult Index()
{
AlertifyMessages.Add(AlertifyType.Success, "Yay!", Url.Action("Index"));
return View(/*new ViewModel()*/);
}
}
// Then loop through and display messages in view using TempData
More information about the redirect problem can be found here.
-
2\$\begingroup\$ @t3chb0t You're right, it was a bad answer, added only for my own sanity. I added considerably more details in an edit. \$\endgroup\$Sinjai– Sinjai2017年12月06日 06:45:31 +00:00Commented Dec 6, 2017 at 6:45
return RedirectToAction("Details", "Notes", new { id = 1, LogMessage = "...", LogCallback = "...", ErrorCallback = "..." });
\$\endgroup\$TempData
(although the message will not be displayed again if the user refreshes the browser, sinceTempData
lasts only one request) \$\endgroup\$