Change class in layout file mvc4 - c#

I have a layout page which consists of the standard Header and Footer and the RenderBody() function. My Header consists of a navigation with big buttons across the top as modules in the system
Customer Quote Jobs etc
My question is I want to be able to give these buttons a class of active on load depending on which has been clicked. Do I need to do this in javascript or is there anyway I can pass model data or some variable to the layout file to set this?

I use the following function to achieve this:
public static string MarkActive<TModel>(this HtmlHelper<TModel> html,string url)
{
if (html.ViewContext.HttpContext.Request.Url.LocalPath
.ToLower().StartsWith(url.ToLower().Trim()))
return "active";
else
return null;
}
It is an extension method I keep in a HtmlExtensions.cs file. I use it like this:
<ul>
<li class="#Html.MarkActive("/home")">Home</li>
<li class="#Html.MarkActive("/microsite">View Microsite</li>
<li class="#Html.MarkActive("/settings")">Settings</li>
<li>Log out</li>
</ul>
There is probably a better solution, but this works for me. In short, you should examine the HttpContext of your page.

I use the following approach to highlight the selected menu item:
<ul class="menu">
#{Dictionary<string, string> menuList = new Dictionary<string, string>() {
{ "Customer", "Customer" },
{ "Quote", "Quote" },
{ "Jobs", "Jobs" },
{ "etc", "etc" },
};
}
#foreach (var item in menuList )
{
<li #(Request.ServerVariables["SCRIPT_NAME"].Contains(item.Key) ? " class=active" : string.Empty)>
#item.Value</li>
}
</ul>
Where item.Key for actions names, item.Value for corresponding name in menu.

Related

Build Menu Items From Controller

I am creating HTML Menus from controller. Menus are stored in database and I make html tag as below :
foreach (UIMenuModel item in list)
{
if (item.Controller != "Home")
{
string line = string.Format(#"<li><a asp-area=""{0}"" asp-controller=""{1}"" id=""{1}""
asp-action = ""{2}"" style = ""font-size:16px;;"" > {3} </a></li>", item.Area, item.Controller, item.Action, item.LinkText);
sb.Append(line);
}
}
which gives me below HTML :
<li><a asp-area="" asp-controller="CrossApproval" id="CrossApproval" asp-action="Index" style="font-size:16px;;"> Cross Approval </a></li>
Other Menu Item, Which is written in HTML itself, gives below HTML in browser.
<li><a id="CrossRequest" style="font-size:16px" href="/CrossRequest">Cross Request</a></li>
On UI, it looks perfect. However, I am not able to click and navigate to desired controller and action methods. Can someone please help me to identify while this anchor tag is not allowing me to navigate.
Use RazorLightEngine to convert plain string as rendered Razor string:
string content = "Hello #Model.Name. Welcome to #Model.Title repository";
var model = new
{
Name = "John Doe",
Title = "RazorLight"
};
var engine = new RazorLightEngine();
string result = engine.ParseString(content, model);
And then add it any place in razor view like encoded string
<div>
#Html.Raw(result)
</div>
As posted in Question, HTML with href was working fine. So, I decided to mimic the same behaviour from the controller and changed my code as below :
string line = string.Format(#"<li><a asp-area=""{0}"" id=""{1}"" href=""/{1}/{2}""
style=""font-size:16px"">{3}</a></li>", item.Area, item.Controller, item.Action, item.LinkText);
This generated a link which I can click and navigate.

POST <ul> with <li> elements from MVC view page

So I have a list, where useer can add any number of items. In other words, I have a <ul> and variable length of <li> elements.
Variable length <li> elements means, user adds <li> elements in runtime, if she/he is done, she/he submits the form. So I don't know how many <li> elements are there.
Something like this :
<form asp-controller="MyController" asp-action="AddList" method="post">
<ul id="myUL">
<li>item1</li>
<li>item2</li>
/*...*/
<li>itemN</li>
</ul>
<button type="submit" class="btn btn-default">New List</button>
</form>
I add <li> elements dynamically with JavaScript if user clicks a button, like this:
function newElement() {
var li = document.createElement("li");
var inputValue = document.getElementById("myInput").value;
var t = document.createTextNode(inputValue);
li.appendChild(t);
document.getElementById("myUL").appendChild(li);
}
My controller's method (AddList) looks like this:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> NewShoppingList(object list)
{
// I don't know how to accept that list
}
But this way my parameter list is null. How can I get that <ul> with some <li> elements?
ul and li are not submittable form elements.You can try the following.
I'm not sure what your view model looks like, but it seems like a list of string values? So then it will look like this in your view model:
public class YourViewModel
{
public List<string> Items { get; set; }
}
In your view try the following:
<ul id="myUL">
<li>item1</li>
<li>item2</li>
/*...*/
<li>itemN</li>
</ul>
function addHidden(theLi, key, value) {
// Create a hidden input element, and append it to the li:
var input = document.createElement('input');
input.type = 'hidden';
input.name = key;'name-as-seen-at-the-server';
input.value = value;
theLi.appendChild(input);
}
function newElement() {
var li = document.createElement("li");
var inputValue = document.getElementById("myInput").value;
var t = document.createTextNode(inputValue);
li.appendChild(t);
addHidden(li, 'your-model-propertyName' + li-Count+1, inputValue );
document.getElementById("myUL").appendChild(li);
}
When you post then these items should still be in the list.
[HttpPost]
[AllowAnonymous]
//[ValidateAntiForgeryToken]
public async Task<IActionResult> NewShoppingList(List<string> list)
{
// I don't know how to accept that list
}
If you dont like add hidden field for each li , you can get all li items and send thems with Ajax.
like this:
function sendData() {
var items = [];
$("#myUL li").map(function () {
items.push(this.innerText);
});
$.ajax({
type: "POST",
data: {
list: items
},
url: "/Home/NewShoppingList",
success: function (res) {
}
}
I hope this helps.
You should avoid posting the html, after all you'll probably need to parse it, save to db etc. As Jeremy Thomson suggested use some client side library - jQuery, Angular or what ever you want and do the posts of items using Ajax. You can post them on each user input, or keep them in mvc model, javascript object etc. and post them at once.
example just to get the idea:
MVC - Binding dynamically added controls to a List<T> in a Model

Redirect to Page but specific Tab

I have used this as an example of what I am trying to do.
I have a tabbed page.
In my Controller I want to redirect to a page but a specific tab on that page.
Now when I am on that page, and hover over the tab the link is http://localhost:xxxxx/OInfoes/Details/2#phases
So in my controller I did this to try and recreate the same link:
return new RedirectResult(Url.Action("Details", "OInfoes", new { id = phase.OID }) + "#phases");
This gives me the correct link but it doesn't put me on the correct tab.
How do I get this to work?
My HTML for the tabs is below:
<ul class="nav nav-tabs">
<li class="active">Information</li>
<li>Previous Reports</li>
<li>Previous/Current Phases</li>
</ul>
You should update your action method to take a parameter to represent the tab to be selected.
public ActionResult Details(int id,string tab="")
{
if (id != null)
ViewBag.ActiveTab = id.ToString();
// to do : Return a view
}
When you return the redirect result, send a querystring with name tab
return new RedirectResult(Url.Action("Details", "OInfoes", new { id = phase.OID ,
tab="phases"}));
Now in your document ready, use the value of ViewBag.ActiveTab value, generate the jQuery selector from that and use that to call .tab('show').
$(function () {
var selector = '#ViewBag.ActiveTab';
if(selector)
{
$("#link-tab"+selector).tab('show');
}
});
Make sure your tab's has id's matching with our above code.
<ul class="nav nav-tabs">
<li class="active">Info</li>
<li>Prev Reports</li>
<li>Prev/Cur Phases</li>
</ul>

Reloading Partial View with JQuery

I have a page with a video at the top and a list of videos you can choose from. Currently, clicking a link in the video list will reload the entire page. I need it to only refresh the partial view I have containing the video at the top of the page.
I saw several posts here on SO showing how to reload partial views with JQuery, but couldn't get it to work correctly in my situation. I'm unsure how to pass the correct id of the video along.
Controller:
public ActionResult Videos(int topVideo = 0)
{
VideosModel model = new VideosModel();
model.Videos = StatsVideoService.GetEntityList(new Lookup(TableStatsVideo.IsDeleted, false)).OrderByDescending(x => x.DateCreated).ToList();
if (topVideo == 0)
model.TopVideo = model.Videos.First();
else
{
model.TopVideo = model.Videos.Where(x => x.StatsVideoId == topVideo).FirstOrDefault();
if (model.TopVideo == null)
model.TopVideo = model.Videos.First();
}
return View(model);
}
View:
#model Project.Models.VideosModel
<section class="videos">
<div id="top_video">
#{Html.RenderPartial("StatsVideo", Model.TopVideo);}
</div>
<ul>
#foreach (var item in Model.Videos)
{
<li>
<div class="videoList">
<a href ="#Url.Action("Videos", "Home", new { topVideo = item.StatsVideoId })">
<img src="#Url.Content("~/Content/img/video-ph.png")" />
</a>
<p class="videoTitle">#item.Title</p>
</div>
</li>
}
</ul>
</section>
If there's any more information needed, please let me know.
After several hours of bashing my head against the wall, I got it to work! Just as a reference to anyone else in the future who's viewing this article, here's how I got it to work:
I set the onclick of the link to point to a javascript method, passing in the id of the video as a parameter:
#foreach (var item in Model.Videos)
{
<li>
<div class="videoList">
<a href ="#" onclick="updateTopVideo(#item.StatsVideoId)">
<img src="#Url.Content("~/Content/img/video-ph.png")" />
</a>
<p class="videoTitle">#item.Title</p>
</div>
</li>
}
And then I included this script in the view at the bottom:
<script>
var updateTopVideo = function (itemId) {
var url = '#Url.Content("~/Home/StatsVideo/")';
url = url + itemId;
$.get(url, "", callBack, "html");
};
var callBack = function (response) {
$('#top_video').html(response);
};
</script>
Finally, I added a method to my controller that would return the partial view needed for the video at the top of the screen:
public ActionResult StatsVideo(int Id)
{
IStatsVideo vid = StatsVideoService.GetEntity(new Lookup(TableStatsVideo.StatsVideoId, Id));
if (vid == null)
vid = StatsVideoService.GetEntityList(new Lookup(TableStatsVideo.IsDeleted, false)).OrderByDescending(x => x.DateCreated).FirstOrDefault();
return PartialView(vid);
}
This code should be fairly easy to understand. Basically, the onclick calls the first javascript method, which then calls the controller. The controller builds the partial view and returns it. The first javascript method passes it to the second javascript method which sets the html of the div "top_video" to be the returned partial view.
If anything doesn't make sense, or anyone's having trouble with this in the future, let me know and I'll do my best to offer some help.
I think there may be several confusing and inconsistent elements here.
First, you are returning a full view instead of a partial view. This reloads all containing elements, not just the part that is relevant to your partial view.
Second, you are using Url.Action, which only generates the url. I would recommend using Ajax.ActionLink, which allows you to do fully ajax calls, refreshing the content of your partial div and updating a target div element.
instead of:
<div class="videoList">
<a href ="#Url.Action("Videos", "Home", new { topVideo = item.StatsVideoId })">
<img src="#Url.Content("~/Content/img/video-ph.png")" />
</a>
<p class="videoTitle">#item.Title</p>
</div>
try the more modern solution
<div class="videoList">
#Ajax.ActionLink(
"Videos",
"Home",
"new { topVideo = item.StatsVideoId },
new AjaxOptions {
HttpMethod = "GET",
OnSuccess = "handleSuccess"
}
)
</div>
This way you can be very specific on what you want each link to do, and you can pass along multiple parameters as well as define a callback function. You can also use "UpdateTargetId" in your ajax options to load your newly refreshed partial view into a DOM element.
You can remove the around the image and just store the url generated by the Url.Action in a data-href attribute.
Then you can use the jquery load method to load the data:
$(".videolist>img").click(function () {
$("#content").load($(this).data("href"));
});
I created a fiddle that loads content dynamically here, so you can play with it if you want: http://jsfiddle.net/bTsLV/1/

How to manipulate Html.ActionLink to show a link to another controller?

In the Details view of the HomeController, I'd like to create a link to the Email view on the MiscController. In addition, I need to add an item to the QueryString.
I'd like to create a link that goes something like:
<a href="http://www.blah.com/misc/SendMail?id=6">
<font size="1">Report problems</font>
</a>
I've tried the following:
<% Html.ActionLink("<font size=\"1\">Report</font>", "SendMail", "Misc", Model.ImageID, new object()); %>
It returned no link. What am I missing?
First of all, you missed the = after the <%. That's why it didn't output anything.
Also, the way you passed routeValues parameter was wrong.
It should be :
<%=Html.ActionLink("<font size=\"1\">Report</font>", "SendMail", "Misc",
new { id = Model.ImageID }, null /*htmlAttributes*/) %>
Please keep in mind though the text argument will be encoded in the output, so there's no point in sending HTML with that argument.
It's best to use CSS to style your HTML.
For example :
a.myLink {font-size: 0.5em;color:yellow;}
And to set the class attribute for the anchor element :
<%=Html.ActionLink("Report", "SendMail", "Misc",
new { id = Model.ImageID }, new { #class = "myLink" }) %>

Categories

Resources