I am doing a React web app and trying to dynamically generate table based on the selected data (users from a time period). The user data is downloaded successfully. I am using the same approach in other page. However, this time it does not render.
Here is the code:
displayUsers(tableOfUsers) {
let table = tableOfUsers.map(user => {
return (
<div className="div-table-row-titles" key={user.name}>
<div className="div-table-col">{user.name}</div>
<div className="div-table-col">{user.surname}</div>
<div className="div-table-col">{user.email}</div>
<div className="div-table-col">{user.tel}</div>
<div className="div-table-col">{user.addedBy}</div>
</div>
);
});
return table;
}
render() {
const userData = null;
if (this.state.volunteers !== []) {
console.log("HELLO");
let userData = this.state.volunteers.map(user => {
return (
<div className="div-table-row-titles">
<div className="div-table-col">{user.name}</div>
<div className="div-table-col">{user.surname}</div>
<div className="div-table-col">{user.email}</div>
<div className="div-table-col">{user.tel}</div>
<div className="div-table-col">{user.addedBy}</div>
</div>
);
}); // this.displayUsers(this.state.volunteers);
console.log(userData);
userData = userData[0];
}
return (
<form id="form1">
<div>
<p>{this.props.startTime}</p>
</div>
<div className="div-table">
<div className="div-table-row-titles">
<div className="div-table-col" align="center">
Name
</div>
<div className="div-table-col" align="center">
Surname
</div>
<div className="div-table-col" align="center">
Email
</div>
<div className="div-table-col" align="center">
Tel. No.
</div>
<div className="div-table-col" align="center">
Volunteer
</div>
</div>
{userData}
</div>
</form>
);
}
The user data is there, it is properly ordered, the userData is not null (it is detected as [{...},{...}]). Yet, it does not display. Any ideas how could I fix it?
Thanks in advance!
------SOLUTION-------
I have found the problem. The user.addedBy was an object (addedBy had other properties), therefore React could not process it. Solved!
Without access to the project this is a bit challenging to debug. I would start by simplifying the problem.
First maybe look at what you are trying to accomplish and break out what can be reusable components (Cell, Row, Column, Columns, etc)
Start building each section one at a time so you can debug each part one step a time, such as if you have 5 volunteers can you get 5 rows to show up?
Can I ask why you are using divs instead of a table?
Is this issue due to scoping of let ??
Since let is block scoped and "let userData" is within the if block, it won't be available outside {}.
Could you please try declaring userData outside the if scope or using var??
Related
My end result is supposed to be a list of objects in html. Bootstrap behind this. I'd like for the list to be created dynamically so I don't have to manually create all the divs because I don't know how many there will be. Here's what I have.
I have an array similar to this:
activities =
[
{
"activityOwner": "Raymond Carlson",
"activityDesc": "Complete all the steps from Getting Started wizard"
},
{
"activityOwner": "Flopsy McDoogal",
"activityDesc": "Called interested in March fundraising Sponsorship"
},
{
"activityOwner": "Gary Busy",
"activityDesc": "Get approval for price quote"
}
]
This is the first part where I'm not sure what to do. I can assign the element ids individually for my html like this but what I'd like to do is count how many elements are in my array and create these for me. I won't know how many there are to make these manually. I'm sure there needs to be a loop but I couldn't figure it out.
document.getElementById('activityowner0').innerHTML = activities[0].activityOwner;
document.getElementById('activitydesc0').innerHTML = activities[0].activityDesc;
document.getElementById('activityowner1').innerHTML = activities[1].activityOwner;
document.getElementById('activitydesc1').innerHTML = activities[1].activityDesc;
document.getElementById('activityowner2').innerHTML = activities[2].activityOwner;
document.getElementById('activitydesc2').innerHTML = activities[2].activityDesc;
etc.
etc.
And then...once I have that part, I'd like to know how to create my html divs dynamically based on how many elements are in my array. Again, right now I don't know how many there are so I'm having to create a bunch of these and then have extras if I have too many.
<div class="container">
<div class="row"></div>
<div class="qa-message-list" id="wallmessages">
<br>
<div class="message-item" id="m0">
<div class="message-inner">
<div class="message-head clearfix">
<div class="user-detail">
<h5 class="handle">
<p id='activityowner0'></p>
</h5>
<div class="post-meta"></div>
</div>
</div>
<div class="qa-message-content">
<p id='activitydesc0'></p>
</div>
</div>
</div>
I know this is a big ask so just pointing me in the right direction would be very helpful. I hope my question was clear and I appreciate it.
One way for you to achieve this would be to loop through the objects in your activities array. From there you can use a HTML template to store the base HTML structure which you can clone and update with the values of each object before you append it to the DOM.
In addition, an important thing to note when generating repeated content in a loop: never use id attributes. You will either end up with duplicates, which is invalid as id need to be unique, or you'll end up with ugly code generating incremental/random id at runtime which is unnecessary. Use classes instead.
Here's a working example:
const activities = [{ "activityOwner": "Raymond Carlson", "activityDesc": "Complete all the steps from Getting Started wizard"}, {"activityOwner": "Flopsy McDoogal","activityDesc": "Called interested in March fundraising Sponsorship" }, { "activityOwner": "Gary Busy", "activityDesc": "Get approval for price quote" }]
const html = activities.map(obj => {
let item = document.querySelector('#template').innerHTML;
item = item.replace('{owner}', obj.activityOwner);
item = item.replace('{desc}', obj.activityDesc);
return item;
});
document.querySelector('#list').innerHTML = html.join('');
<div id="list"></div>
<template id="template">
<div class="container">
<div class="row"></div>
<div class="qa-message-list">
<div class="message-item">
<div class="message-inner">
<div class="message-head clearfix">
<div class="user-detail">
<h5 class="handle">
<p class="activityowner">{owner}</p>
</h5>
<div class="post-meta"></div>
</div>
</div>
<div class="qa-message-content">
<p class="activitydesc">{desc}</p>
</div>
</div>
</div>
</div>
</div>
</template>
I'm currently developing a simple true odd finder calculator using python, django and jquery. I need to have form input submit actions executed by jQuery as the user types in the input values. The goal is to get rid of submit buttons in the frontend html. As of nowadays calculator based web applications don't require submit buttons. The functionality behavior should look like here. I did a research and found out that i need to use JQuery. After implementing the functionality in my app, am able to type the first form input element, however upon clicking the second form input so as to start typing, my application crashes with server error 500, if i go back then type the second form input, it updates output.
How can i implement form input onChange using jquery to match the referenced functionality above.
My template and JQuery code
{% extends "base.html" %}
{% block title %}Two Way True Odd Finder{% endblock %}
{% block content %}
<script type="text/javascript" src = "https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.js"></script>
<script type="text/javascript">
$(document).ready(function () {
$('.form-control').change(function () {
$('#myform').submit();
});
});
</script>
<div class="container m-5">
Dashboard Home
3 Way True Odds Finder Calculator
</div>
<div class="container m-5 text-justify">
<div class="row">
<div class="col-4">
<form action="" method="post" id="myform">
{% csrf_token %}
<div class="mb-3">
<label for="Odd1" class="form-label">Odd 1</label>
<input type="number" class="form-control" name="odd1" id="Odd1" min=" " value=" " step=".001" required='required'>
</div>
<div class="mb-3">
<label for="Odd2" class="form-label">Odd 2</label>
<input type="number" class="form-control" name="odd2" id="Odd2" min=" " value=" " step=".001" required='required'>
</div>
<!--<button type="submit" class="btn btn-primary">Submit</button>-->
</form>
</div>
<div class="col-8">
<div class="row">
<div class="col-sm-6">
<div class="card shadow-sm p-3 mb-5 bg-white rounded">
<div class="card-body">
<h5 class="card-title">Results</h5>
<table class="table table-bordered">
<thead class="thead-dark">
<tr>
<th scope="col">#</th>
<th scope="col">Odd1</th>
<th scope="col">Odd2</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Initial Odds With Juice</th>
<td>{{HomeOdd}}</td>
<td>{{AwayOdd}}</td>
</tr>
<tr>
<th scope="row">Implied Probability Win %</th>
<td>{{Home_implied_probability}}</td>
<td>{{Away_implied_probability}}</td>
</tr>
<tr>
<th scope="row">True Odds Without Juice</th>
<td class="text-success">{{Home_True_Odd}}</td>
<td class="text-success">{{Away_True_Odd}}</td>
</tr>
</tbody>
</table>
<div class="container text-justify">
<p>Total Implied probability is {{TotalImpliedProbability}}%</p>
<p>Inverted probability is {{Inverted_Probability}}%</p>
<p>Bookie juice is {{Juice}}%</p>
<p>True probability is {{True_Probability}}</p>
</div>
</div>
</div>
</div>
<div class="col-sm-6">
<div class="card shadow-sm p-3 mb-5 bg-white rounded">
<div class="card-body">
<h5 class="card-title">Enjoyed the calculator?</h5>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="container bg-light p-5">
<h3>HOW IT WORKS</h3>
</div>
</div>
{% endblock %}
django view
def two_way_calc(request):
if request.method == 'POST':
odd1 = float(request.POST.get('odd1'))
odd2 = float(request.POST.get('odd2'))
func_def = odd_finder_true_2(odd1, odd2)
context = {
'Juice': func_def['Juice'],
'TotalImpliedProbability': func_def['TotalImpliedProbability'],
'HomeOdd': func_def['HomeOdd'],
'AwayOdd': func_def['AwayOdd'],
'Home_True_Odd': func_def['Home_True_Odd'],
'Away_True_Odd': func_def['Away_True_Odd'],
'True_Probability': func_def['True_Probability'],
'Home_implied_probability': func_def['Home_implied_probability'],
'Away_implied_probability': func_def['Away_implied_probability'],
'Inverted_Probability': func_def['Inverted_Probability'],
}
return render(request, 'three_way_temp.html', context)
else:
return render(request, 'three_way_temp.html', {})
urls.py
from django.urls import path
from .views import *
from . import views
urlpatterns = [
path('', views.index, name='index'),
path('two_way_calc/', views.two_way_calc, name='two_way_calc'),
]
Python function behind the calculation
def odd_finder_true_2(first_odd, second_odd):
home_implied_probability = round((100/first_odd), 2)
away_implied_probability = round((100/second_odd), 2)
total_implied_probability = home_implied_probability + away_implied_probability
inverted = (100/total_implied_probability) * 100
juice = total_implied_probability - inverted
hundred_odd_home = total_implied_probability/home_implied_probability
hundred_odd_away = total_implied_probability/away_implied_probability
prob_true = 1/(round(hundred_odd_home, 2)) + 1/(round(hundred_odd_away, 2))
my_dict_two = {
'Juice': round(juice, 2),
'TotalImpliedProbability': round(total_implied_probability, 2),
'HomeOdd': first_odd,
'AwayOdd': second_odd,
'Home_True_Odd': round(hundred_odd_home, 2),
'Away_True_Odd': round(hundred_odd_away, 2),
'True_Probability': round(prob_true, 1),
'Home_implied_probability': home_implied_probability,
'Away_implied_probability': away_implied_probability,
'Inverted_Probability': round(inverted, 2)
}
return my_dict_two
so there are a few things that i should say before your answer
First of all it's better approach to use AJAX instead of submit your form because AJAX prevents the page from being reloaded and by using it you do not need to send a whole complete http request and receive a complete response
It will only send needed information to server and get exact answer and js helps you to inject that data in your page
Second you don't actually and necessarily need jQuery, you can do what you want in pure js using fetch API however jQuery is an option too
Now the answer
5xx Errors are server side errors and thank to you for sending your complete code i can show you error
So the problem is when you trigger submit event, you are changing one input and this causes your form to be submitted,
BUT, WHAT ABOUT SECOND INPUT?
it has no value and thus your server can't do the calculations right
How to fix this?
It's up to your algorithm, if you can find a default value to be set on the second input, it can solve the probe
Or
You can check it in you jQuery code
$(".form-control").on("change",function(){
if($("Odd2").value !== "")
$('#myform').submit()
}) ;
Also The jQuery code you've write is correct but it can be better
You are using change event which fires after the value is changed AND when user left the input (blur event)
You will get a better and more live result using input event
$(".form-control").on("input",...)
===============
Updated, based on your last comments, my guess was right and you want the answer to be shown to the user immediately after he entered the number
So you need to use another event called input here's the result (you can add a simple alert before that if to see how input event fires
$(".form-control").on("input",function(){
if($("Odd2").value !== "")
$('#myform').submit()
}) ;
Let me know if your code is changed a lot and provide me your new jQuery code if this didn't solve your problem
============
Updated
Again my guess was right and you need to prevent page from being reloaded (or redirected) this can be done by AJAX
you have multiple choices in AJAX
you can use pure JS (xmlHttpRequest object)
you can use Fetch API in js
you can use Axios.js which is a great library designed to handle Ajax requests and responses
Or finally you can use jQuery
Because in your project you are already using jQuery, I'll do it using jQuery but i don't approve it, Fetch is best option in my opinion, anyway :
$(".form-control").on("input",function(){
if($("Odd2").value !== "")
var fData = new FormData($('#myform'));
var jqxhr = $.ajax({
url: 'AddressToYourBackendFile',
method : 'POST',
data : fData
});
jqxhr.done( res => {
console.log('your response is ready :\n' + res);
});
}) ;
I have a ref variable (foxArticles ), which holds a list that contains 100 items. In a v-for loop i loop over each value. As a result, i have 100 values rendered on the page.
<template>
<div class="news_container">
<div
v-for="article in foxArticles"
v-bind:key="article"
class="article_single_cell"
>
<div
class="news_box shadow hover:bg-red-100 "
v-if="containsKeyword(article, keywordInput)"
>
<div class="news_box_right">
<div class="news_headline text-red-500">
<a :href="article.url" target="_blank">
{{ article.title }}
</a>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
const foxArticles = ref([]);
</script>
I also have a search function, which returns the value, if it includes the passed in keyword. The function is used in the child of the v-for loop.
<div class="search_input_container">
<input
type="text"
class="search_input"
v-model="keywordInput"
/>
</div>
<script>
const keywordInput = ref("");
function containsKeyword(article, keywordInput) {
if (article.title.toLowerCase().includes(keywordInput.toLowerCase())) {
return article;
}
}
</script>
The problem is, i can't use .slice() on the foxArticles array in the v-for loop, because that screws up the search functionality, as it returns only the values from the sliced range.
How can i have the access the all of the values of the array, while not rendering all 100 of returned articles on the initial load?
Any suggestions?
I think your approach will make it incredibly complex to achieve. It would be simpler to always iterate over some set, this set is either filtered based on a search-term, or it will be the first 100 items.
I'm not very familiar yet with the Vue 3 composition api so I'll demonstrate with a regular (vue 2) component.
<template>
<div class="news_container">
<div
v-for="article in matchingArticles"
v-bind:key="article"
class="article_single_cell"
>
... news_box ...
</div>
</div>
</template>
<script>
export default {
...
computed: {
matchingArticles() {
var articles = this.foxArticles;
if (this.keywordInput) {
articles = articles.filter(article => {
return this.containsKeyword(article, this.keywordInput)
})
} else {
// we will limit the result to 100
articles = articles.slice(0, 100);
}
// you may want to always limit results to 100
// but i'll leave that up to you.
return articles;
}
},
....
}
</script>
Another benefit is that the template does not need to worry about filtering results.
ok, so i kind of came up with another solution, for which you don't have to change the script part...
instead of having one v-for loop , you can make two of them, where each one is wrapped in a v-if statement div
The first v-if statement says, If the client has not used the search (keywordInput == ''), display articles in the range of (index, index)
The second one says = If the user has written something (keywordInput != ''), return those articles.
<template>
<div class="news_container">
<!-- if no search has been done -->
<div v-if="keywordInput == ''">
<div
v-for="article in foxArticles.slice(0, 4)"
v-bind:key="article"
class="article_single_cell"
>
<div class="news_box shadow hover:bg-red-100 ">
<div class="news_box_right">
<div class="news_headline text-red-500">
<a :href="article.url" target="_blank">
{{ article.title }}
</a>
</div>
</div>
</div>
</div>
</div>
<!-- if searched something -->
<div v-else-if="keywordInput != ''">
<div
v-for="article in foxArticles"
v-bind:key="article"
class="article_single_cell"
>
<div
class="news_box shadow hover:bg-red-100 "
v-if="containsKeyword(article, keywordInput) && keywordInput != ''"
>
<div class="news_box_right">
<div class="news_headline text-red-500">
<a :href="article.url" target="_blank">
{{ article.title }}
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
im not sure how this impacts performance tho, but that's a problem for another day
I am trying to use a button to update a property of an Object in Vue. The Object is returned by an AJAX query to a database, and the isFetching boolean is then set to false, which attaches the containing div to the DOM. When I try and update the property, I get the following error:
TypeError: Cannot read property '_wrapper' of undefined
Below is my AJAX code:
axios.post("/api/myEndpoint", { id: router.currentRoute.query.id })
.then((response) => {
this.activities = response.data.activities;
this.isFetching = false;
})
.catch((errors) => {
console.log(errors);
router.push("/");
});
Here is my HTML:
<div v-if="isFetching">
<h2>Loading data...</h2>
</div>
<div v-else>
<div class="row no-gutters">
<div class="col">
<div class="d-flex flex-row justify-content-center">
<h4>Activities</h4>
</div>
<div class="custom-card p-2">
<div class="row no-gutters pb-4" v-for="(activity, index) in activities"
:key="activity.stage_id">
<button v-if="!activity.is_removed" class="btn custom-btn" :class="{'hov':
index == 0}" :disabled="index != 0"
#click="markRemoved(activity)">Remove</button>
</div>
</div>
</div>
</div>
</div>
Finally, here is markRemoved() as called by the button's click listener:
markRemoved(a) {
a.is_removed = true;
}
When I try and log a in markRemoved() the Object is logged to the console fine, exactly as expected. Having stepped through it in the debugger, the exception is thrown at the point I try and update the is_removed property of the Object.
Does anyone know what I'm doing wrong here?
Note: the id I pass to the AJAX query is a query parameter of Vue Router. This is also set correctly and passed as expected.
Here is an example activity Object:
{date_time_due: "2020-12-09T11:43:07.740Z"
date_time_reached_stage: "2020-12-02T11:43:07.740Z"
is_complete: true
is_removed: false
selected_option: "Some text"
stage_id: 1
stage_name: "Some text"}
The exception is only thrown on the first click of the button.
Posting in case anybody else comes across this error in the future.
Vue requires exactly one root element within a single-file component's <template> tags. I had forgotten about this and in my case had two <div> elements, shown one at a time, conditionally using v-if:
<template>
<div v-if="fetchingData">
<h2>Loading data...</h2>
</div>
<div v-else>
<!-- The rest of the component -->
</div>
</template>
This caused problems with Vue's reactivity, throwing the error whenever I tried to update some part of the component. After realising my mistake, I wrapped everything in a root <div>. This solved the issue for me:
<template>
<div id="fixedComponent">
<div v-if="fetchingData">
<h2>Loading data...</h2>
</div>
<div v-else>
<!-- The rest of the component -->
</div>
</div>
</template>
I have a quiz form here and would like to add input fields for questions when a user clicks the "Add a question" button.
I've been playing around with the code and have been able to populate the state object with some text. Obviously, the goal is to have this be an input component and then somehow rendering this to the screen.
What I'm struggling with is how to render an actual element to the page. I know it's done in the render method of the component just not exactly sure how.
I think I'm getting close. Any help would be appreciated. The code is below.
Thanks!
var QuizForm = React.createClass({
getInitialState : function() {
return { questions : [] }
},
createQuestion : function() {
this.state.questions.push("Test");
// Adds "Test" to state object.
this.setState({
questions : this.state.questions
});
},
render : function() {
return (
<div className="quiz-form well text-center">
<h1 className="header text-center">Quiz Form</h1>
<ul>
{/* Would like question inputs to show up here */}
</ul>
<button onClick={this.createQuestion} className="add-question-btn btn btn-primary" style={{ marginTop : 40 }}>Add Question</button>
</div>
);
}
Just map your this.state.questions array to the HTML element you want.
For instance, if you want to render <li> elements:
render : function() {
return (
<div className="quiz-form well text-center">
<h1 className="header text-center">Quiz Form</h1>
<ul> // magic happens now
{this.state.questions.map(function(state) {
return <li>{state}</li>
})}
</ul>
<button onClick={this.createQuestion}
className="add-question-btn btn btn-primary"
style={{ marginTop : 40 }}>Add Question</button>
</div>
);
}
See an example.
If you want to render <input> tags, you can use the same technique above, but be mindful of the fact that React treats it as a controlled component.
A React best practice would be to map your state.questions array to a dynamically generated HTML component such as:
render : function() {
return (
<div className="quiz-form well text-center">
<h1 className="header text-center">Quiz Form</h1>
<ul> // magic happens now
{this.state.questions.map(function(state) {
return <li key={state.someId}>{state.question}</li>
})}
</ul>
<button onClick={this.createQuestion}
className="add-question-btn btn btn-primary"
style={{ marginTop : 40 }}>Add Question</button>
</div>
);
}
Please keep in mind that when mapping and rendering dynamic objects in React it's always good to insert a key for each mapped object. So make sure to create that when you're creating the content.
Best Regards,