Aula 78 – Loja Online – Segurança e Organização dos Javascripts
Aula 78 – Loja Online – Segurança e Organização dos Javascripts
Voltar para página principal do blog
Todas as aulas desse curso
Aula 77 Aula 79
Redes Sociais:
Meus links de afiliados:
Hostinger
Digital Ocean
One.com
Melhore seu NETWORKING
https://digitalinnovation.one/
Participe de comunidades de desenvolvedores:
Fiquem a vontade para me adicionar ao linkedin.
E também para me seguir no https://github.com/toticavalcanti.
Código final da aula:
https://github.com/toticavalcanti
Quer aprender python3 de graça e com certificado? Acesse então:
https://workover.com.br/python-codigo-fluente
Canais do Youtube
Toti
Backing Track / Play-Along
Código Fluente
Putz!
Vocal Techniques and Exercises
PIX para doações
Aula 78 – Loja Online – Segurança e Organização dos Javascripts
Vou criar a branch js-organization-and-security para as mudanças que vamos fazer nessa aula.
Colocamos o javascript diretamente no django_ecommerce/e_commerce/templates/base.html.
O que vamos fazer é criar uma pasta dentro de django_ecommerce/e_commerce/static_local/ chamada js.
Dentro de django_ecommerce/e_commerce/static_local/js vamos criar um arquivo chamado ecommerce.js.
Dentro dele vamos colocar todo o javascript que tá no base, ou seja, vamos transferir esse Javascript de lugar.
django_ecommerce/e_commerce/static_local/js/ecommerce.js
$(document).ready(function(){
// Contact Form Handler
var contactForm = $(".contact-form")
var contactFormMethod = contactForm.attr("method")
var contactFormEndpoint = contactForm.attr("action")
function displaySubmitting(submitBtn, defaultText, doSubmit){
if (doSubmit){
submitBtn.addClass("disabled")
submitBtn.html("<i class='fa fa-spin fa-spinner'></i> Enviando...")
} else {
submitBtn.removeClass("disabled")
submitBtn.html(defaultText)
}
}
contactForm.submit(function(event){
event.preventDefault()
const contactFormSubmitBtn = contactForm.find("[type='submit']")
const contactFormSubmitBtnTxt = contactFormSubmitBtn.text()
const contactFormData = contactForm.serialize()
const thisForm = $(this)
displaySubmitting(contactFormSubmitBtn, "", true)
$.ajax({
method: contactFormMethod,
url: contactFormEndpoint,
data: contactFormData,
success: function(data){
contactForm[0].reset()
$.alert({
title: "Success!",
content: data.message,
theme: "modern",
})
setTimeout(function(){
displaySubmitting(contactFormSubmitBtn, contactFormSubmitBtnTxt, false)
}, 500)
},
error: function(error){
console.log(error.responseJSON)
const jsonData = error.responseJSON
let msg = ""
$.each(jsonData, function(key, value){
msg += key + ": " + value[0].message + "<br/>"
})
$.alert({
title: "Oops!",
content: msg,
theme: "modern",
})
setTimeout(function(){
displaySubmitting(contactFormSubmitBtn, contactFormSubmitBtnTxt, false)
}, 500)
}
})
})
// Auto Search
const searchForm = $(".search-form")
const searchInput = searchForm.find("[name='q']") // input name='q'
const typingTimer = 0;
const typingInterval = 500 // .5 seconds
const searchBtn = searchForm.find("[type='submit']")
searchInput.keyup(function(event){
//key released
clearTimeout(typingTimer)
typingTimer = setTimeout(performSearch, typingInterval)
})
searchInput.keydown(function(event){
// key pressed
clearTimeout(typingTimer)
})
function displaySearching(){
searchBtn.addClass("disabled")
searchBtn.html("<i class='fa fa-spin fa-spinner'></i> Searching...")
}
function performSearch(){
displaySearching()
var query = searchInput.val()
setTimeout(function(){
window.location.href='/search/?q=' + query
}, 1000)
}
//Cart + Add Product
const productForm = $(".form-product-ajax")
productForm.submit(function(event){
event.preventDefault();
// console.log("O formulário não foi enviado!");
// o this pega os dados relacionados a esse form
const thisForm = $(this);
//const actionEndpoint = thisForm.attr("action");
const actionEndpoint = thisForm.attr("data-endpoint");
const httpMethod = thisForm.attr("method");
const formData = thisForm.serialize();
$.ajax({
url: actionEndpoint,
method: httpMethod,
data: formData,
success: function(data){
// console.log("Sucesso")
// console.log(data)
// console.log("Adicionado", data.added)
// console.log("Removido", data.removed)
const submitSpan = thisForm.find(".submit-span")
if(data.added){
submitSpan.html("No carrinho <button type='submit' class='btn btn-link'>Excluir</button>")
} else {
submitSpan.html("<button type='submit' class='btn btn-success'>Adicionar</button>")
}
const navbarCount = $(".navbar-cart-count")
navbarCount.text(data.cartItemCount)
const currentPath = window.location.href
if(currentPath.indexOf("cart") != -1){
refreshCart()
}
},
error: function(errorData){
$.alert({
title: 'Oops!',
content: 'Ocorreu um erro, tente novamente mais tarde!',
theme: 'modern'
});
}
})
})
function refreshCart(){
//console.log("Excluído do carrinho atual!")
const cartTable = $(".cart-table")
const cartBody = cartTable.find(".cart-body")
//cartBody.html("<h1>Mudou!</h1>")
const productsRow = cartBody.find(".cart-product")
const currentUrl = window.location.href
const refreshCartUrl = '/api/cart/';
const refreshCartMethod = "GET";
const data = {};
$.ajax({
url: refreshCartUrl,
method: refreshCartMethod,
data: data,
success: function(data){
console.log(data)
const hiddenCartItemRemoveForm = $(".cart-item-remove-form")
if(data.products.length > 0){
productsRow.html(" ")
let i = data.products.length
$.each(data.products, function(index, value){
const newCartItemRemove = hiddenCartItemRemoveForm.clone()
newCartItemRemove.css("display", "block")
newCartItemRemove.find(".cart-item-product-id").val(value.id)
cartBody.prepend("<tr><th scope=\"row\">" + i +
"</th><td><a href='" + value.url + "'>" +
value.name + "</a>" + newCartItemRemove.html() + "</td><td>" + value.price + "</td></tr>")
i--
})
cartBody.find(".cart-subtotal").text(data.subtotal)
cartBody.find(".cart-total").text(data.total)
} else {
window.location.href = currentUrl
}
},
error: function(errorData){
console.log("Erro")
console.log(errorData)
}
})
}
})
E podemos tirar tudo que tá em vermelho no base.html.
e_commerce/templates/base.html
{% load static %}
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Base Template</title>
{% include 'base/css.html' %}
{% block base_head %}{% endblock base_head %}
</head>
<body>
{% include 'base/navbar.html' with nome_da_marca='Loja virtual' %}
<div class='container'>
{% block content %} {% endblock %}
</div>
{% include 'base/js.html' %}
<script>
$(document).ready(function(){
// Contact Form Handler
var contactForm = $(".contact-form")
var contactFormMethod = contactForm.attr("method")
var contactFormEndpoint = contactForm.attr("action")
function displaySubmitting(submitBtn, defaultText, doSubmit){
if (doSubmit){
submitBtn.addClass("disabled")
submitBtn.html("<i class='fa fa-spin fa-spinner'></i> Enviando...")
} else {
submitBtn.removeClass("disabled")
submitBtn.html(defaultText)
}
}
contactForm.submit(function(event){
event.preventDefault()
const contactFormSubmitBtn = contactForm.find("[type='submit']")
const contactFormSubmitBtnTxt = contactFormSubmitBtn.text()
const contactFormData = contactForm.serialize()
const thisForm = $(this)
displaySubmitting(contactFormSubmitBtn, "", true)
$.ajax({
method: contactFormMethod,
url: contactFormEndpoint,
data: contactFormData,
success: function(data){
contactForm[0].reset()
$.alert({
title: "Success!",
content: data.message,
theme: "modern",
})
setTimeout(function(){
displaySubmitting(contactFormSubmitBtn, contactFormSubmitBtnTxt, false)
}, 500)
},
error: function(error){
console.log(error.responseJSON)
const jsonData = error.responseJSON
let msg = ""
$.each(jsonData, function(key, value){
msg += key + ": " + value[0].message + "<br/>"
})
$.alert({
title: "Oops!",
content: msg,
theme: "modern",
})
setTimeout(function(){
displaySubmitting(contactFormSubmitBtn, contactFormSubmitBtnTxt, false)
}, 500)
}
})
})
// Auto Search
const searchForm = $(".search-form")
const searchInput = searchForm.find("[name='q']") // input name='q'
const typingTimer = 0;
const typingInterval = 500 // .5 seconds
const searchBtn = searchForm.find("[type='submit']")
searchInput.keyup(function(event){
// key released
clearTimeout(typingTimer)
typingTimer = setTimeout(performSearch, typingInterval)
})
searchInput.keydown(function(event){
// key pressed
clearTimeout(typingTimer)
})
function displaySearching(){
searchBtn.addClass("disabled")
searchBtn.html("<i class='fa fa-spin fa-spinner'></i> Searching...")
}
function performSearch(){
displaySearching()
var query = searchInput.val()
setTimeout(function(){
window.location.href='/search/?q=' + query
}, 1000)
}
//Cart + Add Product
const productForm = $(".form-product-ajax")
productForm.submit(function(event){
event.preventDefault();
// console.log("O formulário não foi enviado!");
// o this pega os dados relacionados a esse form
const thisForm = $(this);
//const actionEndpoint = thisForm.attr("action");
const actionEndpoint = thisForm.attr("data-endpoint");
const httpMethod = thisForm.attr("method");
const formData = thisForm.serialize();
$.ajax({
url: actionEndpoint,
method: httpMethod,
data: formData,
success: function(data){
// console.log("Sucesso")
// console.log(data)
// console.log("Adicionado", data.added)
// console.log("Removido", data.removed)
const submitSpan = thisForm.find(".submit-span")
if(data.added){
submitSpan.html("No carrinho <button type='submit' class='btn btn-link'>Excluir</button>")
} else {
submitSpan.html("<button type='submit' class='btn btn-success'>Adicionar</button>")
}
const navbarCount = $(".navbar-cart-count")
navbarCount.text(data.cartItemCount)
const currentPath = window.location.href
if(currentPath.indexOf("cart") != -1){
refreshCart()
}
},
error: function(errorData){
$.alert({
title: "Oops!",
content: "Ocorreu um erro, tente mais tarde novamente!",
theme: "modern",
})
console.log("Erro")
console.log(errorData)
}
})
})
function refreshCart(){
//console.log("Excluído do carrinho atual!")
const cartTable = $(".cart-table")
const cartBody = cartTable.find(".cart-body")
//cartBody.html("<h1>Mudou!</h1>")
const productsRow = cartBody.find(".cart-product")
const currentUrl = window.location.href
const refreshCartUrl = '/api/cart/';
const refreshCartMethod = "GET";
const data = {};
$.ajax({
url: refreshCartUrl,
method: refreshCartMethod,
data: data,
success: function(data){
console.log(data)
const hiddenCartItemRemoveForm = $(".cart-item-remove-form")
if(data.products.length > 0){
productsRow.html(" ")
let i = data.products.length
$.each(data.products, function(index, value){
const newCartItemRemove = hiddenCartItemRemoveForm.clone()
newCartItemRemove.css("display", "block")
newCartItemRemove.find(".cart-item-product-id").val(value.id)
cartBody.prepend("<tr><th scope=\"row\">" + i +
"</th><td><a href='" + value.url + "'>" +
value.name + "</a>" + newCartItemRemove.html() + "</td><td>" + value.price + "</td></tr>")
i--
})
cartBody.find(".cart-subtotal").text(data.subtotal)
cartBody.find(".cart-total").text(data.total)
} else {
window.location.href = currentUrl
}
},
error: function(errorData){
$.alert({
title: "Oops!",
content: "Ocorreu um erro, tente mais tarde novamente!",
theme: "modern",
})
console.log("Erro")
console.log(errorData)
}
})
}
})
</script>
</body>
</html>
O base.html agora tá limpo, dessa forma o código vai ficar melhor organizado em pastas e arquivos específicos.
e_commerce/templates/base.html
{% load static %}
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Base Template</title>
{% include 'base/css.html' %}
{% block base_head %}{% endblock base_head %}
</head>
<body>
{% include 'base/navbar.html' with nome_da_marca='Loja virtual' %}
<div class='container'>
{% block content %} {% endblock %}
</div>
{% include 'base/js.html' %}
</body>
</html>
Agora no js.html, vamos colocar o conteúdo que tá em azul, logo abaixo.
django_ecommerce/e_commerce/templates/base/js.html
{% load static %}
<!-- Optional JavaScript -->
<!-- jQuery first, Popper.js, Bootstrap JS and jquery-confirm JS -->
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-confirm/3.3.2/jquery-confirm.min.js"></script>
<!-- eCommerce Custom JS -->
<script src='{% static "js/ecommerce.js" %}'></script>
Toda vez que adicionamos alguma coisa aos estáticos do projeto, precisamos rodar o collectstatic com o comando:
python manage.py collectstatic
Digite Yes e pronto, os estáticos foram copiados.
Esse comando vai fazer uma cópia dos arquivos estáticos da pasta django_ecommerce/e_commerce/static_local/ para a pasta django_ecommerce/static_cdn/.
Teste o projeto e veja se tudo continua funcionando.
Seguindo!
Agora, para usar o JQuery com segurança em projetos Django, precisamos fazer algumas coisas.
Usaremos Tokens CSRF.
Vamos criar um arquivo chamado csrf.ajax.js.
django_ecommerce/e_commerce/static_local/js/csrf.ajax.js
$(document).ready(function(){
// using jQuery
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
const cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
const csrftoken = getCookie('csrftoken');
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
// csrfSafeMethod() return false to POST and
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
// console.log("Settings Type: " + settings.type)
// console.log("CSRF TOKEN: " + csrftoken)
// console.log("XHR: " + xhr.global)
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
})
O xhr é uma função usada para criar o objeto XMLHttpRequest, o settings.type é o método, nesse caso, um POST, e o csrftoken o token que foi pego através da função getCookie().
Antes do JSON virar meio que padrão para troca de dados, o formato mais usado era o XML.
O XMLHttpRequest() é uma função do JavaScript que tornou possível obter dados de APIs que retornavam dados em XML.
O XMLHttpRequest() dá suporte a outros formatos de dados além do XML, inclusive o JSON e texto simples.
O Fetch API é uma versão mais simples e fácil de usar a XMLHttpRequest() para consumir recursos de modo assíncrono.
O Fetch permite que você trabalhe com as APIs REST com opções adicionais como cache de dados, leitura de respostas em streaming, etc.
A comparação if (!csrfSafeMethod(settings.type) && !this.crossDomain) no código acima, verifica se o método HTTP precisa de TOKEN CSRF, e se a request não é cross domain, ou seja, um domínio diferente do domínio do site.
Abra o js.html e adicione o js do csrf.ajax.js que acabamos de criar.
django_ecommerce/e_commerce/templates/base/js.html
{% load static %}
<!-- Optional JavaScript -->
<!-- jQuery first, Popper.js, Bootstrap JS and jquery-confirm JS -->
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-confirm/3.3.2/jquery-confirm.min.js"></script>
<!-- eCommerce Custom JS -->
<script src='{% static "js/ecommerce.js" %}'></script>
<!-- Django Secure AJAX JS -->
<script src='{% static "js/csrf.ajax.js" %}'></script>
Toda vez que adicionamos alguma coisa aos estáticos do projeto, precisamos rodar o collectstatic para fazer uma cópia desses estáticos para a pasta onde vai ficar os estáticos do projeto quando ele já tiver no ar.
python manage.py collectstatic
Digite yes e pronto, todos os estáticos que estão, no meu caso, na pasta django_ecommerce/e_commerce/static_local/ foram copiados para a pasta django_ecommerce/static_cdn/.
Nessa pasta irão ficar todos os estáticos de todos os apps que formam o projeto como um todo.
No caso desse projeto do eCommerce, temos basicamente uma pasta só, a static_local.
O processo do collectstatic é muito útil principalmente quando é preciso fazer deploy de projetos com várias pastas, de vários apps, com arquivos estáticos.
Ficamos por aqui e até a próxima. 😉
Voltar para página principal do blog
Todas as aulas desse curso
Aula 77 Aula 79
Código final da aula:
https://github.com/toticavalcanti
Canais do Youtube
Toti
Backing Track / Play-Along
Código Fluente
Putz!
Vocal Techniques and Exercises
Dêem um joinha 👍 na página do Código Fluente no
Facebook.
Sigam o Código Fluente no Instagram e no TikTok.
Código Fluente no Pinterest.
Meus links de afiliados:
Hostinger
Digital Ocean
One.com
Nos vemos na próxima então, \o/ 😉 Bons Estudos!