For loop and Popup with liquid and jekyll - html

I want to create a list of modal popups with a for-loop, each of them displaying different text.
The site is created with Jekyll with the Liquid templating engine.
In particular, I want to create the list of my scientific publications, for each of them with 2 icons: one for the bibtex entry and one for the abstract. This information is stored in a yaml file.
I m following this simple tutorial for modal popups.
The popups work, but the text is the same for all the entries. How is possible to generate independent modal popups?
This is the html
{% for papers in papers %}
{% for content in paper.papers %}
<a title="{{content.name}}"><i class='{{content.icon}}' data-modal-target="#modal"></i></a>
<div class="modal" id="modal">
<div class="modal-header">
<div class="title">{{content.name}}</div>
<button data-close-button class="close-button">×</button>
</div>
<!-- text to display -->
<div class="modal-body">{{content.text}}</div>
</div>
{% endfor %}
{% endfor %}
and this is the Javascript code:
const openModalIcons = document.querySelectorAll('[data-modal-target]')
const closeModalButtons = document.querySelectorAll('[data-close-button]')
const overlay = document.getElementById('overlay')
openModalIcons.forEach(icon => {
icon.addEventListener('click', () => {
const modal = document.querySelector(icon.dataset.modalTarget)
openModal(modal)
})
})
function openModal(modal) {
if (modal == null) return
modal.classList.add('active')
overlay.classList.add('active')
}
closeModalButtons.forEach(button => {
button.addEventListener('click', () => {
const modal = button.closest('.modal')
closeModal(modal)
})
})
function closeModal(modal) {
if (modal == null) return
modal.classList.remove('active')
overlay.classList.remove('active')
}
overlay.addEventListener('click', () => {
const modals = document.querySelectorAll('.modal.active')
modals.forEach(modal => {
closeModal(modal)
})
})

The problem is that all your modals have id="modal", so the selector #modal is probably always selecting the first one. The id attribute should be unique in the entire document.
Something like this should work:
{% for papers in papers %}
{% for content in paper.papers %}
<a title="{{content.name}}"><i class='{{content.icon}}' data-modal-target="#paperModal{{forloop.parentloop.counter}}_{{forloop.counter}}"></i></a>
<div class="modal" id="paperModal{{forloop.parentloop.counter}}_{{forloop.counter}}">
<div class="modal-header">
<div class="title">{{content.name}}</div>
<button data-close-button class="close-button">×</button>
</div>
<!-- text to display -->
<div class="modal-body">{{content.text}}</div>
</div>
{% endfor %}
{% endfor %}
Instead of the for loop counter, you could also use the paper name as the ID (as long as it's unique), e.g. id="paperModal_{{content.name | slugify}}".
Edit: Edited the snippet to use forloop.counter and account for the nested for loop.

Related

Smooth Scolling in CSS and HTML

Hello I am working on my website with multiple pages and I want to do smooth scrolling on a certain page but I don't want to use the html tag because it will only be for this specific page and not the whole website here is my code.
{% if section.settings.display_product_detail_description == false and section.settings.display_product_reviews == false and section.settings.display_shipping_returns == false %}
{% assign tab5_active = true %}
{% endif %}
<div class="scroll-to-table">
<li class = "tab-title">
<a href="#product_attributes_table" class="tab-links {% if tab5_active %} active {% endif %}">
Specs
</a>
</li>
</div>
This is the code for the HTML
div.scroll-to-table{
scroll-behavior: smooth;}
And here is the code for the CSS
At the moment all that the page is doing is a jump and not a smooth scroll. I've tried using ID instead of Class, .scroll-to-table instead of div.scroll-to-table, and changing the element in which I call the CSS from but no luck
We can add scroll-behaviour: smooth to particular page by using javascript IIFE (Immediately Invoked Function Expression). This will add the smooth behaviour to the only page that runs this script.
<script>
(function () {
document.documentElement.style.scrollBehavior = smooth;
})();
</script>

How to remove multiple cart items on one click in shopify?

On Shopify cart page we can see multiple cart items if we added. Dawn Theme give functionality to remove each cart item separately. Is there any way to remove multiple cart Items by selecting them and click on delete, then all the selected items should be removed?
Below is my code I tried by the reference of Bhumi Shah's answer. But I get selected values in console but can't able to delete them. Screenshot
var ids = [];
$(".delCartCheck").click(function() {
if($(this).prop("checked")){
ids.push($(this).attr('data-check-id'));
} else{
var x = ids.indexOf($(this).attr('data-check-id'));
ids.splice(x,1);
}
console.log(ids)
});
$('#confirm-delete').on('click', function(e) {
// debugger;
var newArray = ids.filter(function(v){return v!==''});
// alert(newArray);
function deletecart(ids) {
$.ajax({
type: 'GET',
url: '/cart/clear.js',
data: {
id: ids
},
success: function(data) {
if (data === 'success') {
$(newArray).parents(".cart-item").remove();
}
}
});
}
});
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<button type="button btn" id='confirm-delete'>Delete Selected Item</button>
<form action="{{ routes.cart_url }}" class="cart__contents critical-hidden" method="post" id="cart">
<div class="cart__items" id="main-cart-items" data-id="{{ section.id }}">
{%- if cart != empty -%}
<table class="cart-items">
<tbody>
{%- for item in cart.items -%}
<tr class="cart-item" id="CartItem-{{ item.index | plus: 1 }}">
<td class="cart-item__media">
<input type="checkbox" class="delCartCheck" data-check-id="{{forloop.index}}">
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</div>
</form>
Take a look here and modify the code according to the required structure

How to modify AJAX request so that the empty dependent drop-down is hidden when page loads?

I have a form that has a dependent drop-down. Currently, it displays the main drop-down and the dependent one when the page loads, and when you select a "Work Area" that does not have a "Station", then the station drop-down disappears from the page.
What I'm trying to achieve is hiding the Station drop-down when the page first loads, and only have it show when a Work Area that does have Stations is selected. How could I modify the AJAX request (or the html tags, not sure where the change would have to occur) so that it is hidden from the beginning?
enter_exit_area.html
{% extends "base.html" %}
{% block main %}
<form id="warehouseForm" action="" method="POST" data-stations-url="{% url 'operations:ajax_load_stations' %}" novalidate >
{% csrf_token %}
<div>
<div>
<label>Employee</label>
{{ form.employee_number }}
</div>
<div>
<label>Work Area</label>
{{ form.work_area }}
</div>
<div>
<label>Station</label>
{{ form.station_number }}
</div>
</div>
<div>
<div>
<button type="submit" name="enter_area" value="Enter">Enter Area</button>
<button type="submit" name="leave_area" value="Leave">Leave Area</button>
</div>
</div>
</form>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script>
$("#id_work_area").change(function () {
var url = $("#warehouseForm").attr("data-stations-url");
var workAreaId = $(this).val();
$.ajax({
url: url,
data: {
'work_area': workAreaId
},
success: function (data) {
$("#id_station_number").html(data);
// Check the length of the options child elements of the select
if ($("#id_station_number option").length == 1) {
$("#id_station_number").parent().hide(); // Hide parent of the select node
} else {
// If any option, ensure the select is shown
$("#id_station_number").parent().show();
}
}
});
});
</script>
{% endblock main %}
station_number_dropdown_options.html
<option value="">---------</option>
{% for station in stations %}
<option value="{{ station.pk }}">{{ station.name }}</option>
{% endfor %}
Not sure if this part is necessary, but just in case:
views.py
def load_stations(request):
work_area_id = request.GET.get('work_area')
stations = Station.objects.filter(work_area_id=work_area_id).order_by('name')
return render(request, 'operations/station_number_dropdown_options.html', {'stations': stations})
Hide your div initially.
<div id="my-hidden-div" style="display:none">
<label>Station</label>
{{ form.station_number }}
</div>
then in your ajax method :
success: function (data) {
$("#my-hidden-div").show(); // show it
$("#id_station_number").html(data);
// Check the length of the options child elements of the select
if ($("#id_station_number option").length == 1) {
$("#id_station_number").parent().hide(); // Hide parent of the select node
} else {
// If any option, ensure the select is shown
$("#id_station_number").parent().show();
}
}
or make an css rule
#id_station_number {display:none}
then show it in the success like this
$("#id_station_number").show();

Django - System architecture for reusing user registration form on multiple pages

I was figuring how to reuse the same registration form and view on multiple templates and pages, and in different formats. E.g. on the page, in a modal etc. I am however some trouble in figuring out the best practice for solving this problem. One thing I am actively trying to avoid is repeating myself, but I can't seem to find a solution that is satisfying enough.
At the moment I have one central view that handles user registrations that looks like this. At the moment it can only handle to output one form on the signup_form template, but I want to extend that to the index page and be able to be outputted in a modal as well.
Views.py
def signup(request):
template_name = 'core/authentication/signup_form.html'
custom_error_list = []
if request.method == "POST":
form = SignUpForm(request.POST)
if form.is_valid():
#Check for duplciate email
email = form.cleaned_data.get('email')
username = form.cleaned_data.get('username')
if email and User.objects.filter(email=email).exclude(username=username).exists():
custom_error_list.append("A user with that email already exists")
else:
user = form.save(commit=False)
user.is_active = False
user.save()
current_site = get_current_site(request)
subject = 'Activate your StockPy Account'
sender = '' #Insert sender address here
message = render_to_string('core/authentication/account_activation_email.html', {
'user': user,
'domain': current_site.domain,
'uid': urlsafe_base64_encode(force_bytes(user.pk)),
'token': account_activation_token.make_token(user)
})
user.email_user(subject, message)
return redirect('authentication:account_activation_sent')
else:
form = SignUpForm()
return render(request, template_name, {'form': form, 'custom_error_list': custom_error_list})
#Activate the user as he/she clicks the email verification link which lead to tihs view
def activate(request, uidb64, token):
try:
#Using a [:1] is ad-hoc solution to get rid of the starting 'b' symbol
uid = force_text(urlsafe_base64_decode(uidb64[1:]))
user = User.objects.get(pk=uid)
except (TypeError, ValueError, OverflowError, User.DoesNotExist):
user = None
if user is not None and account_activation_token.check_token(user, token):
user.is_active = True
user.profile.email_confirmed = True
user.save()
login(request, user)
return redirect(template_name)
else:
return render(request, 'core/authentication/account_activation_invalid.html')
forms.py
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
from django import forms
class LoginForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput)
class Meta:
model = User
fields = ['email','password']
class SignUpForm(UserCreationForm):
email = forms.EmailField(max_length=254, widget=forms.TextInput(attrs={'placeholder': 'Email...', 'class' : 'form-control', 'pattern' : '[a-z0-9._%+-]+#[a-z0-9.-]+\.[a-z]{2,3}$'}))
class Meta:
model = User
fields = ['email', 'username', 'password1', 'password2']
def __init__(self, *args, **kwargs):
super(SignUpForm, self).__init__(*args, **kwargs)
self.fields['username'].widget = forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Username...',
})
self.fields['password1'].widget = forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Password...',
'type': 'password',
})
self.fields['password2'].widget = forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Password again...',
'type': 'password',
})
My signup form currently looks like this.
signup_form.html
{% extends 'base.html' %}
{% load static %}
<!-- End Navbar -->
{% block page-header %}
<div class="section section-signup" style="background-image: url({% static 'core/assets/img/bg8.jpg' %}); background-size: cover; background-position: top center; min-height: 700px;">
<div class="container">
<div class="row">
<div class="card card-signup" data-background-color="orange">
<form class="form" method="POST" action="">
{% csrf_token %}
<div class="header text-center">
<h4 class="title title-up">Sign Up</h4>
<div class="social-line">
<a href="#twitter" class="btn btn-neutral btn-twitter btn-icon btn btn-round">
<i class="fa fa-twitter"></i>
</a>
<a href="#facebook" class="btn btn-neutral btn-facebook btn-icon btn-lg btn-round">
<i class="fa fa-facebook-square"></i>
</a>
<a href="#google" class="btn btn-neutral btn-google btn-icon btn-round">
<i class="fa fa-google-plus"></i>
</a>
</div>
</div>
<div class="card-body">
<!-- Output error messages -->
{% for field in form %}
<div style="color:red; list-decorations:none;" class="text-center">
{{ field.errors.as_text }}
</div>
{% endfor %}
{% for error in custom_error_list %}
<div style="color:red;" class="text-center">
* {{ error }}
</div>
{% endfor %}
<!-- Output all fields -->
{% for field in form %}
<div class="input-group form-group-no-border">
<span class="input-group-addon">
<i class="now-ui-icons
{% if field.name == 'email' %} ui-1_email-85{% endif %}
{% if field.name == 'username' %} users_circle-08{% endif %}
{% if field.name == 'password1' %} ui-1_lock-circle-open{% endif %}
{% if field.name == 'password2' %} ui-1_lock-circle-open{% endif %}
"></i>
</span>
{{ field }}
<!-- Give input box red border if data is not valid -->
{% if field.errors %}
<script>
var element = document.getElementById("{{ field.id_for_label }}");
element.classList.add("form-control-danger");
</script>
{% endif %}
</div>
{% endfor %}
<div class="text-center">
Already registered? Log in here
</div>
<!-- If you want to add a checkbox to this form, uncomment this code -->
<!-- <div class="checkbox">
<input id="checkboxSignup" type="checkbox">
<label for="checkboxSignup">
Unchecked
</label>
</div> -->
</div>
<div class="footer text-center">
<button type="submit" class="btn btn-neutral btn-round btn-lg">Get Started</button>
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock page-header %}
And a small example snippet of my index.html of how I want to implement it ish.
index.html
<div class="main">
<div class="section section-about-us">
<div class="container">
<div class="col-md-8 ml-auto mr-auto text-center">
{{ form }}
</div>
</div>
</div>
</div>
I have really tried to find a smooth way of doing this, but without result unfortunately.
It seems as though you already know how to implement the same form in multiple templates, but you've having trouble avoiding repetition in your code. To that end, here are some suggestions for reducing the amount of repetition you'll encounter when duplicating this form across multiple pages:
Validate data within your form rather than your view. Currently, you are checking for duplicate e-mail addresses within views.py. If you duplicated your form, you'd have to re-write that code all over again. Instead, why not move it into forms.py in a custom cleaning method (see Django docs on custom form cleaning).
Write functions for actions that will be repeated. For example, currently, you are sending an activation e-mail to your user within views.py. It makes more sense to write a function within your user/models.py called something like send_activation_email(). Whenever you want to send an activation e-mail to a user, you can then call user.send_activation_email() rather than duplicating your entire block of activation e-mail code (see Django docs on model methods).
Use inclusion tags to avoid repetition in your templates. If there's a block of code that you find yourself repeating in your templates, you can use Django's inclusion tags to include a snippet of HTML across multiple templates. This allows you to serve the same form in multiple locations without re-writing your code. If you want to style your form differently across multiple pages, you could wrap it in DIVs with different IDs and use CSS to style the form differently depending on which DIV it's wrapped in. (see Django docs on inclusion tags)

Right way to use the data in Jekyll

The goal is to use the variables defined in the front-matter section in a particular page.
Here my structure of the file system:
_Components
c1.html
c2.html
Here I have defined the attributes in the front-matters.
_Includes > Components
c1.html
Here I want to use a loop to refer to a variable defined in the _Components > c1.html page.
How can I achieve this goal ?
In my _Includes > Components > c1.html I have the following code:
<body class="full">
{% assign fullcomponents = site.components %}
{% for component in fullcomponents | where:"title","c1html" %}
{% component.title %}
{% endfor %}
<div class="container-fluid" id="componentscontainer">
<div class="col-md-12">
<div class="panel panel-primary" id ="panelcomponentlarge">
<div class="panel-heading" >
Chart C3 Line
</div>
etc...
Surely I'm missing some trivial things.
SECOND ATTEMPT
I figured out that I can provide a data layer for that so, I tried to split this information into a new data file.
Here the content of components.json
{
"Components": [
"ChartC3Line":{
"component":"ChartC3Line",
"description":"This is an attempt"
},
"ChartC3Lines":{
"component":"ChartC3Lines",
"description":"This is an attempt"
}
]
}
And I'm trying to get this information with the following code:
{% assign comp = site.data.components.Components[ChartC3Line] %}
HTML:
{% highlight html linenos%}
<p> Description: {{ comp.description }} </p>
but anything is coming up.
THIRD ATTEMPT
I found a solution but I don't like it at all here my new json file
{
"ChartC3":[
{
"component":"ChartC3Line",
"description":"This is an attempt"
}],
"ChartC4":[
{
"component":"ChartC3Line",
"description":"This is an attemptSSSSSSS"
}]
}
I don't want to have an object of several arrays of one element!
Here the code to retrieve the right information:
{% assign comp = site.data.components.ChartC4[0] %}
HTML:
{% highlight html linenos%}
<p> Description: {{ comp.description }} </p>
SOLVED
Following the structure of a json file, I changed my structure in an easier way:
{
"ChartC3":
{
"component":"ChartC3Line",
"description":"This is an attempt"
},
"ChartC4":
{
"component":"ChartC3Line",
"description":"This is an attemptSSSSSSS"
}
}
Now I can easily have access to the right object.
{% assign comp = site.data.components.ChartC3 %}
HTML:
{% highlight html linenos%}
<p> Description: {{ comp.description }} </p>