JQuery, how to pass the slug variable - javascript

I'd like to know how I can pass the slug variable into JQuery/javascript.
I have a data-table with items, on each row there are two buttons, one (1) is using Django to create a product, the other (2) is supposed to use JQuery / JS to create a product.
To create a product with button 1 I find being straight forward and well explained.
I'd like to perform the same action on button 2, using JQuery/JS.
button 1
<button type="button" class="btn btn-secondary"><i class="fa fa-pencil"></i></button>
with urls path:
path("products/<uuid:slug>/create/", ProductsCreateView.as_view(), name="products-create"),
views.py
class ProductsCreateView(CreateView):
model = Products
template_name = "products_create.html"
slug_field = "products_uid"
button 2
<button class="btn btn-secondary js-create-button" data-url="{% url 'api-products-create' object.products_uid %}" type="button"><span class="fa fa-pencil"></span></button>
with urls path:
path('api/products/<uuid:slug>/create/', ProductsCreateView.as_view(), name="api-products-create"),
with js (truncated function)
$(function () {
var createProduct = function() {
var slug = '{{ $slug }}'; /* some code/function that gets hold of the slug */
const url = `/api/v1/products/${slug}/create/`;
$.get(url)
.done(function pollAsyncResults(data) {
// bind pollAsyncResults to itself to avoid clashing with
// the prior get request
context: this
// see the URL setup for where this url came from
const pollAsyncUrl = `/api/products/poll_async_results/${data.task_id}`;
})
};
$(".js-create-button").click(createProduct);
});

With help of #31piy answer to this SO post: How to console.log value of a data-attribute in jquery?
To select all the elements which have data-group attribute, you can
loop over the elements selected by the selector [data-group], and in
the iteration, get the value of the attribute using the data method.
Please see the example below:
$('[data-group]').each(function() {
console.log($(this).data('group'));
})
I figured out that the const url could be expressed as:
const url = $(this).data('url');
When logging it in the console, I will get the string I wanted
/api/products/7d49e267-f35d-4161-8eb5-0a815abbb083/create/
Thus, the complete code would become:
$(function () {
var createProduct = function() {
const url = $(this).data('url');
$.get(url)
.done(function pollAsyncResults(data) {
// bind pollAsyncResults to itself to avoid clashing with
// the prior get request
context: this
// see the URL setup for where this url came from
const pollAsyncUrl = `/api/products/poll_async_results/${data.task_id}`;
})
};
$(".js-create-button").click(createProduct);
});

Related

JavaScript stops functioning after adding type=module to src tag in HTML (Using Flask)

What I thought would be the easiest part of my project has turned into a Herculean effort. All I wanted to do was get data from a JSON file to then display on my website. Prior to using a JSON file, I hard coded some data to test my filter/search functionality, all of which I wrote in JavaScript. The code worked perfectly, so I decided to move the data to a JSON file as I am expecting to have a lot more data in the future and can't have it hardcoded. However, I have been unable to get data from the JSON file successfully. I tried using require('./data.json'), but apparently I can't just use require like that. I then tried importing the file, which only works if I go back to the html and add type="module" to the src tag. This then allows all of the data to display on the webpage, however, the function that allows me to filter by category no longer works. When I click on the buttons, I get no response. I used Inspect to get the console to find the error, and the output is:
Uncaught ReferenceError: filterProject is not defined
The search functionality still works, and I suspect this is because that code isn't inside a function. Thus, I don't know why filterProject is supposedly not defined when the other JS code works. Here is all of my code:
import projects from './data.json' assert { type: "json" };
const path = "http://localhost/static/images/";
//ADDING THE HTML, IGNORE
for(let i of projects){
let card = document.createElement("div");
card.classList.add("card", i["category"], "hide");
let imgContainer = document.createElement("div");
imgContainer.classList.add("image-container");
let imageOne = document.createElement("img");
imageOne.setAttribute("src", path.concat(i["imageOne"]));
imgContainer.appendChild(imageOne);
card.appendChild(imgContainer);
let container = document.createElement("div");
container.classList.add("container");
let name = document.createElement("h3");
name.classList.add("project-name");
name.innerText = i["projectName"].toUpperCase();
container.appendChild(name);
let student = document.createElement("h4");
student.classList.add("student-name");
student.innerText = i["studentName"].toUpperCase() + " mentored by " + i["mentor"].toUpperCase();
container.appendChild(student);
let category = document.createElement("h6");
category.innerText = i["category"].toUpperCase().replace("_", " ");
container.appendChild(category);
card.appendChild(container);
document.getElementById("projects").appendChild(card);
}
//FILTERING (DOESNT WORK)
function filterProject(value){
let buttons = document.querySelectorAll(".button-value");
buttons.forEach((button) => {
if(value.toUpperCase() == button.innerText.toUpperCase()){
button.classList.add("active");
}else{
button.classList.remove("active");
}
});
let elements = document.querySelectorAll(".card");
elements.forEach((element) => {
if(value == "all"){
element.classList.remove("hide");
}
else{
//having a space messes it up, make it _
if(element.classList.contains(value.replace(" ", "_"))){
element.classList.remove("hide");
}
else{
element.classList.add("hide");
}
}
});
}
//SEARCH (WORKS)
document.getElementById("search").addEventListener
("click", () => {
let searchInput = document.getElementById("search-input").value;
let elements = document.querySelectorAll(".student-name");
let cards = document.querySelectorAll(".card");
elements.forEach((element, index) =>{
if(element.innerText.includes(searchInput.toUpperCase())){
cards[index].classList.remove("hide");
}
else{
cards[index].classList.add("hide");
}
});
});
//INTIAL STATE
window.onload = () =>{
filterProject("all");
};
Here is the HTML just in case as well:
<div class ="wrapper">
<div id="search-container">
<input type="search" id="search-input" placeholder="Search student name here..."/>
<button id = "search">Search</button>
</div>
<div id ="buttons">
<button class = "button-value" onclick="filterProject('all')">All</button>
<button class = "button-value" onclick="filterProject('Creative Project')">Creative Project</button>
<button class = "button-value" onclick="filterProject('Developing Voice')">Developing Voice</button>
<button class = "button-value" onclick="filterProject('Interdisciplinary Fusion')">Interdisciplinary Fusion</button>
<button class = "button-value" onclick="filterProject('Personal Writing')">Personal Writing</button>
<button class = "button-value" onclick="filterProject('Curriculum Designer')">Curriculum Designer</button>
<button class = "button-value" onclick="filterProject('Internship')">Internship</button>
</div>
<div id = projects></div>
</div>
<script type = "module" src = "{{ url_for('static',filename='javascript/script.js') }}"></script>
If it matters, I am using Flask as my web framework. I'm not sure if that has any impact on anything, but it has created some obstacles when I've tried to create a live server to solve this issue. Thanks in advance for any replies!
What you're looking for is how to load json files locally.
One solution is
Start a local server e.g. http://localhost:8080
Then use fetch() to retrieve the json file
For example, if your data.json file was located within the same folder where you have your html file and where you started your server, then your code could be something like
fetch("http://localhost:8080/data.json")
.then((response) => {
return response.json();
})
.then((data) => {
// Add code to process your data
})

Split API to use its part as a variable

I am using an API that has a lot of different possible calls. I want to change a part of it in VUE so that depending on a different button different call is made.
https://api.themoviedb.org/3/movie/top_rated?api_key=<>&language=en-US&page=1
https://api.themoviedb.org/3/movie/upcoming?api_key=<>&language=en-US&page=1
Here are two different API calls that are possible and I want to make it so that it looks something like this:
"https://api.themoviedb.org/3/movie/" +variable+ "?api_key=<>&language=en-US&page=1"
and then depending on which button I pressed it would either make a call for "top_rated" or if another button is pressed it would be "upcoming"
You can use String literal ` ` to insert dynamic value.
<button #click="apiCall(true)">Upcoming</button>
<button #click="apiCall(false)">Top Rated</button>
...
methods: {
apiCall(isUpcoming) {
const apiType = isUpcoming ? 'upcoming' : 'top_rated';
const url = `https://api.themoviedb.org/3/movie/${apiType}?api_key=<>&language=en-US&page=1`;
// Fetch data with url.
...
}
}
There are many ways to archive this. Without the rest of the code I cannot suggest much.
Eventually you can create a method in your VUE component that returns the "top_rated" or "upconmig".
<template>
<button :href="'https://api.themoviedb.org/3/movie/' + getAction + '/?yourqueryparams'"></button>
</template>
<script>
export default {
name: 'Movie DB',
methods: {
getAction: function(){
return "top_rated"; //put condition here
}
}
}
</script>
I am not sure I understand your question, but maybe you need this:
vue template:
<button #click=handler('top_rated')>this is button no.1</button>
<button #click=handler('upcoming')>this is button no.2</button>
javascript:
methods: {
handler(type) {
const url = "https://api.themoviedb.org/3/movie/" + type + "?api_key=<>&language=en-US&page=1"
return fetch(url)
}
}

how to send a modal form and a normal form together using ajax

Please
I have a form that takes a parent detail and another form which is a modal form that takes a minimal data of a particular student at the point of creating a parent. I want to be able to send the two details at the same time to the controller that handles it in the backend.
This is what I have been trying :
$("#plus").click(function () {
var student = {
"firstName": $("#sfirstName").val(),
"lastName" : $("#slastName").val(),
"middleName" : $("#smiddleName").val(),
}
console.log(student)
});
This manages the modal form and this for the normal form on the page:
$("#addParent").click(function () {
var parentForm = new FormData($('#parentForm')[0]);
parentForm.append("firstName",student[sfirstName]);
parentForm.append("middlesName", student[smiddleName]);
parentForm.append("lastName", student[slastName]);
console.log(parentForm);
})
before I will now send the forms as one the back end using Ajax... But it doesn't seem to be working ... Thanks in advance
Try to fix like this:
$("#plus").click(function () {
var student = {
firstName: $("#sfirstName").val(),
lastName : $("#slastName").val(),
middleName : $("#smiddleName").val()
} // fixed <-- we create an object literal in javascript with name-value pairs wrapped in curly braces
console.log(student)
});
And then:
$("#addParent").click(function () {
var parentForm = new FormData($('#parentForm')[0]);
parentForm.append("firstName",student[firstName]); //fixed typo
parentForm.append("middlesName", student[middleName]); //fixed typo
parentForm.append("lastName", student[lastName]); //fixed typo
console.log(parentForm);
})

How to perform .delete() queryset in Django in a ListView?

Here is what I've done so far:
1.) I've made a javascript function that gets all the id's of the items (using checkbox select) in the database like so (this is DataTables):
function () {
// count check used for checking selected items.
var count = table.rows( { selected: true } ).count();
// Count check.
// Count must be greater than 0 to delete an item.
// if count <= 0, delete functionality won't continue.
if (count > 0) {
var data = table.rows( { selected: true } ).data();
var list = [];
for (var i=0; i < data.length ;i++){
// alert(data[i][2]);
list.push(data[i][2]);
}
var sData = list.join();
// alert(sData)
document.getElementById('delete_items_list').value = sData;
}
}
It outputs something like 1,2,5,7 depending on what rows I have selected.
2.) Passed the values inside a <input type="hidden">.
Now, I've read a post that says you can delete data in Django database using a checkbox, but I'm not sure how exactly can I use this.
I'm guessing I should put it in the ListView that I made, but how can I do that when I click the "Delete selected items" button, I can follow this answer?
I'm trying to achieve what Django Admin looks like when you delete items.
My ListView looks like this:
Yes you can use linked example. Django Admin do it the same way, You send selected ids and django do filtering by given values and after django apply selected action for selected items.
UPDATE
For example.
class List(ListView);
def post(self, request, *args, **kwargs):
ids = self.request.POST.get('ids', "")
# ids if string like "1,2,3,4"
ids = ids.split(",")
try:
# Check ids are valid numbers
ids = map(int, ids)
except ValueError as e:
return JsonResponse(status=400)
# delete items
self.model.objects.filter(id__in=ids).delete()
return JsonResponse({"status": "ok"}, status=204)
And html:
<button id="delete-button">Del</button>
<div id="items-table">
{% for object in objects_list %}
<div class="item" data-id="{{object.id}}">{{ object.name }}</div>
{% endfor %}
</div>
<script>
$(function(){
$('#delete-button').on('click', function(e) {
// Get selected items. You should update it according to your template structure.
var ids = $.map($('#items-table item'), function(item) {
return $(item).data('id')
}).join(',');
$.ajax({
type: 'POST',
url: window.location.href ,
data: {'ids': ids},
success: function (res) {
// Update page
window.location.href = window.location.href;
},
error: function () {
// Display message or something else
}
});
})
})();
</script>

Populate data from groovy controller in pop up

I have a gsp page with a delete button for each row of a table. On the button click I want a pop up which tells the consequences of the delete. These consequences depends on the data present in the row and a few other constraints known to the grails service which is called from the grails controller associated to the gsp page. If the user confirms these consequences the row should be deleted from the table, else the table remains unchanged.
How should i go about to achieve this behavior?
Currently, I have in my gsp
<tr>
<td>name</td>
<td>parentName</td>
<td>data</td>
<td>
<g:link action="deleteRow" params="${[name: row.name, parentName: row.parentName]}">
<button class="deleteSnapshot">Delete</button>
</g:link>
</td>
</tr>
and in my .js file
$(document).on('click', ':button', function (e) {
var btn = $(e.target);
btn.attr("disabled", "disabled"); // disable button
alert('getting deletion details');
//var deletionDetails -->not sure how to get these
//var deletionDetails will get data from controller action:"getDetails"
if (confirm('/*print deletion details and ask*/ Do you want to proceed?')) {
alert('will delete')
return true
}
else {
btn.removeAttr("disabled"); // enable button
return false
}
});
and in my controller
class DeleteController{
DeleteService deleteService
def index() {
[list:deleteService.getTableList()]
}
def getDeletionDetails(string name, String parentName){
return deleteService.getDetails(name,parentName)
}
def deleteRow(String name, String parentName){
service.deleteRow(name, parentName)
redirect controller:"DeleteController", action:"index"
}
}
I know the deletion works fine, because it works even with in the current state. Just that the confirmation box asks Do you want to proceed, without displaying the details.
Any help on how i could achieve what I am looking for will be appreciated.
P.S. I am new to stackoverflow, so if i missed out on certain convention do let me know.
Thanks in advance.
I can think of two ways of doing it:
The first one is using ajax to both get deletion details and delete the row
Assuming that deleteService.getDetails(name, parentName) returns a String,
first you need to change an getDeletionDetails action so it renders the response:
def getDeletionDetails(String name, String parentName){
render deleteService.getDetails(name, parentName)
}
and change g:link-s to buttons in gsp:
<button data-name="${row.name}" data-parent-name="${row.parentName}">
Delete
</button>
In your .js then put:
$(document).on('click', ':button', function (e) {
var btn = $(e.target);
btn.attr("disabled", "disabled"); // disable button
var name = btn.data('name');
var parentName = btn.data('parentName');
$.ajax({
url: "/delete/getDeletionDetails",
data: {
name: name,
parentName: parentName
},
success: function (data) {
if (confirm(data + '\nDo you want to proceed?')) {
$.ajax({
url: '/delete/deleteRow',
data: {
name: name,
parentName: parentName
},
success: function (d) {
console.log("Success: " + d);
}
});
} else {
btn.removeAttr("disabled"); // enable button
}
}
});
});
What this code does is it sends an ajax call to /delete/getDeletionDetails, then uses its response (rendered by getDeletionDetails action in DeleteController) to show a confirmation alert. If user confirms the question, another ajax call is sent - now to deleteRow action of DeleteController - with parameters taken from data attributes of clicked button. If user cancels - nothing happens, except for reenabling a button.
Your deleteRow should only change the return statement - it also must render the response:
def deleteRow(String name, String parentName){
service.deleteRow(name, parentName)
render "You deleted an item $name - $parentName."
}
You don't need redirect here, because - thanks to using ajax - user will never leave delete/index. You can just display some kind of confirmation on page after successful ajax call.
The second option is to put deletion details in hidden fields or data- attributes in each row and then just retrieve them in js:
You can create a method getDeletionDetails() in row's domain class (presumably Row) that returns the details (using services in domain classes is not perfect, but is should work ok if the service is not very complex). Then, in your .gsp place:
<td>
<g:link action="deleteRow" params="${[name: row.name, parentName: row.parentName]}">
<button class="deleteSnapshot" data-details="${row.deletionDetails}">Delete</button>
</g:link>
</td>
You should then be able to get details in .js like this:
var deletionDetails = btn.data('details');

Categories

Resources