MVC 4 theme switching with Ajax.ActionLinks - asp.net-mvc-4

The full text of this question is available with a screenshot here
Thanks for any help - original post follows:
So I downloaded the MvcMusicStore and fired up the completed project. I read all the articles talking about extending the view engine and using jquery plugins but I wanted to believe it could be simpler than that to just change the CSS file path when a link gets clicked. Mainly because I didn't want to copy code verbatim that I didn't fully understand. I'm very new to MVC.
So this is what I did:
To HomeController.cs I added:
public ActionResult Theme(string themeName)
{
ViewBag.Theme = ThemeModel.GetSetThemeCookie(themeName);
return View();
}
to Models I added this class:
public class ThemeModel
{
public static string GetSetThemeCookie(string theme)
{
HttpCookie cookie = HttpContext.Current.Request.Cookies.Get("userTheme");
string rv = "Blue";
if (theme != null)
rv = theme;
else
{
if (cookie != null)
rv = cookie["themeName"];
else
rv = "Blue";
}
cookie = new HttpCookie("userTheme");
HttpContext.Current.Response.Cookies.Remove("userTheme");
cookie.Expires = DateTime.Now.AddYears(100);
cookie["themeName"] = rv;
HttpContext.Current.Response.SetCookie(cookie);
return rv;
}
}
I then created 2 copies of Site.css, changing only the background color and font-family and a view to generate my link tag.
<link href="#Url.Content(string.Format("~/Content/{0}.css", ViewBag.Theme))" rel="stylesheet" type="text/css" />
Finally, I made these changes to my _Layout.cshtml.
<!DOCTYPE html>
<html>
<head>
<title>#ViewBag.Title</title>
#if (ViewBag.Theme == null) {Html.RenderAction("Theme", "Home");}
<script src="#Url.Content("~/Scripts/jquery-1.4.4.min.js")"
type="text/javascript"></script>
</head>
<body>
<div id="header">
<h1>ASP.NET MVC MUSIC STORE</h1>
<ul id="navlist">
<li class="first">Home</li>
<li>Store</li>
<li>#{Html.RenderAction("CartSummary", "ShoppingCart");}</li>
<li>Admin</li>
</ul>
</div>
#{Html.RenderAction("GenreMenu", "Store");}
<div id="main">
#RenderBody()
</div>
<div id="footer">
Themes: #Ajax.ActionLink("Coral", "Theme", "Home", new { themeName = "Coral" }, null, new { #style = "color : coral"} )
#Ajax.ActionLink("Blue", "Theme", "Home", new { themeName = "Blue" }, null, new { #style = "color : blue;"})
</div>
</body>
</html>
When I run the app I get the general layout rendered twice. Once with only the genre menu rendered on the left and nothing in the body. And then again with the top 5 albums. I can't post the image as I don't have enough rep.
When I click my Coral and Blue links, my theme changes and I get just the one set without the top 5 albums.
So after some more reading on here I tried this:
_Layout.cshtml:
#{Html.RenderAction("Theme", "Home");}
HomeController.cs
public ActionResult Theme(string themeName)
{
ViewBag.Theme = ThemeModel.GetSetThemeCookie(themeName);
return PartialView();
}
But even though this stops the duplicate rendering, when I click the theme link, the colour changes but I get absolutely nothing else on the page.
Well and truly flummoxed now and could really use some help.
Cheers,
.pd.

Okay - here's how I did it in the end.
Create a javascript file. Mine's called master.js:
function ajaxSuccSetTheme(theme) {
$('#linkTheme').attr('href', '/Content/' + theme + '.css');
}
Modify the _Layout.cshtml:
#{
if (ViewBag.Theme == null) {
ViewBag.Theme = MvcMusicStore.Models.ThemeModel.GetSetThemeCookie();
}
}
<link id="linkTheme" href="#Url.Content(string.Format("~/Content/{0}.css", ViewBag.Theme))" rel="stylesheet" type="text/css" />
<script src="#Url.Content("~/Scripts/jquery-2.0.3.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/master.js")" type="text/javascript"></script>
Notes on this:
The first time the page loads Theme will not have been written to the ViewBag
Give the <link> tag the same ID as the jQuery selector in your js file above
Update unobtrusive ajax jQuery file to the same version as your jQuery lib. Your Ajax.ActionLink won't work without it.
Then my theme switching links in _Layout.cshtml look like this:
<div id="footer">
Themes :
#Ajax.ActionLink("Coral", "Theme", "Home", new { themeName = "Coral" },
new AjaxOptions { HttpMethod = "POST", OnSuccess = string.Format("ajaxSuccSetTheme('{0}');", "Coral")},
new { #style = "color : coral;" }) |
#Ajax.ActionLink("Blue", "Theme", "Home", new { themeName = "Blue" },
new AjaxOptions { HttpMethod = "POST", OnSuccess = string.Format("ajaxSuccSetTheme('{0}');", "Blue")},
new { #style = "color : blue;" })
</div>
Notes on that:
themeName = "whatever" is the argument to your Theme Controller method. this gets passed to the cookie method in the ThemeModel
method = POST so IE doesn't cache it and I've read a couple other questions that got solved by not doing a GET
you have to kludge your own args to the OnSuccess js callback
Next the HomeController.cs change:
public ActionResult Theme(string themeName)
{
ViewBag.Theme = ThemeModel.GetSetThemeCookie(themeName);
if (Request.IsAjaxRequest())
{
return PartialView();
}
else
{
return null;
}
}
Honestly, it doesn't matter if you just return null without checking for IsAjaxRequest() cuz all we need from this is to set the cookie so it remembers when you next login.
Which just leaves the cookie setting method in the ThemeModel:
public class ThemeModel
{
public static string GetSetThemeCookie(string theme = null)
{
HttpCookie cookie = HttpContext.Current.Request.Cookies.Get("userTheme");
string rv = "Blue";
if (theme != null)
rv = theme;
else
{
if (cookie != null)
rv = cookie["themeName"];
else
{
cookie = new HttpCookie("userTheme");
rv = "Blue";
}
}
cookie.Expires = DateTime.Now.AddYears(100);
cookie["themeName"] = rv;
HttpContext.Current.Response.SetCookie(cookie);
return rv;
}
}
Hope I helped somebody. If you'd rather do it all in jQuery here's Tim Vanfosson's Theme Manager jQuery Plugin
Cheers,
.pd.

Related

Highlight a day with an icon or color in DateTimePicker. (MVC and Bootstrap)

I have a table like the picture below.
I would like the value in the Date column to be highlight with a circle in the
DateTimePicker (or in specific color).
Is it possible ? The best way ? Any examples ?
Thank you
P.s. I use MVC and Bootstrap 3
Copy this in Controller
public ActionResult GetArrayofDates()
{
DateTime[] d = new DateTime[]
{
new DateTime(2019,9,27),
new DateTime(2019,9,25),
new DateTime(2015,7,27),
new DateTime(2019,5,5)
};
return View(d);
}
And This is The View
#{
ViewBag.Title = "GetArrayofDates";
}
<link href="~/Content/themes/base/jquery-ui.min.css" rel="stylesheet" />
<style>
.highlight {
background-color: #ff0000 !important;
color: #ffffff !important;
}
.nothighlight{
background-color:#fff7f7;
color:#000000;
}
</style>
<h2>GetArrayofDates</h2>
#{
for (int i = 0; i < Model.Length; i++)
{
<h3>#Model[i]</h3>
}
}
<div id="calandar">
</div>
<script src="~/Scripts/jquery-3.3.1.min.js"></script>
<script src="~/Scripts/jquery-ui-1.12.1.js"></script>
<script>
var dates = []
#foreach (var item in Model)
{
//#: allow you to write javascritp/hhtml code inside c# code which you specific using # which allow write c# inside javascrpit/html
#:dates.push('#item.ToString("yyyy-M-dd")');
}
console.log(dates);
console.log(dates["0"])
$("#calandar").datepicker({
todayHighlight: true,
changeYear: true,
changeMonth: true,
minDate: new Date(2010,1,1),
beforeShowDay: function (date) {
var calender_date = date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + ('0' + date.getDate()).slice(-2);
console.log(calender_date)
var search_index = $.inArray(calender_date, dates);
if (search_index > -1) {
return [true,'highlight','Employee Worked on this day.' ];
} else {
return [true, 'nothighlight', 'Employee did not Work on this day.'];
}
}
});
</script>
This full working example makes sure to match the format of the Date sent from the server and put into array as I did and everything will be okay.
and Download Jquery UI, and use UI CSS, I suppose you know that

How to fix Invalid field or parameter url in SP.Executor.js in people picker Sharepoint Provided-Hosted App

#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<!-- IE9 or superior -->
<meta http-equiv="X-UA-Compatible" content="IE=9">
<title>People Picker HTML Markup</title>
<!-- Widgets Specific CSS File -->
<link rel="stylesheet"
type="text/css"
href="../Scripts/Office.Controls.css" />
<!-- Ajax, jQuery, and utils -->
<script src="~/Scripts/MicrosoftAjax.js"></script>
<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.10.2.min.js"></script>
<script type="text/javascript">
// Function to retrieve a query string value.
// For production purposes you may want to use
// a library to handle the query string.
function getQueryStringParameter(paramToRetrieve) {
var params =
document.URL.split("?")[1].split("&");
var strParams = "";
for (var i = 0; i < params.length; i = i + 1) {
var singleParam = params[i].split("=");
if (singleParam[0] == paramToRetrieve)
return singleParam[1];
}
}
</script>
<!-- Cross-Domain Library and Office controls runtime -->
<script type="text/javascript">
//Register namespace and variables used through the sample
Type.registerNamespace("Office.Samples.PeoplePickerBasic");
//Retrieve context tokens from the querystring
Office.Samples.PeoplePickerBasic.appWebUrl =
decodeURIComponent(getQueryStringParameter("SPAppWebUrl"));
Office.Samples.PeoplePickerBasic.hostWebUrl =
decodeURIComponent(getQueryStringParameter("SPHostUrl"));
//Pattern to dynamically load JSOM and and the cross-domain library
var scriptbase =
Office.Samples.PeoplePickerBasic.hostWebUrl + "/_layouts/15/";
//Get the cross-domain library
$.getScript(scriptbase + "SP.RequestExecutor.js",
//Get the Office controls runtime and
// continue to the createControl function
function () {
$.getScript("../Scripts/Office.Controls.js", createControl)
}
);
</script>
<!--People Picker -->
<script src="../Scripts/Office.Controls.PeoplePicker.js"
type="text/javascript">
</script>
</head>
<body>
Basic People Picker sample (HTML markup declaration):
<div id="PeoplePickerDiv"
data-office-control="Office.Controls.PeoplePicker">
</div>
<script type="text/javascript">
function createControl() {
//Initialize Controls Runtime
Office.Controls.Runtime.initialize({
sharePointHostUrl: Office.Samples.PeoplePickerBasic.hostWebUrl,
appWebUrl: Office.Samples.PeoplePickerBasic.appWebUrl
});
//Render the widget, this must be executed after the
//placeholder DOM is loaded
Office.Controls.Runtime.renderAll();
}
</script>
</body>
</html>
I want to create a people picker function in SharePoint Provider-Hosted app. I tried this tutorial: https://msdn.microsoft.com/en-us/library/office/dn636915.aspx
I'm stuck on this error.
Invalid field or parameter url in SP.Executor.js
Check whether your SP.RequestExecutor.js is loaded successfully or not if not then you can give the path of the same and load it directly or you can use the below code to get the SP.RequestExecutor.js
var scriptbase = hostweburl + "/_layouts/15/";
$.getScript(scriptbase + "SP.Runtime.js",
function () {
$.getScript(scriptbase + "SP.js",
function () { $.getScript(scriptbase + "SP.RequestExecutor.js", createControl); }
);
}
);
Hope this will resolve your issue.
This solved my error. Thanks to #Rahul for the hint
var scriptbase = hostWebUrl + "/_layouts/15/";
$.getScript(scriptbase + "SP.Runtime.js",
function () {
$.getScript(scriptbase + "SP.js",
function () { $.getScript(scriptbase + "SP.RequestExecutor.js",
$.getScript("../Scripts/Office.Controls.js", createControl));
}
);
}
);

Show alert after action in mvc3/4

I need to inform user about success/failure of clicked operation. In view I've prepared action link that goes to controller, performs database operation and returns with result. Then I'd like to show alert with message "done" or "failure". Everything should be done without reloading page. I've tried to define #Ajax.ActionLink and text/javascript function but it doesn't work at all... Please help. Thanks in advance.
Robert
Firstly you should include scripts: jquery and jquery.unobtrusive-ajax like:
<head>
...
<script src="#Url.Content("~/Scripts/jquery-1.8.2.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>
</head>
Inside View:
<script type="text/javascript">
function myCallback(data) {
if(!data.Success)
{
alert(data.ErrorMessage);
}
else
{
alert("id: " + data.Id);
}
}
</script>
#Ajax.ActionLink("Actionlink", // <-- Text to display
"GetDeneme", // <-- Action Method Name
new { id = "2"},
new AjaxOptions
{
HttpMethod = "POST", // <-- HTTP method
OnSuccess = "myCallback"
})
In Controller:
public ActionResult GetDeneme(string id)
{
var error = (id == "3");
if (error)
{
return Json(new { Success = false, ErrorMessage = "Error!" });
}
return Json(new { Success = true, Id = id });
}

Customising xlviewer.aspx

I'm trying to customise the xlviewer.aspx page of SharePoint to remove the 'Open in Excel' button and potentially replace it with the 'Download a Snapshot', has anyone else tried to do this and made any progress?
Adding the following Javascript to xlviewer.aspx allowed me to remove the open in excel buttons
</script>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script type="text/javascript">
$(document).ready(function ()
{
setTimeout(HideOpenInExcelRibbonButton, 10);
});
function HideOpenInExcelRibbonButton()
{
$('a[id*="openInExcel"]').hide();
var doc = document.getElementsByTagName('ie:menuitem');
for (var i = 0; i < doc.length; i++)
{
itm = doc[i];
if (itm.id.match("OpenInExcel")!=null)
{ itm.hidden=true; }
}
setTimeout(HideOpenInExcelRibbonButton, 10);
}
</script>

Rally Cardboard for Stories by Project

Im trying to create a Cardboard in Rally to show Stories assigned to specific project (within a given release).
We use the project field to identify which of our three scrum team are working a specific story. I would like a board style display to allow me to move stories from team to team quickly, and to show a list of whats on each teams plate for a given release.
I came up with the following custom HTML App:
function cardboardOnLoad(cardboard, args) {
var items = args.items;
var itemsByType = cardboard.getItems(null, "Defect");
var itemsByState = cardboard.getItems("Accepted");
var itemsByTypeAndState = cardboard.getItems("Backlog", "Defect");
}
function onLoad() {
var rallyDataSource = new rally.sdk.data.RallyDataSource('__WORKSPACE_OID__',
'__PROJECT_OID__', '__PROJECT_SCOPING_DOWN__');
var cardboardConfig = {
types : ["Defect", "HierarchicalRequirement"],
attribute: "Project",
fetch : "Name,FormattedID,Owner,ObjectID",
query : 'Release.Name = "RI 3.1.0"',
order : 'Rank'
};
var cardboard = new rally.sdk.ui.CardBoard(cardboardConfig, rallyDataSource);
cardboard.addEventListener(cardboard.getValidEvents().onLoad, cardboardOnLoad);
cardboard.display("cardboard");
}
rally.addOnLoad(onLoad);
Only Problems is that it doesn't actually show my stories... just the project column names...
It stories are shown if I change the attribute value to "ScheduleState", but not for "Project", and im not sure why...
Any help would be appreciated.
Thanks.
This requires a slightly advanced usage of the cardboard where the columns are queried for manually. The comments above are correct in that you can run into some strange project scoping behavior otherwise.
The following app will build a board for all direct child projects of the currently scoped project.
Assuming you have a project hierarchy like so:
Project 1
+--Project 2
+--Project 3
+--Project 4
The board will contain columns Project 2, Project 3 and Project 4 when scoped to Project 1.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<title>Release Project Board</title>
<meta name="Name" content="Release Project Board" />
<script type="text/javascript" src="/apps/1.32/sdk.js"></script>
<script type="text/javascript">
var rallyDataSource;
var cardBoard;
var releaseDropdown;
function onLoad() {
rallyDataSource = new rally.sdk.data.RallyDataSource('__WORKSPACE_OID__',
'__PROJECT_OID__',
'__PROJECT_SCOPING_UP__',
'__PROJECT_SCOPING_DOWN__');
releaseDropdown = new rally.sdk.ui.ReleaseDropdown({}, rallyDataSource);
releaseDropdown.addEventListener("onLoad", findProjects);
releaseDropdown.addEventListener("onChange", onReleaseChanged);
releaseDropdown.display("release");
}
function onReleaseChanged(rd, args) {
var config = cardboard.getConfiguration();
config.query = releaseDropdown.getQueryFromSelected();
cardboard.refresh(config);
}
function findProjects() {
rallyDataSource.find({
key: "projects",
type: "project",
query: new rally.sdk.util.Query('Parent = /project/__PROJECT_OID__'),
fetch: true
}, onProjectsRetrieved);
}
function onProjectsRetrieved(results) {
var columns = {};
rally.forEach(results.projects, function(project) {
columns[rally.sdk.util.Ref.getRelativeRef(project)] = {
displayValue: project.Name
};
});
var cardboardConfig = {
types : ["Defect", "HierarchicalRequirement"],
attribute: "Project",
fetch : "Name,FormattedID,Owner,ObjectID,Project",
query: releaseDropdown.getQueryFromSelected(),
columns: columns
};
cardboard = new rally.sdk.ui.CardBoard(cardboardConfig, rallyDataSource);
cardboard.display("cardboard");
}
rally.addOnLoad(onLoad);
</script>
</head>
<body>
<div id="release"></div>
<div id="cardboard"></div>
</body>
</html>