How can I add a JavaScript event to my Django HTML template? - javascript

My current logout is GET, I just redirect the user to /auth/logout
However, I’ve discovered that this is unsafe and I trying to add a post to this redirection. By the way, my login is using django-allauth, so I am looking to use this concept here too. But, I need to do it with JavaScript because my front end is written in Vue.js.
https://django-allauth.readthedocs.io/en/latest/views.html#logout-account-logout
This is my javascript file where I use too much vue:
let userNavigation = [
{ name: 'Account', href: '/account/'},
{ name: 'Logout', href: '/auth/logout'}
]
This is my HTML using vue
const menu =
`
<MenuItems>
<MenuItem v-for="item in userNavigation" :key="item.name">
<a :href="item.href">
[[ item.name ]]
</a>
</MenuItem>
</MenuItems>
`
This is what I am trying to do to:
<script type="text/javascript">
function logoutPost() {
let form = document.createElement('form');
form.setAttribute('method', 'post');
form.setAttribute('action', '/auth/logout/');
let csrf = document.createElement('input');
csrf.setAttribute('type', 'hidden');
csrf.setAttribute('name', 'csrfmiddlewaretoken');
csrf.setAttribute('value', '{{ csrf_token }}');
form.appendChild(csrf);
document.body.appendChild(form);
let logoutAnchor = document.getElementsByName('Logout')[0].value;
form.appendChild(logoutAnchor);
logoutAnchor.addEventListener('click', function (e) {
e.preventDefault();
form.submit();
console.log("logout clicked");
});
}
</script>
But, even if I try to add JavaScript DOM, nothing changes, I do not see anything in my console log and it just redirects. Where I am making a mistake?

Related

Confirmation button on save button like delete already has

I want to modify Django admin interface.
There is delete button already has a confirmation page. But I want to add this confirmation to save or change buttons. Actually not exactly the same delete button.
I want to change default admin buttons like this JS or I want to add JS to admin buttons.
<input type="submit" onclick="linkSubmit('http://www.google.com')" value="Submit">
<p id="demo"></p>
<script>
function linkSubmit(link) {
let text = "Press a button!\nEither OK or Cancel.";
if (confirm(text) == true) {
window.location.href = link;
} else {
}
document.getElementById("demo").innerHTML = text;
}
</script>
We found the solution in the files of the delete command. We took copies of the files confirm to the delete function and connected them to the confirm button.
We still can't give it as a alert. Gives confirmation on a another page.
Assuming there is already some type of event listener for the button I would add my own custom function as an additional listener for the on click event. Then I would put in my if(confirm) logic and call event.stopImmediatePropagation() as needed to prevent the original functionality from occuring.
Create templates/admin/change_form.html in your project:
{% extends "admin/change_form.html" %}
{% block admin_change_form_document_ready %}{{ block.super }}
<script id="django-admin-form-change-constants"
data-model-name="{{ opts.model_name }}">
let modelName = document.getElementById('django-admin-form-change-constants').dataset.modelName;
let form = document.getElementById(modelName + '_form');
form.addEventListener('submit', (event) => {
let text = "Press a button!\nEither OK or Cancel.";
if (!confirm(text)) {
event.preventDefault();
}
});
</script>
{% endblock %}
Set DIRS in myproject/settings.py to point to your project's templates directory:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates'],
'APP_DIRS': True,
...
}
]
References:
https://docs.djangoproject.com/en/4.1/ref/contrib/admin/#overriding-admin-templates
https://docs.djangoproject.com/en/4.1/howto/overriding-templates/#overriding-from-the-project-s-templates-directory
https://github.com/django/django/blob/4.1/django/contrib/admin/templates/admin/change_form.html#L66-L74
https://github.com/django/django/blob/4.1/django/contrib/admin/static/admin/js/change_form.js#L5

Using generated Stripe checkout button

I'm trying to implement in my VueJS Project a checkout button generated from the Stripe Dashboard.
I feel like i'm not doing it the right way, so if you have advises i would like to hear them.
I have add <script src="https://js.stripe.com/v3/"></script> in index.html.
the 1st error i get
the 2nd error i get
Here is my Vue component.
<template>
<div>
<button
style="background-color:#6772E5;color:#FFF;padding:8px 12px;border:0;border-radius:4px;font-size:1em"
id="checkout-button-MY_PLAN"
role="link"
>
Checkout
</button>
<div id="error-message"></div>
</div>
</template>
<script>
(function() {
let stripe = Stripe('MY_KEY');
let checkoutButton = document.getElementById('MY_PLAN');
checkoutButton.addEventListener('click', function () {
stripe.redirectToCheckout({
items: [{plan: 'MY_PLAN', quantity: 1}],
successUrl: '',
cancelUrl: '',
})
.then(function (result) {
if (result.error) {
let displayError = document.getElementById('error-message');
displayError.textContent = result.error.message;
}
});
});
})();
</script>
<style scoped>
</style>
If i can't use Stripe in a VueJS Project how can i get around the problem without modifying all my project ?
For the first error where Stripe is undefined. That is likely because the Stripe.js library isn't loaded before that code runs. You'll need to ensure that you've included Stripe.js [1] with this tag in your HTML before any of your JavaScript using Stripe is executed. Note that it's not loaded async.
<script src="https://js.stripe.com/v3/"></script>
The second error is because when you're attempting to getElementById the ID that you're passing is not the same as the ID in the HTML for the button.
The button's ID is checkout-button-MY_PLAN and the ID you're trying to find the button with by passing is MY_PLAN. I would recommend updating your call to getElementById to:
let checkoutButton = document.getElementById('checkout-button-MY_PLAN');
[1] https://stripe.com/docs/js/including

How to toggle a class based on a variable passed from Laravel using a Vuejs component

This is the 3rd attempt for me to get some help on this issue, for so long I've been trying to toggle an id using Vuejs, I just want that id to change based on a boolean value passed on from Laravel, I managed to do it without components but I ran into a problem, there's multiple buttons on the page, when one gets their id updated the others do as well, so I thought maybe a component could solve this, I just can't get it to work.
Here's the blade template, this is inside a table inside a foreach loop that has access to a $courses variable:
courses.blade:
<form method="POST" action="{{ route('course.completed', $course->name) }}" id="form-submit">
{{ csrf_field() }}
#if ($course->pivot->completed == true)
<course-buttons id="this.greenClass.aClass">Done</course-buttons>
#else
<course-buttons id="this.redClass.aClass">Not Yet</course-buttons>
#endif
</form>
this is app.js:
require('./bootstrap');
Vue.component('course-buttons', require('./components/course-buttons.vue'))
var vm = new Vue({
el: '#app'
});
And this is the course-buttons.vue file:
<template>
<button #click.prevent="onSubmit({{ $course }})" type="button" class="btn btn-sm" :id=id><slot></slot></button>
</template>
<script>
export default {
props: ['id'],
data: function() {
return {
greenClass: {aClass: 'coursetrue', text: 'Done!'},
redClass: {aClass: 'coursefalse', text: 'Not Yet!'}
};
},
methods: {
onSubmit: function(course) {
axios.post('/MyCourses/' + course.name)
.then(function (response){
console.log(response.data.course.pivot.completed)
if (response.data.course.pivot.completed == true){
return [
vm.redClass.aClass = 'coursetrue',
vm.redClass.text = 'Done!',
vm.greenClass.aClass = 'coursetrue',
vm.greenClass.text = 'Done!'
]
} else {
return [
vm.greenClass.aClass = 'coursefalse',
vm.greenClass.text = 'Not Yet',
vm.redClass.aClass = 'coursefalse',
vm.redClass.text = 'Not Yet'
]
}
});
}
}
}
</script>
First I know that my code isn't good, that's why I asked for help many times but with no answers at all, so if you have any tips that might help me get this code cleaner, even change it totally but just get the job done, I'm all ears.
The errors I'm getting right now is first the #click.prevent is an invalid expression, tried moving that to the tag, it doesn't do anything over there so I had that going before and now I lost it as well, also I get an error that the id is not defined on the instance, although I defined the props and data in the vue component.
if you're wondering why do I even assign the id on the tag itself and not the component, it's because of how my code is structured,e.g "If the value is true then load the tag with that id, otherwise load it with that id", once again if you can help me do this in a completely different way I'll be grateful.
One error I see is you have to use v-bind:id here, as you are assigning a vue variable:
<course-buttons v-bind:id="this.greenClass.aClass">Done</course-buttons>
One more is in onSubmit method, you dont have to return anything, and also you are using vm there, instead of that you can just do following:
onSubmit: function(course) {
axios.post('/MyCourses/' + course.name)
.then((response) => {
console.log(response.data.course.pivot.completed)
if (response.data.course.pivot.completed == true){
this.redClass.aClass = 'coursetrue',
this.redClass.text = 'Done!',
this.greenClass.aClass = 'coursetrue',
this.greenClass.text = 'Done!'
} else {
this.greenClass.aClass = 'coursefalse',
this.greenClass.text = 'Not Yet',
this.redClass.aClass = 'coursefalse',
this.redClass.text = 'Not Yet'
}
});
}
Here I have also used arrow syntax, here is why.

Accessing Jquery Key/Values in Python

I have a simple form and I need to access the key/value and properties of the jquery code for the form. Also when I try to create a customer in my view with request.form['stripeToken'] it gives error mentioned at end of question. I need to access following fields in Jquery script with key/value attributes:
$("#stripeToken").val(token.id);
$("#stripeEmail").val(token.email);
Following is the code:
Jquery Code:
<form id="myForm" action="/yearly" method="POST">
<input type="hidden" id="stripeToken" name="stripeToken" />
<input type="hidden" id="stripeEmail" name="stripeEmail" />
<button id="customButton">Purchase</button>
</form>
<script type="text/javascript">
var handler = StripeCheckout.configure({
key: 'pk_test_YgHVTCLIMQLW4NV6ntnJPAXs',
image: 'https://stripe.com/img/documentation/checkout/marketplace.png',
locale: 'auto',
token: function (token) {
$("#stripeToken").val(token.id);
$("#stripeEmail").val(token.email);
$("#myForm").submit();
}
});
$('#customButton12').on('click', function (e) {
handler.open({
name: 'Yearly',
description: 'Yearly Charge',
amount: 9500
});
e.preventDefault();
});
$(window).on('popstate', function () {
handler.close();
});
</script>
Following is the view:
#app.route('/yearly', methods=['GET', 'POST'])
def yearly_charge():
key = stripe_keys['publishable_key']
data = get_profile_data(session['auth_token'])
profile_data = data['Student']
student_id = profile_data.id
student = get_profile_data(session['auth_token'])['StudentProfile']
pkg = Package.query.filter_by(student_id=profile_data.id).first()
# customer
stripe_token = request.form['stripeToken']
email = request.form['stripeEmail']
if not pkg:
try:
customer = stripe.Customer.create(
email=email,
source=request.form['stripeToken']
)
print request.form
subscription = stripe.Subscription.create(
customer=customer.id,
plan="yearly",
)
student_id = profile_data.id
student.stripe_customer_id = customer.id
student.stripe_subscription_id = subscription.id
package = Package(
student_id=student_id,
stripe_id = customer.id,
student_email=request.form['stripeEmail'],
is_active=True,
package_type='yearly',
subscription_id=subscription.id
)
dbase.session.add(package)
dbase.session.commit()
except stripe.error.CardError as e:
# The card has been declined
body = e.json_body
err = body['error']
flash("You've successfylly subscribed for annual package.")
return redirect(url_for('new_template', key=key))
Error:
stripe.error.InvalidRequestError
InvalidRequestError: Request req_AMbjoYEtQR1d6Y: Invalid source object: must be a dictionary or a non-empty string. See API docs at https://stripe.com/docs'
It seems you are trying to implement custom button for stripe. There is an easy way instead of going to write all the code if you don't know how to communicate between python and js.
<form action='/yoururl' method='post'>
<script src="https://checkout.stripe.com/checkout.js" class="stripe-button"
data-key="{{ key }}"
data-description="Your Description"
data-amount="500"
data-locale="auto"></script>
<input type="submit" value='Subscribe' class='btn' style='height:33px;width:50%;color:white;'>
</form>
<script>
document.getElementsByClassName("stripe-button-el")[0].style.display = 'none';
</script>
What actually happened in the code is it hides the default stripe button. So then any input button will work for you inside tag. You can use multiple buttons such as if you have another button you can use different price or other variables and just change the script like below:
<script>
document.getElementsByClassName("stripe-button-el")[1].style.display = 'none';
</script>
If there is a third button you can do it like:
<script>
document.getElementsByClassName("stripe-button-el")[2].style.display = 'none';
</script>

How can I make dynamic Ajax requests with Laravel

I'm creating a social network site using Laravel. I have a page that load all the posts created by users the currentUser follows. I have a comment section on each post. I want a user to be able to comment on any post without the page reloading so the user doesn't have to re-scroll through the page.
I have everything working fine without ajax (minus the reloading page). I'm able to post comments, the page reloads and the new comment is displayed. However, when I try to use Ajax I've been running into problems.
Here is my code.
Here is the view of the comment-box. It contains a section where I loop through each comment and display them. At the end is the type field so a user can post a new comment:
<div class="comment-box-container ajax-refresh">
<div class="comment-box">
#if ($type->comments)
#foreach ($type->comments as $comment)
<div class="user-comment-box">
<div class="user-comment">
<p class="comment">
<!-- starts off with users name in blue followed by their comment-->
<span class="tag-user">{{ $comment->owner->first_name }} {{ $comment->owner->last_name }} </span>{{ $comment->body }}
</p>
<!-- Show when the user posted comments-->
<div class="com-details">
<div class="com-time-container">
{{ $comment->created_at->diffForHumans() }} ·
</div>
</div>
</div><!--user-comment end-->
</div><!--user-comment-box end-->
#endforeach
#endif
<!--type box-->
<div class="type-comment">
<div class="type-box">
{{ Form::open(['data-remote', 'route' => ['commentPost', $id], 'class' => 'comments_create-form']) }}
{{ Form::hidden('user_id', $currentUser->id) }}
{{ Form::hidden($idType, $id) }}
{{--{{ Form::hidden('user_id', $currentUser->id) }}--}}
{{ Form::textarea('body', null, ['class' =>'type-box d-light-solid-bg', 'placeholder' => 'Write a comment...', 'rows' => '1']) }}
{{ Form::close() }}
</div><!--type-box end-->
</div><!--type-comment-->
</div><!--comment-box end-->
The user submit the form for the comment type box by pressing the "enter/return" key. Here is the JS for that
<script>
$('.comments_create-form').on('keydown', function(e) {
if (e.keyCode == 13) {
e.preventDefault();
$(this).submit();
}
});
</script>
Here is my Ajax
(function(){
$('form[data-remote]').on('submit', function(e){
var form = $(this);
var method = form.find('input[name="_method"]').val() || 'POST';
var url = form.prop('action');
$.ajax({
type: method,
url: url,
data: form.serialize(),
success: function(data) {
var tmp = $('<div>');
tmp.html(data);
$('.ajax-refresh').html(tmp.find('.ajax-refresh').html());
$('.type-box').html(tmp.find('.type-box').html());
tmp.destroy();
}
});
e.preventDefault();
});
})();
I'm running into a few problems with this code. The comment gets displayed on ever single post until I manually refresh the page then it only shows on the correct post. I feel like every post's comment-box will need it's own unique ID to solve this, but I do not know how to do this with Laravel and make the JavaScript work.
also,
After I submit one comment I can no longer submit a second one because my "submit on enter/return key" functionally is no longer working. My cursor just moves to a new line, and I'm not able to post another comment.
Does anyone know a way to fix these problems?
EDIT
Here is my ajax so far
(function(){
$(document).on('submit', 'form[data-remote]', function(e){
e.preventDefault();
var form = $(this)
var target = form.closest('div.ajax-refresh');
var method = form.find('input[name="_method"]').val() || 'POST';
var url = form.prop('action');
$.ajax({
type: method,
url: url,
data: form.serialize(),
success: function(data) {
var tmp = $('<div>');
tmp.html(data);
target.html( tmp.find('.ajax-refresh').html() );
target.find('.type-box').html( tmp.find('.type-box').html() );
tmp.destroy();
}
});
});
})();
Please use the following to fix the issue:
$(document).on('submit', 'form[data-remote]', function(e){
e.preventDefault();
var form = $(this),
var target = form.closest('div.ajax-refresh');
var method = form.find('in......
......
.....
tmp.html(data);
target.html( tmp.find('.ajax-refresh').html() );
target.find('.type-box').html( tmp.find('.type-box').html() );
tmp.destroy();
}
});
});
The variable target will help you target just the right div to add the ajax response to.
Further, you would have to just reset the relevant form rather than replace the form markup. Otherwise each form will work only once.
UPDATE
The above code has been updated to use a delegated submit event -- $(document).on('submit', '.selector', ...) instead of $('.selector').on('submit', .....) since the form content is being replaced after each comment.
UPDATE 2
The following delegated keydown event should enable you to submit by pressing the enter key:
$(document).on('keydown', '.comments_create-form', function(e) {
if (e.keyCode == 13) {
e.preventDefault();
$(this).submit();
}
});

Categories