How to check if current route in Laravel Blade matches a pattern? - regex

I'm checking the routes inside my blade template, to add an active class to a specific li in my menu with this code:
<ul>
<li class="{{ Request::is('*/sobre') || Request::is('*') ? "active" : "" }}">
Sobre o salão
</li>
<li class="{{ Request::is('*/servicos') ? "active" : "" }}">
Serviços
</li>
<li class="{{ Request::is('*/avaliacoes') ? "active" : "" }}">
Avaliações
</li>
<li class="{{ Request::is('*/galeria') ? "active" : "" }}">
Fotos
</li>
</ul>
And those are the routes:
Route::group(['prefix' => '{domain}', 'middleware'=>'salao'], function () {
Route::get('/', 'Frontend\FrontendSalaoController#sobre');
Route::get('sobre', 'Frontend\FrontendSalaoController#sobre');
Route::get('servicos', 'Frontend\FrontendSalaoController#servicos');
Route::get('avaliacoes', 'Frontend\FrontendSalaoController#avaliacoes');
Route::get('galeria', 'Frontend\FrontendSalaoController#galeria');
});
When i access the route http://website/x or the route http://website/x/sobre, the active class is positioned correctly. But, if I access the http://website/x/servicos route, the class will be added in the first li and in the servicos li.
How can i handle this?

Request::is('*') actually matches everything so the first item will always have the active class. Instead you should check for '/':
<li class="{{ Request::is('*/sobre') || Request::is('/') ? "active" : "" }}">
The is method even supports multiple parameters, of which only one has to match, so you can shorten it to this:
<li class="{{ Request::is('*/sobre', '/') ? "active" : "" }}">

Related

Toggle nav-link active state in django

For changing the .nav-link activate state I wrote
$(".nav-link").on("click", function(){
$(".nav-link.active").removeClass("active");
$(this).addClass("active");
});
But this won't work because the page reloads when clicking on the .nav-link. I can do something like:
var current_url = location.href;
if (current_url == "https://xyx.com/home") {
$('.nav-link.home').addClass('active');
} else {
$('.nav-link.home').removeClass('active');
}
But when I was looking for another method I found this
<li class="nav-item">
<a href="{{ route('home') }}"
class="nav-link dropdown-toggle {{ request()->routeIs('home') ? 'active' : '' }}">Home</a>
</li>
<li class="nav-item">
About
</li>
This is Laravel code but I am looking something like this for Django.

Translate Django template to ReactJS

I've created some templates in Django, and I want to translate them to work in ReactJS. The thing that I'm struggling with the most is the regroup function. There's a number of ways I've thought of approaching it, but I think the easiest is probably to do it within the component. All I've managed to do is map the items which generates the entire list, but I need to have the items dynamically grouped before iterating each group.
In React I'd like to be able to apply a command like items.groupBy('start_time').groupBy('event_name').map(item =>
The output should be 'start_time', 'event_name', and then the rest of the data within each event group. Each 'start_time' will contain multiple events. I'd like to keep the code as concise as possible.
This is the Django template:
{% if event_list %}
<div id="accordian" class="panel-group" role="tablist" aria-multiselectable="true">
{% regroup event_list by start_time as start_time_list %}
{% for start_time in start_time_list %}
<div class="row start-time">
<div class="col">
<h6 class="text-muted">{{ start_time.grouper|date:' d-m-Y H:i' }}</h6>
</div>
</div>
{% regroup start_time.list by event as events_list_by_start_time %}
{% for event_name in events_list_by_start_time %}
<div class="panel panel-default">
<div class="card-header" id="{{ event_name.grouper|slugify }}">
<div class="panel-heading">
<h5 class="panel-title">
<a data-toggle="collapse" data-parent="#accordian" href="#collapse-{{ event_name.grouper|slugify }}">
{{ event_name.grouper|title }}
</a>
</h5>
</div>
</div>
<div id="collapse-{{ event_name.grouper|slugify }}" class="panel-collapse collapse in">
<div class="panel-body">
{% for item in event_name.list %}
# continue iterating the items in the list
This is the render method from the React component:
render() {
const { error, isLoaded, items, groups } = this.state;
if (error) {
return <div>Error: {error.message}</div>;
} else if (!isLoaded) {
return <div>Loading...</div>;
} else {
return (
<div>
{items.map(item => (
<h4 key={item.id}>{item.event}</h4>
)
)}
</div>
);
}
}
}
If you are setting up a React front end, it would be more sensible not to mix django templates with the react front end components. Instead set up a Django/DRF backend api that will feed your react components with JSON data.
To translate this template from django to react, you just have to reimplement the regroup template tag as a javascript function. For pretty much any django template tag, you can easily find some javascript library that does the same. This is not included in React.js, but instead you can import utilities from libraries such as underscore or moment.js etc.
This is the sample django template code from the example in the documentation for the {% regroup %} template tag.
{% regroup cities by country as country_list %}
<ul>
{% for country in country_list %}
<li>{{ country.grouper }}
<ul>
{% for city in country.list %}
<li>{{ city.name }}: {{ city.population }}</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
Here's how you could do it with react.js
// React ( or javascript ) doesn't come with a groupby function built in.
// But we can write our own. You'll also find this kind of stuff in lots
// of javascript toolsets, such as lowdash, Ramda.js etc.
const groupBy = (key, data) => {
const groups = {}
data.forEach(entry => {
const {[key]: groupkey, ...props} = entry
const group = groups[groupkey] = groups[groupkey] || []
group.push(props)
})
return groups
}
// I'll define a dumb component function for each nested level of the list.
// You can also write a big render function with everything included,
// but I find this much more readable – and reusable.
const City = ({ name, population }) => (
<li> {name}: {population} </li>
)
const Country = ({ country, cities }) => (
<li>
{country}
<ul>{cities.map(props => <City key={props.name} {...props} />)}</ul>
</li>
)
const CityList = ({ cities }) => {
const groups = Object.entries(groupBy("country", cities))
return (
<ul>
{groups.map(([country, cities]) => (
<Country key={country} country={country} cities={cities} />
))}
</ul>
)
}
// We'll use the exact same data from the django docs example.
const data = [
{ name: "Mumbai", population: "19,000,000", country: "India" },
{ name: "Calcutta", population: "15,000,000", country: "India" },
{ name: "New York", population: "20,000,000", country: "USA" },
{ name: "Chicago", population: "7,000,000", country: "USA" },
{ name: "Tokyo", population: "33,000,000", country: "Japan" }
]
ReactDOM.render(<CityList cities={data} />, document.getElementById("app"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<main id=app></main>
If you run the snippet above, you should get the exact same output as what you find in the original django example.
<ul>
<li>India
<ul>
<li>Mumbai: 19,000,000</li>
<li>Calcutta: 15,000,000</li>
</ul>
</li>
<li>USA
<ul>
<li>New York: 20,000,000</li>
<li>Chicago: 7,000,000</li>
</ul>
</li>
<li>Japan
<ul>
<li>Tokyo: 33,000,000</li>
</ul>
</li>
</ul>

How to render menu with one active item with DRY?

I would like to render a constructions like:
<a href='/home'>Home</a>
<span class='active'>Community</span>
<a href='/about'>About</a>
Where Community is selected menu item. I have menu with same options for several templates but I would not like to create combinations for each template:
<!-- for Home template-->
<span class='active'>Home</span>
<a href='/comminuty'>Community</a>
<a href='/about'>About</a>
...
<!-- for Community template-->
<a href='/home'>Home</a>
<span class='active'>Community</span>
<a href='/about'>About</a>
...
<!-- for About template-->
<a href='/home'>Home</a>
<a href='/community'>Community</a>
<span class='active'>About</span>
We have permanent list of menu items, so, it can be more effective way - to create only one generalized structure of menu then render menu with required option for template.
For example it could be a tag that allows to do that.
Figured out another way to do it, elegant enough thanks to this answer : https://stackoverflow.com/a/17614086/34871
Given an url pattern such as:
url(r'^some-url', "myapp.myview", name='my_view_name'),
my_view_name is available to the template through request ( remember you need to use a RequestContext - which is implicit when using render_to_response )
Then menu items may look like :
<li class="{% if request.resolver_match.url_name == "my_view_name" %}active{% endif %}">Shortcut1</li>
<li class="{% if request.resolver_match.url_name == "my_view_name2" %}active{% endif %}">Shortcut2</li>
etc.
This way, the url can change and it still works if url parameters vary, and you don't need to keep a list of menu items elsewhere.
Using template tag
You can simply use the following template tag:
# path/to/templatetags/mytags.py
import re
from django import template
try:
from django.urls import reverse, NoReverseMatch
except ImportError:
from django.core.urlresolvers import reverse, NoReverseMatch
register = template.Library()
#register.simple_tag(takes_context=True)
def active(context, pattern_or_urlname):
try:
pattern = '^' + reverse(pattern_or_urlname)
except NoReverseMatch:
pattern = pattern_or_urlname
path = context['request'].path
if re.search(pattern, path):
return 'active'
return ''
So, in you your template:
{% load mytags %}
<nav><ul>
<li class="nav-home {% active 'url-name' %}">Home</li>
<li class="nav-blog {% active '^/regex/' %}">Blog</li>
</ul></nav>
Using only HTML & CSS
There is another approach, using only HTML & CSS, that you can use in any framework or static sites.
Considering you have a navigation menu like this one:
<nav><ul>
<li class="nav-home">Home</li>
<li class="nav-blog">Blog</li>
<li class="nav-contact">Contact</li>
</ul></nav>
Create some base templates, one for each session of your site, as for example:
home.html
base_blog.html
base_contact.html
All these templates extending base.html with a block "section", as for example:
...
<body id="{% block section %}section-generic{% endblock %}">
...
Then, taking the base_blog.html as example, you must have the following:
{% extends "base.html" %}
{% block section %}section-blog{% endblock %}
Now it is easy to define the actived menu item using CSS only:
#section-home .nav-home,
#section-blog .nav-blog,
#section-contact .nav-contact { background-color: #ccc; }
I found easy and elegant DRY solution.
It's the snippet:
http://djangosnippets.org/snippets/2421/
**Placed in templates/includes/tabs.html**
<ul class="tab-menu">
<li class="{% if active_tab == 'tab1' %} active{% endif %}">Tab 1</li>
<li class="{% if active_tab == 'tab2' %} active{% endif %}">Tab 2</li>
<li class="{% if active_tab == 'tab3' %} active{% endif %}">Tab 3</li>
</ul>
**Placed in your page template**
{% include "includes/tabs.html" with active_tab='tab1' %}
You could make a context variable links with the name, URL and whether it's an active item:
{% for name, url, active in links %}
{% if active %}
<span class='active'>{{ name }}</span>
{% else %}
<a href='{{ url }}'>{{ name }}</a>
{% endif %}
{% endfor %}
If this menu is present on all pages, you could use a context processor:
def menu_links(request):
links = []
# write code here to construct links
return { 'links': links }
Then, in your settings file, add that function to TEMPLATE_CONTEXT_PROCESSORS as follows: path.to.where.that.function.is.located.menu_links. This means the function menu_links will be called for every template and that means the variable links is available in each template.
I have come up with a way to utilize block tags within menu-containing parent template to achieve something like this.
base.html - the parent template:
Home
About
Contact
{% block content %}{% endblock %}
about.html - template for a specific page:
{% extends "base.html" %}
{% block menu_about_class %}active{% endblock %}
{% block content %}
About page content...
{% endblock %}
As you can see, the thing that varies between different page templates is the name of the block containing active. contact.html would make use of menu_contact_class, and so on.
One benefit of this approach is that you can have multiple subpages with the same active menu item. For example, an about page might have subpages giving information about each team members of a company. It could make sense to have the About menu item stay active for each of these subpages.
Here ist my solution:
{% url 'module:list' as list_url %}
{% url 'module:create' as create_url %}
<ul>
<li>List Page</li>
<li>Creation Page</li>
</ul>
Assuming the nav item is a link with the same URL as the current page, you could just use JavaScript. Here's an annotated method that I use to add a class="active" to a li in a navigation menu with class="nav":
// Get the path name (the part directly after the URL) and append a trailing slash
// For example, 'http://www.example.com/subpage1/sub-subpage/'
// would become '/subpage1/'
var pathName = '/' + window.location.pathname.split('/')[1];
if ( pathName != '/' ) { pathName = pathName + '/'; }
// Form the rest of the URL, so that we now have 'http://www.example.com/subpage1/'
// This returns a top-level nav item
var url = window.location.protocol + '//' +
window.location.host +
pathName;
console.log(url);
// Add an 'active' class to the navigation list item that contains this url
var $links = document.querySelectorAll('.nav a');
$link = Array.prototype.filter.call( $links, function(el) {
return el.href === url;
})[0];
$link.parentNode.className += ' active';
This method means you can simply pop it into your base template once and forget about it. No repetition, and no manual specification of the page URL in each template.
One caveat: this obviously only works if the url found matches a navigation link href. It would additionally be possible to specify a couple of special use cases in the JS, or target a different parent element as needed.
Here's a runnable example (keep in mind, snippets run on StackSnippets):
// Get the path name (the part directly after the URL) and append a trailing slash
// For example, 'http://www.example.com/subpage1/sub-subpage/'
// would become '/subpage1/'
var pathName = '/' + window.location.pathname.split('/')[1];
if ( pathName != '/' ) { pathName = pathName + '/'; }
// Form the rest of the URL, so that we now have 'http://www.example.com/subpage1/'
// This returns a top-level nav item
var url = window.location.protocol + '//' +
window.location.host +
pathName;
console.log(url);
// Add an 'active' class to the navigation list item that contains this url
var $links = document.querySelectorAll('.nav a');
$link = Array.prototype.filter.call( $links, function(el) {
return el.href === url;
})[0];
$link.parentNode.className += ' active';
li {
display: inline-block;
margin: 0 10px;
}
a {
color: black;
text-decoration: none;
}
.active a {
color: red;
}
<ul class="nav">
<li>
Example Link
</li>
<li>
This Snippet
</li>
<li>
Google
</li>
<li>
StackOverflow
</li>
</ul>
I ran into this challenge today with how to dynamically activate a "category" in a sidebar. The categories have slugs which are from the DB.
I solved it by checking to see category slug was in the current path. The slugs are unique (standard practice) so I think this should work without any conflicts.
{% if category.slug in request.path %}active{% endif %}
Full example code of the loop to get the categories and activate the current one.
{% for category in categories %}
<a class="list-group-item {% if category.slug in request.path %}active{% endif %}" href="{% url 'help:category_index' category.slug %}">
<span class="badge">{{ category.article_set.count }}</span>
{{ category.title }}
</a>
{% endfor %}
Based on #vincent's answer, there is an easier way to do this without messing up with django url patterns.
The current request path can be checked against the rendered menu item path, and if they match then this is the active item.
In the following example I use django-mptt to render the menu but one can replace node.path with each menu item path.
<li class="{% if node.path == request.path %}active{% endif %}">
node.title
</li>
I am using an easier and pure CSS solution. It has its limitations, of which I know and can live with, but it avoids clumsy CSS class selectors, like this:
index
Because a space character before active is missing the class selector gets called itemactive instead of item active and this isn't exactly too difficult to get wrong like that.
For me this pure CSS solution works much better:
a.item /* all menu items are of this class */
{
color: black;
text-decoration: none;
}
a.item[href~="{{ request.path }}"] /* just the one which is selected matches */
{
color: red;
text-decoration: underline;
}
Notice: This even works if the URL has additional path components, because then href also matches partially. That could eventually cause 'collisions' with more than one match, but often enough it just works, because on well structured websites a "subdirectory" of an URL usually is a child of the selected menu item.
I personally find the simplest way is to create blocks for each link like so:
# base.py
...
<a href="{% url 'home:index' %}" class={% block tab1_active %}{% endblock %}>
...
<a href="{% url 'home:form' %}" class={% block tab2_active %}{% endblock %}>
...
And then in each relative template declare that link as "active" e.g.:
tab1 template:
{% block tab1_active %}"active"{% endblock %}
tab2 template:
{% block tab2_active %}"active"{% endblock %}
Simply use template tags
# app/templatetags/cores.py
from django import template
from django.shortcuts import reverse
register = template.Library()
#register.simple_tag
def active(request, url, classname):
if request.path == reverse(url):
return classname
return ""
Do like this in your template
{% load cores %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Example</title>
</head>
<body>
<div>
myUrl
</div>
</body>
</html>
Have fun 😎

How to set multiple values for the same attribute in tal:attributes using TALES

I'm trying to set multiple css classes on one element.
Unfortunately this doesn't work, as it returns: LanguageError: Duplicate attribute name in attributes.
<ul>
<li tal:repeat="item mainnav"
tal:attributes="class 'first' if repeat.item.start else nothing;
class 'last' if repeat.item.end else nothing;
class 'active' if item.active else nothing">
<a tal:attributes="href item.href" tal:content="item.title">title</a>
</li>
</ul>
Combining those 3 cases into one expression makes it quite complicated, because there are 6 different css states:
first + active
first
last + active
last
active
(none)
There are 2 possible solutions that I can think of:
-> check each combination inline:
<ul>
<li tal:repeat="item mainnav"
tal:attributes="
class 'first active' if (repeat.item.start and item.active) else
'first' if repeat.item.start else
'last active' if (repeat.item.end and item.active) else
'last' if repeat.item.end else
'active' if item.active else nothing">
<a tal:attributes="href item.href" tal:content="item.title">title</a>
</li>
</ul>
-> create a method that returns the combined css classes
Now, is there a better approach and if not, which of those 2 is better (probably the latter one, as if it gets more complicating the inline script will become unreadable/unmanageable).
BTW, are there any good resources and examples about Chameleon, TALES (other than http://chameleon.repoze.org/docs/latest)
You can use tal:define multiple times to define the various parts of your class string, then construct the actual attribute from those parts:
<tal:loop repeat="item mainnav">
<li tal:define="class_first 'first' if repeat.item.start else '';
class_last 'last' if repeat.item.end else '';
class_active 'active' if item.active else '';"
tal:attributes="class string:$class_first $class_last $class_active">
<a tal:attributes="href item.href" tal:content="item.title">title</a>
</li>
</tal>
This could result in an empty class attribute, which is harmless.
As for additional documentation; Chameleon is an implementation of TAL, originally developed for Zope Page Templates. As such, you'll find a lot of documentation for the latter also applies to Chameleon, as long as you take into account that Chameleon's default TALES modus is python:, while ZPT defaults to path: instead. The Advanced Page Templates chapter of the Zope Book applies to Chameleon as well, for example.
In Chameleon you can do:
<ul>
<li tal:repeat="item mainnav"
class="${'first' if repeat.item.start else ''}
${'last' if repeat.item.end else ''}
${'active' if item.active else ''">
<a tal:attributes="href item.href" tal:content="item.title">title</a>
</li>
</ul>
[Edit]
Or better like this:
<ul>
<li tal:repeat="item mainnav"
class="${('first ' if repeat.item.start else '') +
('last ' if repeat.item.end else '') +
('active' if item.active else '')}">
<a tal:attributes="href item.href" tal:content="item.title">title</a>
</li>
</ul>
You're not using tal:condition, it has a purpose. I don't like overly nested conditionals, gets you no where.
Haven't tested this but you may get the idea.
<ul>
<tal:myloop tal:repeat="item mainnav">
<li tal:condition="item.active" tal:attributes="class
'active first' if repeat.item.start
else 'active last' if repeat.item.end
else 'active'">
<a tal:attribute="href item.href" tal:content="item.title"></a>
</li>
<li tal:condition="not item.active" tal:attributes="class
'first' if repeat.item.start
else 'last' if repeat.item.end else None">
<a tal:attribute="href item.href" tal:content="item.title"></a>
</li>
</tal:myloop>
</ul>

How do i save drag and drop in django backend?

I'm trying to create a drag and drop function in my project that when ever i drop an item it automatically saves in the backend side.
<script style="text/javascript">
$(function() {
$( "#roles li" ).draggable({
appendTo: "body",
helper: "clone",
});
$( "#item-roles li" ).droppable({
activeClass: "ui-state-default",
hoverClass: "ui-state-hover",
accept: ":not(.ui-sortable-helper)",
drop: function( event, ui ) {
alert('you drop this')
$( "<li></li>" ).text( ui.draggable.text() ).appendTo( this );
}
})
});
In my Html code goes here.
<div id="item-roles">
<h3>Role in {{item.name|title}} Item</h3>
<ul>
<h3>Drag role here!</h3>
{% for item_role in item_roles %}
<li class="placeholder" style="list-style-type: none;">
{{item_role.show_role}} Remove
</li>
{% empty%}
<li>No items found</li>
{% endfor %}
</ul>
</div>
<div id="roles">
<h3>Roles in this Show</h3>
<ul>
{% for role in roles %}
<li>
<a class="role-detail">{{ role.role_name }}</a>
</li>
{% empty %}
<p>No show roles </p>
{% endfor %}
</ul>
You can use dajaxice (and dajax) for calling your django functions "from" javascript. Now you need to write down your python code which is missing from your question...