I'm trying to build my own comment system.
Here's the code that show a comment box when clicking on the "Reply" button:
function postComment(i, parentId) {
let content;
if (parentId === undefined) {
content = commentBox.value
} else {
content = eval("subCommentBox"+i).value
}
const body = JSON.stringify({
"post_slug": location.pathname.slice(1),
"username": "quantong",
"parent_id": parentId,
"content": content,
})
fetch("http://localhost:8080/comments", {
method: "post",
headers: { "Content-Type": "application/json" },
body: body
})
.then(resp => {
if (resp.status === 200) {
return resp.json()
} else {
console.log("Status: " + resp.status)
}
})
commentBox.value = "";
window.location.reload();
}
let allElements = document.body.getElementsByClassName("replybtn");
let addCommentField = function () {
for (let i = 0; i < allElements.length; i++) {
if (allElements[i] === this) {
if (document.getElementsByClassName("replyform")[i].innerHTML.length === 0) {
document.getElementsByClassName("replyform")[i].innerHTML = `
<div class="form-group">
<textarea class="form-control" id="subCommentBox`+i+`" rows="3"></textarea>
</div>
<div class="d-flex flex-row-reverse">
<button type="button" class="btn btn-success" onclick="postComment(` + i + `, ` + allElements[i].id + `)">Comment</button>
</div>
`
}
}
}
};
window.onload = function() {
for (let i = 0; i < allElements.length; i++) {
allElements[i].addEventListener('click', addCommentField, false)
}
}
It worked fine if I put in a .js file.
The thing is after user is logged in, I want to pass username and profile picture as a body to the backend side, so I moved it to App.svelte file:
let commentBox;
function postComment(i, parentId) {
let content;
if (parentId === undefined) {
content = commentBox
} else {
content = eval("subCommentBox"+i).value
}
const body = JSON.stringify({
"post_slug": location.pathname.slice(1),
"image_url": responsePayload.picture,
"username": responsePayload.name,
"parent_id": parentId,
"content": content},
)
fetch("http://localhost:8090/comments", {
method: "post",
headers: { "Content-Type": "application/json" },
body: body
})
.then(resp => {
if (resp.status === 200) {
return resp.json()
} else {
console.log("Status: " + resp.status)
}
})
commentBox = "";
window.location.reload();
}
If I leave the innerHTML text as it is, it caused:
Uncaught ReferenceError: postComment is not defined
If I change it from:
<button type="button" class="btn btn-success" onclick="postComment(` + i + `, ` + allElements[i].id + `)">Comment</button>
to:
<button type="button" class="btn btn-success" on:click={() => postComment(i, allElements[i].id)}>Comment</button>
it will be rendered as:
So, in a .svelte file, how can I call a javascript function with parameters in innerHTML?
If you want to make this in Svelte you should try doing it the 'Svelte' way as well, that means dropping all this constructing html in javascript and injecting it.
Instead consider your markup as a reflection of 'state' and also use components to make your life easier.
Your state would be for example an array of comments and if a user clicked 'reply'.
<!-- This will render a 'Comment' component for each item in the comments -->
{#each comment as comment}
<Comment {...comment} />
{/each}
<!-- Comment.svelte -->
<script>
export let id = "";
export let children = [];
export let text = "";
let isReplying = false;
let reply = ""
async function postComment() {
// Hide comment box
isReplying = false;
// Send to server, the inputted text is available in 'reply'
const res = await fetch(...).then(res => res.json();
// Append to children so it appears 'live'
children = [...children, res]
}
</script>
<!-- The post itself -->
<div>{text}</div>
<!-- Each child is a comment of it's own so render it with this component -->
{#each children as child}
<svelte:self {...child} />
{/each}
<!-- The reply button simply toggles a state -->
<button on:click={() => isReplying = !isReplying}>Reply</button>
<!-- Show this based on state -->
{#if isReplying}
<textarea bind:value={reply}>Blabla</textarea>
<button on:click={postComment}>Send</button>
{/if isReplying}
This should give you a fair idea of what direction to go in.
Remember that your UI is a reflection of your state. So just changed the state and Svelte will take care of the rest (inserting, removing, updating domelements)
Related
I have a js code that works, but the problem is that when I use it on the main HTML page, for some reason it only works on the first picture, although the LIKE icon is on all the pictures.
I do not understand why it is used only on the first value of the icon and not on the others.
How to make it work on all LIKE icons?
const likeIcon = document.getElementById('like-icon');
const likeCount = document.getElementById('like-count');
likeIcon.onclick = () => {
const newId = likeIcon.getAttribute('data-news');
const url = `/like_news/${parseInt(newId)}/`;
fetch(url, {
method: 'GET',
headers: {
'Content-type': 'applicatin/json'
}
})
.then(response => {
return response.json();
})
.then(data => {
if(data.liked) {
likeIcon.classList.remove('empty-heart');
}
else {
likeIcon.classList.add('empty-heart');
}
likeCount.innerHTML = data.like_count;
})
.catch(error => {
console.log(error);
})
}
HTML CODE
<section class="trend">
<div class="container">
<div class="flex align-center justify-between trend-header">
<h2 class="subtitle">
<i class="ri-fire-line subtitle-icon"></i>
В тренде
</h2>
<button class="btn btn-outline">Посмотреть все</button>
</div>
<div class="trend-content">
{% for new in news|slice:"2" %}
<div class="trend-card">
<img src="{{new.banner.url}}" alt="newsPhoto" class="trend-background" />
<div class="card-header">{{new.category.title}}</div>
<div class="card-bottom">
<h3 class="card-title">
{{new.title}}
</h3>
<div class="card-btn">
<div class="count" id="like-count">{{new.likes.count}}</div>
{% if liked_by %}
<button class="btn-up" class="fa fa-heart">
<li><i id="like-icon" data-news="{{new.id}}" class="fa fa-heart"></i></li>
</button>
{% else %}
<button class="btn-up" class="fa fa-heart empty-heart">
<li><i id="like-icon" data-news="{{new.id}}" class="fa fa-heart empty-heart"></i></li>
</button>
</div>
{% endif %}
</div>
</div>
{% endfor %}
</div>
</div>
</section>
Tried to use https://developer.mozilla.org/en/docs/Web/API/Document/querySelectorAll but couldn't do it
const likeIcon = document.querySelectorAll('#like-icon');
const likeCount = document.getElementById('like-count');
likeIcon.forEach(like-icon => {
like-icon.addEventListener("click", () => {
const newId = likeIcon.getAttribute('data-news');
const url = `/like_news/${parseInt(newId)}/`;
fetch(url, {
method: 'GET',
headers: {
'Content-type': 'applicatin/json'
}
})
.then(response => {
return response.json();
})
.then(data => {
if(data.liked) {
likeIcon.classList.remove('empty-heart');
}
else {
likeIcon.classList.add('empty-heart');
}
likeCount.innerHTML = data.like_count;
})
.catch(error => {
console.log(error);
})
});
}
}
And i expected it to work for all pictures that I had on main HTML page
It looks like you're on the right track using querySelectorAll to get all the like icons. However, there is a small mistake in your code when you use likeIcon instead of like-icon in your forEach loop.
Try replacing your JS code with the following, which should work for all like icons:
const likeIcons = document.querySelectorAll('#like-icon');
const likeCounts = document.querySelectorAll('#like-count');
likeIcons.forEach((likeIcon, index) => {
likeIcon.addEventListener('click', () => {
const newId = likeIcon.getAttribute('data-news');
const url = `/like_news/${parseInt(newId)}/`;
fetch(url, {
method: 'GET',
headers: {
'Content-type': 'application/json'
}
})
.then(response => {
return response.json();
})
.then(data => {
if (data.liked) {
likeIcons[index].classList.remove('empty-heart');
} else {
likeIcons[index].classList.add('empty-heart');
}
likeCounts[index].innerHTML = data.like_count;
})
.catch(error => {
console.log(error);
});
});
});
This code should loop through all the like icons and add a click event listener to each of them. When a user clicks on a like icon, the corresponding like count and icon should update based on the server response.
Note that we are using querySelectorAll to get all the like icons and like counts, and then we are looping through them using forEach. We are also using the index of each like icon to update the correct like count and icon.
You need to use class selector instead of ID selector.
While multiple Dom have the same ID, but only the first Dom can be query by document.querySelectorAll or document.querySelector.
Selects an element based on the value of its id attribute. There should be only one element with a given ID in a document.
https://developer.mozilla.org/en-US/docs/Web/CSS/ID_selectors
<i id="like-icon" data-news="{{new.id}}" class="fa fa-heart like-icon"></i>
const likeIcon = document.querySelectorAll('.like-icon');
// like-icon is not a right variable definition.
likeIcon.forEach(likeIcon => {
likeIcon.addEventListener("click", () => {
...
I have a simple django search functionality using js/ajax. I want to add functionality so that when the queryset is greater than 5 a 'Show all' href will appear in the search results and it will redirect to a page with all the queryset.
This is for the case when a queryset returns a large number of results, rather than have them in one big box.
I thought I could just add a dictionary to my queryset, e.g. data.append({'pk': <add number to querset>, 'name': 'Show all results'}) but then I think this will mess around with the js logic with the forEach loop.
I'd want each search result up to 5 to link to the detail view, but then the last one should link to a completely different view.
I'm not sure what the best option is here.
My search in views.py:
def search_results(request):
"""
Handles search logic
"""
if request.is_ajax():
res = None
quote = request.POST.get('quote')
qs = Quote.objects.filter(name__icontains=quote)
if len(qs) > 0 and len(quote) > 0:
data = []
for pos in qs:
item = {
'pk': pos.pk,
'name': pos.name,
'image': str(pos.image.url)
}
data.append(item)
res = data
else:
res = 'No quotes found...'
return JsonResponse({'data': res})
return JsonResponse({})
and main.js that handles loading the search results:
const url = window.location.href
const searchForm = document.getElementById('search-form')
const searchInput = document.getElementById('search-input')
const resultsBox = document.getElementById('results-box')
const csrf = document.getElementsByName('csrfmiddlewaretoken')[0].value
const sendSearchData = (quote) => {
$.ajax({
type: 'POST',
url: 'search/',
data: {
'csrfmiddlewaretoken': csrf,
'quote': quote,
},
success: (res)=> {
console.log(res.data)
const data = res.data
let length = data.length
console.log(length)
if (Array.isArray(data)) {
resultsBox.innerHTML = ""
data.forEach(quote=> {
resultsBox.innerHTML += `
<a href="${url}${quote.pk}" class="item">
<div class="row mt-2 mb-2">
<div class="col-2">
<img src="${quote.image}" class="quote-img">
</div>
<div class="col-10">
<h5>${quote.name}</h5>
<p class="text-muted">${quote.seller}</p>
</div>
</div>
</a>
`
})
} else {
if (searchInput.value.length > 0) {
resultsBox.innerHTML = `<b>${data}</b>`
} else {
resultsBox.classList.add('not-visible')
}
}
error: (err)=> {
console.log(err)
}
}
})
}
searchInput.addEventListener('keyup', e=>{
console.log(e.target.value)
if (resultsBox.classList.contains('not-visible')){
resultsBox.classList.remove('not-visible')
}
sendSearchData(e.target.value)
})
I have a button which depending on whether the text input it triggers is opened or not, switches between 'Report A Problem' to 'Close'. In an effort to consolidate my code I've decided to use Handlebars.JS to create the following:
<button id="myBtnToReportABug" onclick="toggleReportForm(); clearFields();" type="button" class="btn btn-secondary float-right" >{{#if report_form_opened}} Report A Problem {{else}} Close {{/if}}</button>
I've followed examples in the documentation, and as you can see in the Javascript I will attach below, I have a functioning toggle on togggleReportForm, which console logs true & false upon each click. I'm not sure what I'm missing here and would appreciate any tips.
Currently the button only displays 'Close' with no error messages.
var report_form_opened = report_form_opened;
$(document).ready(function() {
const client = ZAFClient.init();
$("#myBtnToReportABug").click(() => collapse.collapse('toggle'));
$("#bugForm").submit((e) => {
e.preventDefault()
const input = document.getElementById('nameInput');
bugInfo = {
"name": "Report From " + idTicket,
"story_type" : "Bug",
"description": input.value + " " + `${data.ticket.brand.url}/agent/tickets/${idTicket}`,
}
reportBug(bugInfo).then(collapse.collapse('toggle'))
})
});
});
});
async function reportBug(data = {}) {
const url = 'https://www.pivotaltracker.com/services/v5/projects/2530461/stories'
const response = await fetch(url, {
method: 'POST',
headers: {
"X-TrackerToken": `${metadata.settings.token}`,
"Content-Type": "application/json"
},
body: JSON.stringify(data)
});
return response.json();
}
// changes text on 'report problem' button when form open
function toggleReportForm() {
report_form_opened = !report_form_opened;
console.log(toggleReportForm)
}
// clears text field onclick
function clearFields() {
document.getElementById("nameInput").value = "";
}
I'm having trouble figuring out my javascript. The e.preventDefault() is not working. I've tried changing the submit input to a button as well. I know with a form and using rails that it has an automatic rage reload but I thought e.preventDefault was suppose to stop that. Is there some hidden feature in the backend that I need to turn off? I set my project up to be an api by using an api flag. It also has all the right info for cors. My server is showing my data correctly ...it's just the frontend I cant get up.
I'm going to post a sample code I followed.
<html lang="en" dir="ltr">
<head>
<title>Problems</title>
<meta charset="utf-8">
<link rel="stylesheet" href="styles.css">
<script type="application/javascript" src="src/user.js" charset="UTF-8"></script>
<script type="application/javascript" src="src/problem.js" charset="UTF-8"></script>
</head>
<body>
<div class="container" id="container">
<h1>Everyone Has Problems</h1>
<div id="new-user-and-new-problem-container">
<form id="new-user-form">
<label>Your name:</label>
<input type="text" id="new-user-body"/>
<input type="submit"/>
</form>
</div>
</div>
<div id="problems-container" class="problems-container">
</div>
</body>
</html>```
src/user.js
```document.addEventListener('DOMContentLoaded', function(){
User.createUser()
})
class User {
constructor(user){
this.id = user.id
this.name = user.name
this.problems = user.problems
}
static createUser(){
let newUserForm = document.getElementById('new-user-form')
newUserForm.addEventListener('submit', function(e){
e.preventDefault()
console.log(e);
fetch('http://localhost:3000/api/v1/users', {
method: "POST",
headers: {
"Content-Type": "application/json",
"Accept": "application/json"
},
body: JSON.stringify(
{
user: {
name: e.target.children[1].value
}
})
})
.then(resp => {
return resp.json()
})
.then(user => {
let newUser = new User(user)
newUser.displayUser()
})
})
}
displayUser() {
let body = document.getElementById('container')
body.innerHTML = ''
let userGreeting = document.createElement('p')
userGreeting.setAttribute('data-id', this.id)
let id = userGreeting.dataset.id
userGreeting.innerHTML = `<h1>Hey, ${this.name}!</h1>`
body.append(userGreeting)
if (this.problems) {
this.problems.forEach(function(problem){
let newProblem = new Problem(problem)
newProblem.appendProblem()
})
}
Problem.newProblemForm(this.id)
}
}```
src/problem.js
```class Problem {
constructor(problem){
this.id = problem.id
this.name = problem.name
this.description = problem.description
}
static newProblemForm(user_id) {
let body = document.getElementById('container')
let form =
`
<form id="new-problem-form">
<label>What's your problem?:</label>
<input type="text" id="problem-name"/>
<label>Describe it:</label>
<input type="text" id="problem-description"/>
<input type="submit"/>
<h4>Your current problems:</h4>
</form>
`
body.insertAdjacentHTML('beforeend', form)
Problem.postProblem(user_id)
}
//is it appropriate for this to be a static method?
static postProblem(user_id) {
let newForm = document.getElementById('new-problem-form')
newForm.addEventListener('submit', function(e){
e.preventDefault()
fetch('http://localhost:3000/api/v1/problems', {
method: "POST",
headers:{
"Content-Type": "application/json",
"Accept": "application/json"
},
body: JSON.stringify(
{
problem: {
name: e.target.children[1].value,
description: e.target.children[3].value,
user_id: user_id
}
}
)
})
.then(resp => resp.json())
.then(json => {
let newProblem = new Problem(json)
newForm.reset()
newProblem.appendProblem()
})
})
}
appendProblem(){
let problems = document.getElementsByClassName('problems-container')
let li = document.createElement('li')
li.setAttribute('data-id', this.id)
li.setAttribute('style', "list-style-type:none")
li.innerHTML = `${this.name} ~~ ${this.description}`
let solveForm = `<button type="button" id="${this.id}" class="solve-problem"> Solve </button>`
li.insertAdjacentHTML('beforeend', solveForm)
problems[0].append(li)
let button = document.getElementById(`${this.id}`)
this.solve(button)
}
solve(button){
button.addEventListener('click', function(e){
e.preventDefault()
fetch(`http://localhost:3000/api/v1/problems/${e.target.parentNode.dataset.id}`, {
method: "DELETE"
})
e.target.parentElement.remove();
})
}
}```
Try not splitting the element up.
document.getElementById('new-problem-form').
addEventListener('submit', function(e){
e.preventDefault()
}
even Jquery
$('#new-problem-form').addEventListener('submit', function(e){
e.preventDefault()
});
The preventDefault is working on the event..
Take this for example:
$('#message').keydown(function (e) {
if (e.keyCode == 13) {
e.preventDefault();
return false;
}
});
This is preventing the enter key from defaulting the submit based on the keydown function. Is this option the actual 'default' you're trying to stop?
Sorry for my English. I am trying to pre select those checkboxes whos values have been saved in the database. I did it using javascript in vuejs but those selected checkboxes values are not storing in array.
My code is like
role.component.js
getRoleRowData(data) {
this.roleaction = "edit";
this.addrolemodal = true;
console.log(data.role_id);
axios
.post(apiUrl.api_url + "getRolePermissionData", {
role_id: data.role_id
}).then(
result => {
this.permid = result.data;
var list = [];
result.data.forEach(function(value) {
list.push(value.perm_id);
});
var options = list;
for (var i = 0; i < options.length; i++) {
if (options[i]) document.querySelectorAll('input[value="' + options[i] + '"][type="checkbox"]')[0].checked = true;
}
},
error => {
console.error(error);
}
);
this.addrole = data;
},
And role.component.html
<div class="col-md-8">
<b-form-fieldset>
<div class="form" id="demo">
<h6>Permissions</h6>
<span v-for="perm_name_obj in listPermissionData">
<input type="checkbox" class="perm_id" v-bind:value="perm_name_obj.perm_id" name="perm_id" id="perm_name" v-model="checkedPerm_Id"> {{perm_name_obj.perm_name}}<br>
</span>
<span>Checked names: {{ checkedPerm_Id }}</span>
</div>
</b-form-fieldset>
</div>
And the Output
And the Ouput I got
In simple words I want to pre check checkboxes in vuejs of which values are stored in database.
See the following example, using simulation data
var app = new Vue({
el: '#app',
data () {
return {
listPermissionData: [],
checkedPerm_Id: []
}
},
created () {
setTimeout(_=>{
//Here simulates axois to request Permission data
this.listPermissionData = [
{perm_id:1,perm_name:'perm_name1'},
{perm_id:2,perm_name:'perm_name2'},
{perm_id:3,perm_name:'perm_name3'},
{perm_id:4,perm_name:'perm_name4'},
{perm_id:5,perm_name:'perm_name5'}
];
//Here simulates axois to request Selected Permissions (result.data)
var selected = [
{perm_id:2,perm_name:'perm_name2'},
{perm_id:5,perm_name:'perm_name5'}
]
this.checkedPerm_Id = selected.map(o=>o.perm_id)
},1000)
}
})
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app">
<div class="form">
<h6>Permissions</h6>
<span v-for="perm_name_obj in listPermissionData">
<input type="checkbox" class="perm_id" v-bind:value="perm_name_obj.perm_id" name="perm_id" id="perm_name" v-model="checkedPerm_Id"> {{perm_name_obj.perm_name}}<br>
</span>
<span>Checked names: {{ checkedPerm_Id }}</span>
</div>
</div>
I solved my problem, here is my code
role.component.js
getRoleRowData(data) {
this.roleaction = "edit";
this.addrolemodal = true;
console.log(data.role_id);
let tempthis = this;
axios
.post(apiUrl.api_url + "getRolePermissionData", {
role_id: data.role_id
}).then(
result => {
this.permid = result.data;
var list = [];
result.data.forEach(function(value) {
//by using tempthis variable we provided the current access to the checkedPerm_Id array which not accessible from out of the function which is getRoleRowData
tempthis.checkedPerm_Id.push(value.perm_id);
list.push(value.perm_id);
});
},
error => {
console.error(error);
}
);
this.addrole = data;
},