I have an ASP.NET Core MVC (with EF) site and I want administrators to modify some variables via admin panel. For example contact e-mail, counter section visibility, navigation menu visibility etc.
And most of the parameters are in _Layout.cshtml.
May I learn the best practice how to manage this issue.
For example: I have a one row table that named GeneralSettings and it has some columns (IsLoginEnable, IsPrivacyVisible etc.) and want to read these columns in _Layout.cshtml
<div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">
#if (true) //for example I want to write here "if (Model.ToList()[0].IsLoginEnable)" instead of this
{
<partial name="_LoginPartial" />
}
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
</li>
#if (true) //if (Model.ToList()[0].IsPrivacyVisible)
{
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
</li>
}
</ul>
I've added comments near the if conditions that I actually want to do.
Updated Answer
Seed your GeneralSettings on the application first start.
The admins can modify it from the UI using a regular form.
When needed, get it from the server's memory via the IMemoryCache service. If it's not present in the memory fetch it from the database and cache it.
When a modification in the GeneralSettings occures, purge it from memory using the IMemoryCache service.
The below code is just an example and may need to be modified to work in your application.
Turn your GeneralSettings into a service:
public Interface IGeneralSettingsService
{
Task<GeneralSettings> GetSettingsAsync();
}
public class GeneralSettingsRepo : IGeneralSettingsService
{
//Inject Context...
//Inject IMemoryCache...
public async Task<GeneralSettings> GetSettingsAsync()
{
if (!memoryCache.TryGetValue("GeneralSettings", out GeneralSettings GS))
{
GS = await context.GeneralSettings.AsNoTracking().FirstOrDefaultAsync();
_ = memoryCache.Set<GeneralSettings>("GeneralSettings", GS, new MemoryCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(cachehours),
SlidingExpiration = TimeSpan.FromHours(cachehours)
});
}
return GS;
}
}
In your admin controller:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> UpdateGlobalSettings(SettingsVm vm)
{
//Update properties...
memoryCache.Remove("GeneralSettings"); //Purge old settings from cache
}
And finally in your Layout.cshtml inject the settings service:
#inject IGeneralSettingsService GeneralSettingsService
#{
var settings = await GeneralSettingsService.GetSettingsAsync();
}
<div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">
#if (settings.IsLoginEnabled)
{
<partial name="_LoginPartial" />
}
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
</li>
#if (settings.IsPrivacyVisible)
{
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
</li>
}
</ul>
Don't forget to register the service in ConfigureServices.
Related
I am fiddling around with Gatsby JS using WP as the backend and so far so good. Now I was trying to pull in the menu which for main menu items works just as expected. What I can't really wrap my head around is how to get the submenus pulled in.
The only related thing I found was https://github.com/gatsbyjs/gatsby/issues/2426 which does give me the submenus if I log the data. Just can't get them to be pulled into the menu.
Here is my query in layouts/index.js:
export const query = graphql`
query LayoutQuery {
allWordpressWpApiMenusMenusItems {
edges {
node {
name
count
items {
order
title
url
wordpress_children {
wordpress_id
title
url
}
}
}
}
}
}
`
This is my menu component:
class MainMenu extends Component {
render(){
const data = this.props.menu.allWordpressWpApiMenusMenusItems.edges["0"].node.items
console.log(data)
return(
<div>
<h1>Menu</h1>
<ul>
{data.map((item) =>
<li key={item.object_slug}>
<Link to={item.url}>
{item.title}
</Link>
</li>
)}
</ul>
</div>
)
}
}
export default MainMenu
I tried fiddling around with variations of
{item.children["0"].wordpress_children.title}
but just can't get it to work:/ Any ideas or pointers would be much appreciated!
I just tested this, and your logic is sound all you need is another loop to display subitems. So in your MainMenu.js you can do something like this:
class MainMenu extends Component {
render() {
const data = this.props.menu.allWordpressWpApiMenusMenusItems.edges[0].node.items
console.log(data)
return (
<div>
<h1>Main Menu</h1>
<ul>
{data.map((item) =>
<li key={item.object_slug}>
<Link to={item.url}>
{item.title}
</Link>
<ul>
{item.wordpress_children && item.wordpress_children.map((subitem) =>
<li key={item.wordpress_id}>
<Link to={subitem.url}>
{subitem.title}
</Link>
</li>
)}
</ul>
</li>
)}
</ul>
</div>
)
}
}
This line is very important {item.wordpress_children && item.wordpress_children.map((subitem)
This will check if your menu item has subitems, and if it does it will map them and iterate through them.
I hope this works for you, I tested it and it works.
I'm currently trying to work on a basic JavaScript application.
I ultimately want to have a book that shows basic information on it's cover and with the click of a link, the book will show additional information.
I have two divs; #frontBook which will displays the front of the book which includes basic information about the current Book (i.e. Title, Author, Pages, Read or not) and #insideBook which will display additional info about the book(i.e. what page they're up to, publisher's name, etc.)
I want the #frontBook div to show and #insideBook to be hidden at first. Then once #additional-info link has been clicked, I want #frontBook to be hidden and #insideBook to be shown.
I've added my code below but it does not work. I'm really not sure where I'm going wrong. Please Help!
Thanks in advance.
P.S. I know that JQuery would be recommended for something like this but I just want to make sure I can grasp this concept with JavaScript first.
HTML:
<div id="frontBook">
<div id="book-outline">
<div id="book-content">
<ul id="basic-book-content">
<li><span class="basic-book-details" id="new-title">Title: </span></li>
<li><span class="basic-book-details" id="new-author">Author: </span></li>
<li><span class="basic-book-details" id="new-pages">Pages: </span></li>
<li><span class="basic-book-details" id="new-read">Read?:</span></li>
<li style="line-height: 80px; margin-right:-2px;">
<a href="#" id="additional-info" onclick="toggle_book();">Additional Info
<i style="color:white;" class="far fa-caret-square-right" ></i>
</a>
</li>
</ul>
</div>
<!-- closes #book-content-->
</div>
</div>
<div id="insideBook" style="display:none">
<div id="inside-book-outline">
<div id="inside-book-content">
<ul id="additional-book-content">
<li><span class="additional-book-details" id="new-editor"> Editor's Name: </span></li>
<li><span class="additional-book-details" id="new-publisher">Publisher's Name: </span></li>
<li><span class="additional-book-details" id="new-pub-comp">Publsiher Company's Name: </span></li>
<li><span class="additional-book-details" id="new-page-at">Page Up to:</span></li>
<li><span class="additional-book-details" id="new-line-at">Line Up to:</span></li>
<li><span class="additional-book-details" id="new-genre">Genre:</span></li>
<li><span class="additional-book-details" id="new-in-series">In Series?:</span></li>
<li style="line-height: 80px; margin-right:-2px;">
<a href="#" id="less-info" onclick="toggle_book();">
<i style="color:white;" class="far fa-caret-square-right"></i>Less Info
</a>
</li>
</ul>
</div>
<!-- closes #inside-book-content-->
</div>
</div>
JavaScript:
function toggle_book() {
var front = document.getElementById('frontBook');
var inside = document.getElementById('insideBook');
if (inside.style.display === 'block') {
inside.style.display = 'none';
front.style.display = 'block';
} else {
inside.style.display = 'block';
front.style.display = 'none';
}
}
Well you seemed to have missed two things.
1) You're if statement logic is backwards it should be:
function toggle_book() {
var front = document.getElementById('frontBook');
var inside = document.getElementById('insideBook');
if (inside.style.display === 'block') {
inside.style.display = 'none';
front.style.display = 'block';
} else {
inside.style.display = 'block';
front.style.display = 'none';
}
2) You don't have a onclick attribute for the <a> tag in the insideBook to toggle it back
I'm trying to use jstree and let one node and all its parent be opened when the page is opened. Here is the html code I used to test.
<div id="treeTask">
<ul>
<li id="node_37">TEST1
<ul>
<li id="node_38">TEST2</li>
<li id="node_39">TEST3</li>
</ul>
</li>
</ul>
<ul>
<li id="node_3">TEST1
<ul>
<li id="node_4">TEST2</li>
<li id="node_6">TEST3</li>
</ul>
</li>
</ul>
</div>
And here is the call to initialize jstree and open the node.
$(function () {
$("#treeTask").jstree();
$("#treeTask").bind("ready.jstree", function (event, data) {
$("#treeTask").jstree("open_node", $("#node_4"));
if((data.inst._get_parent(data.rslt.obj)).length) {
data.inst._get_parent(data.rslt.obj).open_node(this, false);
}
});
});
I have been manipulating the code for a while, but could not make it work. I would really appreciate if anyone can help.
Thanks so much!
You can use the built-in _open_to function:
http://www.jstree.com/api/#/?q=open_to&f=_open_to%28obj%29
$("#treeTask").jstree().bind('ready.jstree', function (event, data) {
data.instance._open_to('node_4');
});
Based on #maddin solution, I've updated it to support any number of parent levels.
jsFiddle
$("#treeTask").jstree().bind('ready.jstree', function (event, data) {
$("#treeTask").jstree('open_node', 'node_4', function(e,d) {
for (var i = 0; i < e.parents.length; i++) {
$("#treeTask").jstree('open_node', e.parents[i]);
};
});
});
It should be noted that if a node is selected, all its parents will be opened automatically. This would be somewhat equivalent to the above:
$("#treeTask").jstree().bind('ready.jstree', function (event, data) {
$("#treeTask").jstree('select_node', 'node_4');
});
Maybe this is an Solution for your problem:
fiddle
$("#treeTask").jstree().bind('ready.jstree', function (event, data) {
$("#treeTask").jstree('open_node', 'node_4', function(e,d) {
if(e.parents.length){
$("#treeTask").jstree('open_node', e.parent);
};
});
});
I'm trying to create some sort of class="active" functionality using Twirl and Play 2.3 but I can't find a solution that works, is short and doesn't throw my Intellij syntax all over the place.
This is what I currently have:
<ul class="nav navbar-nav">
<li #(if (request.uri == "/") Html("""class="active""""))>Home</li>
<li #(if (request.uri == "/about") Html("""class="active""""))>About</li>
<li #(if (request.uri == "/features") Html("""class="active""""))>Features</li>
</ul>
And this is what Intellij thinks of it:
Yeah, IntelliJ sometimes is confused with view's syntax, anyway you can create i.e. custom util for adding (or not) the active class (Java sample)
package utils;
import play.api.templates.Html;
import play.mvc.Http;
public class AttrHelpers {
public static Html setActiveClass(String requiredPath, Http.Request request) {
return (requiredPath.equals(request.uri())) ? Html.apply("class=\"active\"") : null;
}
}
So then you can use it in your views like:
<li #utils.AttrHelpers.setActiveClass("/about", request) >
About
</li>
<li #utils.AttrHelpers.setActiveClass("/features", request) >
Features
</li>
Can I execute that line of code
nav = request().path().toString()
inside of scala template like index.scala.html
I would like to have that code to check on witch side is user and to mark it on menu
using code like this in main.scala.html:
<li class="#("active".when(nav == "contact"))">
Contacts
</li>
I would recommend you different approach, create tag - resuable template, which takes Integer as an argument,
it will render menu and mark as an active different menuitem depends on value.
#(menuItem: Int)
<ul >
<li #if(menuItem==1){ class="active" } >
////
</li>
<li #if(menuItem==2){ class="active" }>
</li>
<li #if(menuItem==3){ class="active" }>
///
</li>
</ul>
from your contact page and any other page, call this tag with corresponding value, #views.html.tags.menu(1)
You can define variables like that if that is your question. If it is not your question than please try to explain your problem in more detail.
#nav = { #request().path().toString() }