I am making a django app that displays a progress bar. So far I have got it working to display a progress bar using this library and the following code which they suggested.
<div class='progress-wrapper'>
<div id='progress-bar' class='progress-bar' style="background-color: #68a9ef; width:
0%;"> </div>
</div>
<div id="progress-bar-message">Waiting for progress to start...</div>
<script src="{% static 'celery_progress/celery_progress.js' %}"></script>
<script>
// vanilla JS version
document.addEventListener("DOMContentLoaded", function () {
var progressUrl =
"{%
try:
url 'celery_progress:task_status' task_id
catch:
pprint("PHEW")
%}";
CeleryProgressBar.initProgressBar(progressUrl);
});
</script>
However, how might i integrate the above code with the code below to get it to display success or error:
function customSuccess(progressBarElement, progressBarMessageElement) {
progressBarElement.innerHTML = (
'<figure class="image"><img src="/static/projects/images/aww-yeah.jpg"></figure>'
)
progressBarElement.style.backgroundColor = '#fff';
progressBarMessageElement.innerHTML = 'success!'
}
function customError(progressBarElement, progressBarMessageElement) {
progressBarElement.innerHTML = (
'<figure class="image"><img src="/static/projects/images/okay-guy.jpg"></figure>'
)
progressBarElement.style.backgroundColor = '#fff';
progressBarMessageElement.innerHTML = 'shucks.'
}
CeleryProgressBar.initProgressBar(taskUrl, {
onSuccess: customSuccess,
onError: customError,
});
just change the script in your html with this one:
<script>
function customSuccess(progressBarElement,
progressBarMessageElement) {
progressBarElement.innerHTML = (
'<figure class="image"><img src="/static/projects/images/aww-yeah.jpg"></figure>'
)
progressBarElement.style.backgroundColor = '#fff';
progressBarMessageElement.innerHTML = 'success!'
}
function customError(progressBarElement, progressBarMessageElement) {
progressBarElement.innerHTML = (
'<figure class="image"><img src="/static/projects/images/okay-guy.jpg"></figure>'
)
progressBarElement.style.backgroundColor = '#fff';
progressBarMessageElement.innerHTML = 'shucks.'
}
document.addEventListener("DOMContentLoaded", function () {
var progressUrl = "{% url 'celery_progress:task_status' task_id %}";
CeleryProgressBar.initProgressBar(progressUrl, {
onSuccess: customSuccess,
onError: customError,
});
});
</script>
i tried it with the same app. it works! :)
don't forget to change this one to a valid url:
<img src="/static/projects/images/aww-yeah.jpg">)
Related
I am trying to add a class to an element in one component (nav) when an animation in a different component (logo) ends. In my $lib/Logo.svelte file I have the following code:
<script>
import { onMount } from 'svelte';
import { isLogoAnimationEnded } from './stores';
onMount(() => {
const body = document.querySelector('body');
const h1 = document.querySelector('.name');
h1?.addEventListener('animationend', () => {
isLogoAnimationEnded.update((n) => (n = true));
console.log($isLogoAnimationEnded);
body?.classList.add('shake');
});
return () => {
h1?.removeEventListener('animationend', () => {
isLogoAnimationEnded.set(false);
body?.classList.remove('shake');
});
};
});
</script>
<div class="logo-wrapper">
<h1 class="name">Tim Smith</h1>
<p class="title">Full Stack Web Engineer</p>
</div>
<style>
...
</style>
My
store is defined in $lib/stores.js:
import { writable } from 'svelte/store';
export const isLogoAnimationEnded = writable(false);
What I am trying to do is listen for a change in isLogoAnimationEnded in $lib/Nav.svelte and add a class to nav when isLogoAnimationEnded becomes true.
<script lang="ts">
import { onMount } from 'svelte';
import { isLogoAnimationEnded } from './stores';
let nav;
onMount(() => {
nav = document.querySelector('nav');
console.log('nav', nav);
});
if ($isLogoAnimationEnded) {
nav?.classList.add('fly-down');
}
</script>
<div class="nav-wrapper">
<nav aria-label="Main">
<ul>
<li>About</li>
<li>Projects</li>
<li>Contact</li>
</ul>
</nav>
</div>
<style>
...
</style>
My current setup does not work. Please help.
The code below is only going to run once
if ($isLogoAnimationEnded) {
nav?.classList.add('fly-down');
}
So I would delete it and write the class attribute on nav element like this:
<nav class={$isLogoAnimationEnded ? "fly-down" : ""} aria-label="Main">
This also gets rid of the onMount, nav variable and querySelector
I am doing a search page in which parameters are sent by ajax and then upon reception of the queryset I rebuild my cards. The whole thing is classic and working ok, here is a simplified version of the thing. Lots of lines killed or modified since it is not really the subject of the post
let getobject = async (value,url) => {
var res2 = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
"X-CSRFToken": getCookie("csrftoken"),
},
body: JSON.stringify({
value: value,
})
})
let data2 = await res2.json();
videoitems.innerHTML = ''
modalbin.innerHTML = ''
data2["data"].forEach(async item => {
if (item.ext == '.mp4') {
const dynamicreation = async () => {
let dyncontent3 = await createnewcard(item)
let placing = await videoitems.appendChild(dyncontent3);
}
const nooncares2 = await dynamicreation()
} else if (item.ext == ".pdf") {
const dynamicreation2 = async () => {
let dyncontent4 = await createnewcard(item)
let placing2 = await videoitems.appendChild(dyncontent4);
}
const nooncares4 = dynamicreation2()
}
})
}
the createnewcard function
var createnewcard = item => {
var dyncontent = document.createElement("div");
dyncontent.innerHTML =
`<div class="m-2 extralarge-modal video${item.id}">
<div data-reco="${item.id}"
class="extralarge-modal bg-white rounded-lg border border-gray-200 shadow-md dark:bg-gray-800 dark:border-gray-700">
<div class="p-5">
<p class="mb-3 font-normal text-gray-700 dark:text-gray-400">
${item.title}
</p>
</div>
</div>
</div>`;
return dyncontent
}
What I would like to know is if it would be possible to mix this js with the django "include" function and instead of using js template litterals use an html component of the card that I would include upon looping in the data reveived. I could also maybe include it inside the createnewcard js function but so far it all failed quite miserably.
Thanks a lot
Yes, you've to create card.html or whatever you can name then you've to just include inside your js make sure you use same variables while looping in js eg.(item)
card.html
<div class="m-2 extralarge-modal video${item.id}">
<div data-reco="${item.id}"
class="extralarge-modal bg-white rounded-lg border border-gray-200 shadow-md dark:bg-gray-800 dark:border-gray-700">
<div class="p-5">
<p class="mb-3 font-normal text-gray-700 dark:text-gray-400">
${item.title}
</p>
</div>
</div>
</div>
and inside your Js do like this
var createnewcard = item => {
var dyncontent = document.createElement("div");
dyncontent.innerHTML = `{% include "card.html" %}`
return dyncontent
}
I'm trying to learn this tutorial, the custom payment flow last bit to integrate stripe with Django
https://justdjango.com/blog/django-stripe-payments-tutorial
in my views.py, I have these views
class StripeIntentView(View):
def post(self, request, *args, **kwargs):
try:
req_json = json.loads(request.body)
customer = stripe.Customer.create(email=req_json['email'])
price = Price.objects.get(id=self.kwargs["pk"])
intent = stripe.PaymentIntent.create(
amount=price.price,
currency='usd',
customer=customer['id'],
metadata={
"price_id": price.id
}
)
return JsonResponse({
'clientSecret': intent['client_secret']
})
except Exception as e:
return JsonResponse({'error': str(e)})
class CustomPaymentView(TemplateView):
template_name = "custom_payment.html"
def get_context_data(self, **kwargs):
product = Product.objects.get(name="Test Product")
prices = Price.objects.filter(product=product)
context = super(CustomPaymentView, self).get_context_data(**kwargs)
context.update({
"product": product,
"prices": prices,
"STRIPE_PUBLIC_KEY": settings.STRIPE_PUBLIC_KEY
})
return context
and in my urls I have
from django.contrib import admin
from django.urls import path
from products.views import stripe_webhook
from products.views import StripeIntentView, CustomPaymentView
urlpatterns = [
path('admin/', admin.site.urls),
path('create-payment-intent/<pk>/', StripeIntentView.as_view(), name='create-payment-intent'),
path('custom-payment/', CustomPaymentView.as_view(), name='custom-payment')
and in my custom_payment.html I have
{% load static %}
<!DOCTYPE html>
<html>
<head>
<title>Custom payment</title>
<script src="https://polyfill.io/v3/polyfill.min.js?version=3.52.1&features=fetch"></script>
<script src="https://js.stripe.com/v3/"></script>
<link rel="stylesheet" href="{% static 'products/global.css' %}">
</head>
<body>
<section>
<div class="product">
<div class="description">
<h3>{{ product.name }}</h3>
<hr />
<select id='prices'>
{% for price in prices %}
<option value="{{ price.id }}">${{ price.get_display_price }}</option>
{% endfor %}
</select>
</div>
<form id="payment-form">{% csrf_token %}
<input type="text" id="email" placeholder="Email address" />
<div id="card-element">
<!--Stripe.js injects the Card Element-->
</div>
<button id="submit">
<div class="spinner hidden" id="spinner"></div>
<span id="button-text">Pay</span>
</button>
<p id="card-error" role="alert"></p>
<p class="result-message hidden">
Payment succeeded, see the result in your
Stripe dashboard. Refresh the page to
pay again.
</p>
</form>
</div>
</section>
<script>
var csrftoken = document.querySelector('[name=csrfmiddlewaretoken]').value;
var stripe = Stripe("{{ STRIPE_PUBLIC_KEY }}");
document.querySelector("button").disabled = true;
var elements = stripe.elements();
var style = {
base: {
color: "#32325d",
fontFamily: 'Arial, sans-serif',
fontSmoothing: "antialiased",
fontSize: "16px",
"::placeholder": {
color: "#32325d"
}
},
invalid: {
fontFamily: 'Arial, sans-serif',
color: "#fa755a",
iconColor: "#fa755a"
}
};
var card = elements.create("card", { style: style });
// Stripe injects an iframe into the DOM
card.mount("#card-element");
card.on("change", function (event) {
// Disable the Pay button if there are no card details in the Element
document.querySelector("button").disabled = event.empty;
document.querySelector("#card-error").textContent = event.error ? event.error.message : "";
});
var form = document.getElementById("payment-form");
form.addEventListener("submit", function(event) {
event.preventDefault();
var selectedPrice = document.getElementById("prices").value
// Complete payment when the submit button is clicked
fetch(`/create-payment-intent/${selectedPrice}/`, {
method: "POST",
headers: {
"Content-Type": "application/json",
'X-CSRFToken': csrftoken
},
body: JSON.stringify({
email: document.getElementById('email').value
})
})
.then(function(result) {
return result.json();
})
.then(function(data) {
payWithCard(stripe, card, data.clientSecret);
});
});
// Calls stripe.confirmCardPayment
// If the card requires authentication Stripe shows a pop-up modal to
// prompt the user to enter authentication details without leaving your page.
var payWithCard = function(stripe, card, clientSecret) {
loading(true);
stripe
.confirmCardPayment(clientSecret, {
payment_method: {
card: card
}
})
.then(function(result) {
if (result.error) {
// Show error to your customer
showError(result.error.message);
} else {
// The payment succeeded!
orderComplete(result.paymentIntent.id);
}
});
};
/* ------- UI helpers ------- */
// Shows a success message when the payment is complete
var orderComplete = function(paymentIntentId) {
loading(false);
document
.querySelector(".result-message a")
.setAttribute(
"href",
"https://dashboard.stripe.com/test/payments/" + paymentIntentId
);
document.querySelector(".result-message").classList.remove("hidden");
document.querySelector("button").disabled = true;
};
// Show the customer the error from Stripe if their card fails to charge
var showError = function(errorMsgText) {
loading(false);
var errorMsg = document.querySelector("#card-error");
errorMsg.textContent = errorMsgText;
setTimeout(function() {
errorMsg.textContent = "";
}, 4000);
};
// Show a spinner on payment submission
var loading = function(isLoading) {
if (isLoading) {
// Disable the button and show a spinner
document.querySelector("button").disabled = true;
document.querySelector("#spinner").classList.remove("hidden");
document.querySelector("#button-text").classList.add("hidden");
} else {
document.querySelector("button").disabled = false;
document.querySelector("#spinner").classList.add("hidden");
document.querySelector("#button-text").classList.remove("hidden");
}
};
</script>
</body>
</html>
The tutorial was missing a csrf token so I added that and the card element loaded up, and also I had to add an id of prices to the select
Then I got this error
(index):1 Uncaught (in promise) IntegrationError: Missing value for stripe.confirmCardPayment intent secret: value should be a client_secret string.
at X ((index):1)
at Q ((index):1)
at uo ((index):1)
at (index):1
at (index):1
at e.<anonymous> ((index):1)
at e.confirmCardPayment ((index):1)
at payWithCard ((index):104)
at (index):94
Can anyone help me with this ? Thanks
i would suggest adding an additional line to check what is the value of data (and data.clientSecret). It looks like clientSecret may not have a value, or may not be a string.
.then(function(data) {
console.log(data);
payWithCard(stripe, card, data.clientSecret);
});
You would then need to trace why clientSecret does not have the expected value.
I need to compare a value (variable) extracted from a page to context.
For example:
Color is a dropdown selection.
$(document).ready(function(){
$("#color").change(function() {
var selected_color = $(this).val() ;
{% if context_value == selected_color %}
.. do something
{% endif %}
})};
Is this possible ? If not, is there some solution for such case ?
I recommend you use Ajax to communicate asynchronously between JavaScript and python (without refreshing the page).
your JS:
$(document).ready(function(){
$("#color").change(function() {
var selected_color = $(this).val() ;
$.ajax({
method: "POST",
url: 'color_check',
data: selected_color,
success: handleFormSuccess,
error: handleFormError,
})
})
function handleFormSuccess(data, textStatus, jqXHR){
.. do something
}
function handleFormError(jqXHR, textStatus, errorThrown){}
};
Your python view:
def color_check(request):
if request.is_ajax():
selected_color = request.POST
context_value = 'Red'
if selected_color == context_value:
return JsonResponse(True)
EDIT: Arun Singh's solution is simpler and works too. I would only make the paragraph hidden from the user:
<p style="display:none" id="my-data" data-name="{{context_value}}"></p>
<p id='data'>{{ context_value }}</p>
or
<p id="my-data" data-name="{{context_value}}"></p>
$(document).ready(function(){
$("#color").change(function() {
var selected_color = $(this).val() ;
var djangoData = $('#data').val();
if (djangoData === selected_color){
console.log('do something)
}else{
console.log('do something else')
}
})};
I'd like to transition one element as it changes to another element.
I've got 3 examples:
one that works, but uses a list of items that are kept around (jsfiddle)
one that doesnt work, and only keeps one item around, depending on the state (jsfiddle)
another one that doesn't work, that keeps both items around and hides/shows them (jsfiddle using hide/show)
What I want is more like the second one, which is a very slight variation of the first attempt that works.
Option 1:
/** #jsx React.DOM */
var ReactTransitionGroup = React.addons.TransitionGroup;
var TodoList = React.createClass({
getInitialState: function() {
return {items: ['hello', 'world', 'click', 'me']};
},
handleAdd: function() {
var newItems =
this.state.items.concat([prompt('Enter some text')]);
this.setState({items: newItems});
},
handleRemove: function(i) {
var newItems = this.state.items;
newItems.splice(i, 1)
this.setState({items: newItems});
},
render: function() {
var items = this.state.items.map(function(item, i) {
return (
<div key={item} onClick={this.handleRemove.bind(this, i)}>
{item}
</div>
);
}.bind(this));
return (
<div>
<div><button onClick={this.handleAdd} /></div>
<ReactTransitionGroup transitionName="example">
{items}
</ReactTransitionGroup>
</div>
);
}
});
var app = React.renderComponent(<TodoList />, document.body);
Option 2:
JSX that doesn't work, but is closer to what I'd like to do (really, hide one view, and show another)
/** #jsx React.DOM */
var ReactTransitionGroup = React.addons.TransitionGroup;
var Test = React.createClass({
getInitialState: function() {
return {showOne:true}
},
onClick: function() {
this.setState({showOne:! this.state.showOne});
},
render: function() {
var result;
if (this.state.showOne)
{
result = <div ref="a">One</div>
}
else
{
result = <div ref="a">Two</div>
}
return (
<div>
<div><button onClick={this.onClick}>switch state</button></div>
<ReactTransitionGroup transitionName="example">
{result}
</ReactTransitionGroup>
</div>
);
}
});
var app = React.renderComponent(<Test />, document.body);
Option 3:
Uses hide/show to keep the 2 views around, but still doesn't work.
/** #jsx React.DOM */
var ReactTransitionGroup = React.addons.TransitionGroup;
var Test = React.createClass({
getInitialState: function() {
return {showOne:true}
},
onClick: function() {
this.setState({showOne:! this.state.showOne});
},
render: function() {
var result;
var c1 = this.state.showOne ? "hide" : "show";
var c2 = this.state.showOne ? "show" : "hide";
return (
<div>
<div><button onClick={this.onClick}>switch state</button></div>
<ReactTransitionGroup transitionName="example">
<div className={c1}>One</div>
<div className={c2}>Two</div>
</ReactTransitionGroup>
</div>
);
}
});
var app = React.renderComponent(<Test />, document.body);
So long story short - How can I make a transition execute on switching from one main "component" to another? I don't get why option 1 works, but option 2 doesn't!
React is just changing the content of the DOM because that's all that changed. Give the elements unique keys to make them animate.
if (this.state.showOne)
{
result = <div key="one">One</div>
}
else
{
result = <div key="two">Two</div>
}
JSFiddle
I used Michelle Treys answer to solve a similar problem using React-Router (1.0.1). Its not clear from the api that the key is needed. I was following React-routers suggestion to render a routes children in a parent as follows:
render() {
return (
<div id='app-wrapper'>
<ReactTransitionGroup component='div' className='transition-wrapper'>
{this.props.children}
</ReactTransitionGroup>
</div>
);
}
However the componentWillEnter only triggered on page load. Following Michelle's solution, I cloned a the children as per the react-router updates and added a key as follows:
render() {
const { location } = this.props;
return (
<div id='app-wrapper'>
<ReactTransitionGroup component='div' className='transition-wrapper'>
{React.cloneElement(this.props.children, {
key: location.pathname,
})}
</ReactTransitionGroup>
</div>
);
}
Thanks for the fix. Cheers