Created a JSBin that demostrated the problem: http://jsbin.com/kukehoj/1/edit?html,js,console,output
I'm creating my first REST-powered website. The backend is in Python (Django REST Framework), and seems to be working fine. I'm trying to make the front-end get the comments for the posts, but its not working.
HTML Imports
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.1/knockout-min.js"></script>
scripts.js
function Comment(data) {
this.body = ko.observable(data.responseText)
}
function Post(data) {
this.title = ko.observable(data.title)
this.body = ko.observable(data.body)
var self = this;
self.comments = ko.observableArray([])
self.comments(($.map(data.comments, function(link) { // Map the data from
return $.getJSON(link, function(data) { return new Comment(data)}) //These requests
})))
}
function PostViewModel() {
// Data
var self = this;
self.posts = ko.observableArray([])
// Get the posts and map them to a mappedData array.
$.getJSON("/router/post/?format=json", function(allData) {
var mappedData = $.map(allData, function(data) { return new Post(data)})
self.posts(mappedData)
})
}
ko.applyBindings(new PostViewModel());
Server data:
[{ "title":"-->Title here<--",
"body":"-->Body here<--",
"comments":[
"http://127.0.0.1:8000/router/comment/6/?format=json",
"http://127.0.0.1:8000/router/comment/7/?format=json",
"http://127.0.0.1:8000/router/comment/8/?format=json",
"http://127.0.0.1:8000/router/comment/9/?format=json"]
}]
where each of the links leeds to:
{"body":"-->Body here<--"}
index.html
<div class="col-lg-7" data-bind="foreach: { data: posts, as: 'posts' }">
<h3 data-bind="text: title"></h3>
<p data-bind="text: body"> </p>
<span data-bind="foreach: { data: comments(), as: 'comments' }">
<p data-bind="text: comments.body"></p>
</span>
</div>
(There is a lot more HTML, but i removed the irrelevant parts)
Everything is working fine, except from that the comments seem to be in the wrong format.
The chrome console shows JSON "responseText" bound to each of the comment object values.
Wrong format
I'm sorry if this is a stupid question, but I have tried everything - but it doesn't work. (I'm a noob)
There is nothing wrong with your sample code you provided except the part you have this.body = ko.observable(data.responseText) while your data does not contain a responseText in your sample commentData object . if you replace commentData object with var commentData = {"responseText":"-->Body here<--"} it works.
Note: this part
<span data-bind="foreach: { data: comments(), as: 'comments' }">
<p data-bind="text: comments.body"></p> // comments.body => body
</span>
on your question is wrong but you have it correct on your sample code .It should be
<span data-bind="foreach: { data: comments(), as: 'comments' }">
<p data-bind="text: body"></p>
</span>
Here is a working version of your sample :https://jsfiddle.net/rnhkv840/26/
I assume you are using Django Rest Framework, so the JSON structure you get for your posts is done automatically by your serializer based on your model fields.
Back to the frontend, I have not used knockout js before, but what you require is to load the comments using another controller. Either you do it one by one using the links provided by your main resource (this can result in lots of queries sometimes), or you create a filter on your comments endpoint which will allow you to retrieve comments for a specific post.
Ever considered using the django REST framework? It can help you serialize all you models with a simple viewset. Check out the docs.
So found the actual problem. The way the JavaScript read the data from the server, ment that since there was only one value for the comments, the data property of a comment was the variable storing the body of the comment. Not the data.body.
Related
I have a Python variable whose value is a string of text and would like to edit that value via Javascript.
I have no idea how to go about doing this.
Attempts:
function changeValue(val) {
val = 'new text';
}
<textarea placeholder="some text">{{ changeValue({{ result }}) }}</textarea>
<textarea placeholder="some text">
{{ result }}
</textarea>
What I want: I have some text (result) being added and would like to check if the text is empty. If so, I want to show the placeholder text.
The issue: Although I can check if the value is empty, when I try to print that result out it reads none
Thanks to all!
You do not need to call the JavaScript function from the HTML file. There are several approaches you can take:
1. Store the variable in HTML metadata:
<meta id="result_var" data-result={{result}}>
And then get the data in JavaScript:
result = document.getElementById("result_var").value;
2. Keep the variable in the tag where it's supposed to be and get it from there in JavaScript:
<textarea placeholder="some text" id="result-var"> {{result}} </textarea>
And then get it in JavaScript:
let result = document.getElementById("result-var");
3. Query it from your API: You can create a route in your Flask app that returns JSON data with the variable you need and then get that data to your JavaScript file by sending a request to your API.
4. Jinja format: I've seen solutions that involve just using the variable as if it was a jinja variable in JavaScript like this: let result = JSON.parse('{{ result | tojson }}');. But I haven't been able to get this working properly, not sure why.
I hope this helps!
I solved a lot of my issues with stackoverflow.
Today, I decided to ask my question.
I'm new learning Python. I'm looking at how to scrape data from the web.
I came to an example of website where products may have different variants in term of size or colour.
I can't figure out how 'follow' the link to reach the page of the variant. I can see that there is a call to a function but I don't know how to have access to this function/link.
See below urls as examples:
variant of colour and size
variant of colour:
Here is the code I use to get all the variants available but it's not working as I want and finally I don't know how to get the links:
# Define the part of the page I'm interested in:
article = soup.find('header', class_='pdp-header')
for variant in article.find_all('p', class_='pdp-size-variants__title'):
print(variant)
if "Colour" in variant:
for colours in article.find('div', class_='swatches__item'):
print(colours)
if "Size" in variant:
for sizes in article.find('button', class_='btn-supportive'):
print(sizes)
The result, I have is:
<p class="pdp-size-variants__title small--xs mb-1"><strong>Colour</strong></p>
<p class="pdp-size-variants__title small--xs mb-1"><strong>Size</strong></p>
If you can put me on the right direction that would be great.
Thanks a lot.
David
I can see that there is a call to a function but I don't know how to have access to this function/link.
The first step is to look at that function. Start by looking at the swatch:
<div class="swatches__item"><span title="Black" onclick="getProductVariantFunc(12145,267721)" class="swatch active" style="background-image:url('https://ccshop.sirv.com/ccs/images/swatches/Black.png');background-size:100%"> </span></div>
Here we see there is an onclick handler named getProductVariantFunc. Now you should open up the page source and find the code for that function:
<script>function getProductVariantFunc(n, t) {
$("#product-variants").addClass("disabled-div");
t !== 0 ? $.ajax({
cache: !1,
url: "/VariantAttributes/GetProductHtmlByAttributes",
type: "POST",
data: {attributeId: t, productId: n},
success: function (n) {
$("#product-info").html(n);
$("#product-variants").removeClass("disabled-div");
initProductDetails();
initPdp();
renderRecommendedProductList()
}
}) : $.ajax({
cache: !1,
url: "/VariantAttributes/GetProductHtml",
type: "POST",
data: {productId: n},
success: function (n) {
$("#product-info").html(n);
$("#product-variants").removeClass("disabled-div");
initProductDetails();
initPdp();
renderRecommendedProductList()
}
})
}</script>
We can see here that the page makes an ajax() call to the URL "/VariantAttributes/GetProductHtmlByAttributes". So all you have to do is request that same URL to see if it has the data you need.
I wanted to show my sound sensor readings from a django site (original code as posted in the link). Now as per the situation 2 of the accepted answer, I wanted to make a Javascript function which repeatedly calls the ajax_data function from views template.
But it seems that no repeated calls are being made. And no update in reading reflects either.
My django template till now:
<!doctype html>
<html>
<head>
<title>Noise measurement site</title>
<script src="http://code.jquery.com/jquery-3.0.0.js"
integrity="sha256-jrPLZ+8vDxt2FnE1zvZXCkCcebI/C8Dt5xyaQBjxQIo="
crossorigin="anonymous"></script>
<script language="javascript" type="text/javascript">
function updateValue() {
$.ajax({
url:"D:/Python programs/django_projects/Noise_Measurement/noise_m/views.py/ajax_data/",
//success: updateValue(), for experimenting with a recursive call...
});
}
$(document).ready(function(){
var v = setInterval(updateValue,2000);
});
</script>
</head>
<body>
<p>Hello there</p>
<p>Present noise level : {{noise_level}} dB</p>
</body>
</html>
(I have mentioned rest of the code in my previously asked question. I have read some of the answers on the platform but I'm not getting much results.)
Update
Sorry I made a mistake. I made slight changes in the code and posted output without that only. Now I have made the exact changes as in the previous part. But output is not sorted out yet. (Thanks to comment by CumminUp07)
** Own answer on own question **
Oh sorry, actually it was a misunderstanding of the syntax from my side.
I was first supposed to create method in views.py, which will send the reading from the module taking it. Then for that method, I had to assign an url using path(), in a fashion like:
path('read', views.data_update1, name='readings'),
Then the ajax request was supposed to be made to read link:
$.ajax({
url: "read",
dataType: "json",
contentType: "application/json",
success: function(r) { ... }
});
Then this method is conveniently called using setInterval method.
But finally at the line, the {{ }} didn't help, so the div where the value was to be displayed was assigned an id, whose value was updated on each call of the method.
Suppose I want to render a page (not just JSON) using Flask with some specific data that I fetch from the database. For example
display_data.html includes:
<script src='display_data.js'></script>
...
<h1>Data display page!</h1>
<div id="chartContainer"></div>
display_data.js:
$(function() {
draw_chart($("#chartContainer"), json_data);
//draw_chart is defined elsewhere and json_data is what I want to pass in
});
Python:
#app.route('/<data_id>')
def get_display_data_page(data_id):
data = get_data_by_id(data_id)
return render_template('display_data.html', data = data)
I think that if I want to just "render template", I'd have to include elsewhere in display_data.html the following:
<script>window.json_data = {{ data | tojson | safe}}</script>
This pattern smells bad: I'm leaving an object on the global namespace (so that my JS file can access it), displaying the data as plain text, and rendering a string in that is parsed into JSON so the JS can use it. Looks bad but this does work.
Two other options:
Return the data with AJAX. Given the title of this post I'm specifically trying to avoid ajax. The reason for this is mainly that I'm building a mobile site and want to reduce the number of pings back to the server. I'm also thinking (perhaps more metaphysically) about encapsulating the page: once you have it, you have all of it.
Render my JS file via Flask and Jinja. This seems like a bummer because I'd have to then write a route down and render the JS based on the same logic that I have in the get_display_data_page: looking up the data by its id, etc. Code duplication and dynamic JS sound like big no-no's to me.
Is there a known pattern to doing this well?
There's no need to leave data in the global scope if you don't want to. In your template you can do something like this:
<script>
function registerTask(f, args) {
$(function() {
f.call(this, args);
});
}
{% for name, args in js_tasks %}
registerTask({{name}}, {{ args | tojson | safe }});
{% endfor %}
</script>
Then, in your JS file, redefine draw_chart to just take the data (or have a wrapper around it that you use as your task registry name):
function draw_chart_task(data) {
draw_chart($('#chartContainer'), data);
}
Finally, in your controller, simply provide the data and the task name as a tuple:
return render_template('display_data.html', js_tasks=[('draw_chart_task', data)])
This ensures that your JavaScript is not just plucking its dependencies out of the global scope, and you are not making extra network calls.
The data is visible in the raw text output of the page, but it is visible if you make an AJAX call too, you just have to look in a different panel of your browser's developer's tools to see it.
I have been calling a function to run a query in to the database on a like event of facebook like button on my python site .And it is working absolutely fine .
FB.Event.subscribe('edge.create', function(href, widget)
But the problem is I need to use a another query of update on unlike event of the facebook button.
<script>
FB.Event.subscribe('edge.create', function(href, widget) {
top.window.location = 'http://pydev.abc.com/surpriseme/';
});
</script>
<div class="suprise">
<div class="suprise_con">
<div class="fb-like" style="overflow:hidden;" data-href="https://www.facebook.com/VivaAviesta" data-send="false" data-layout="button_count" data-width="100" data-show-faces="false"></div>
<span style="margin-left:5px;"> Like Us to Reveal</span>
</div>
</div>
For the url surprise me I have added a functionin views :
def surpriseme(request):
if request.user.is_authenticated():
cursor = connection.cursor()
cursor.execute("INSERT "......." (name,facebook_id,category,user_id) VALUES ('Abcd',1,'',%s)",[request.user.id])
#return HttpResponse("success")
return redirect("/showroom/")
Can anyone help in calling another function at the unlike event?
Actually the thing is that the person who has not liked the face book button on our site will not be able to see few of the products and once he/she hits like button they can see the products.
Now what I need is if anyone again unlike the button will should not be able to see products , which is not working at the moment.
calll like this
<script type="text/javascript">
window.fbAsyncInit=function() {
FB.Event.subscribe('edge.remove', function(href, widget) {
top.window.location = '{{baseurl}}/surprisemeunlike/';
});
FB.Event.subscribe('edge.create', function(href, widget) {
top.window.location = '{{baseurl}}/surpriseme/';
var url = '{{baseurl}}/surpriseme/';
});
}
</script>
Ok I got the answer, there is another event fron facebook :
FB.Event.unsubscribe('edge.create', handleResponse);
We can call another function on this event .
<script>
FB.Event.unsubscribe('edge.create', function(href, widget) {
top.window.location = 'http://pydev.abc.com/surpriseunlike/';
});
</script>
and it is working fine for me now .
"Like Gating" isn't against the terms of service and is used by many brands. It worked well under FBML, but it isn't reliable for an actual lockdown on the current API options. Facebook only prohibits requiring a like for contest entries (unless you use an approved vendor to run the contest).
If you actually want to require people to "Like" , you should be using their Graph API and 'realtime updates' https://developers.facebook.com/docs/reference/api/realtime/ Your javascript function will only work if someone likes or unlikes on that same page. It won't handle people liking or unliking in other browser windows or different time periods, which undermines your intent.
But if someone Likes your page and then unlikes it - so what? They already saw your special content.