(JS) Markdown issue in flask - javascript

I have a small problem with markdown in my flash project.
I want markdown to work for all posts, currently it only works for the first post. I suspect I need to use some kind of loop, but I don't know what to do to make it work.
Code:
{% extends "layout.html" %}
{% block content %}
<article class="media content-section">
<div class="media-body">
{% for post in document.posts %}
<div class="article-metadata">
<h3><a class="article-title" href="{{ url_for('posts.post', post_id=post.id) }}">{{ post.title }}</a></h3>
<p class="article-content">{{ post.content }}</p>
</div>
{% endfor %}
</div>
</article>
<script>
const content_text = document.querySelector(".article-content");
content_text.innerHTML = marked(content_text.innerHTML);
</script>
{% endblock content %}
Pic of the posts on website

querySelector returns the first element with the class. You need to use querySelectorAll to get all the elements and then use for loop to loop and apply markdown to each element.
const content_text = document.querySelectorAll(".article-content");
for (i = 0; i < content_text.length; i++){
content_text[i].innerHTML = marked(content_text[i].innerHTML);
}

Related

Automatically add divs to product descriptions in Shopify

I'm trying to add divs to product descriptions in Shopify so that I can then create an accordion.
Currently my code looks like this
In the .liquid file:
<div class="product-single__description rte">
{{ product.description }}
</div>
This is the output:
<div class="product-single__description rte">
<h2>TEXT</h2>
<h4>TEXT</h4>
<p><em>Text</em></p>
<h4>TEXT</h4>
<p>Text</p>
<h2>TEXT</h2>
<h4>Text</h4>
<p>Text</p>
<h4>TEXT</h4>
<p>Text</p>
<h4>TEXT</h4>
<p>Text</p>
<p><em>Text</em></p>
</div>
My goal is to insert a div wrapper and enclose the content from H2 to the next h2, so for example:
<div class="product-single__description rte">
<div class=“class_1”
<h2>TEXT</h2>
<h4>TEXT</h4>
<p ><em>Text</em></p>
<h4>TEXT</h4>
<p>Text</p>
</div>
<div class=“class_2”
<h2>TEXT</h2>
<h4>Text</h4>
<p>Text</p>
<h4>TEXT</h4>
<p >Text</p>
<h4>TEXT</h4>
<p>Text</p>
<p><em>Text</em></p>
</div>
</div>
The number of H2s and the content changes from product to product.
Well there are a few checks that you need to make before you do this.
First we will set a variable for the content:
{% assign content = product.description %}
After that we will check if the if there is a plain <h2> in there
{% if content contains '<h2>' %}
// logic to add here
{% else %}
{{content}}
{% endif %}
(have in mind that if your h2 tags have any inline styles you will have to target <h2 instead)
If there is we will continue the logic inside the if, but if there is not we will output the plain content in the else statement.
So we are now in the // logic to add here part here.
We will split the content by <h2> like so:
{% assign content_arr = content | split: '<h2>' %}
We will check if you have some content before the first <h2> since we don't want to loose it in that case, so we check it like so:
{% if content_arr[0] != '' %}
{{content_arr[0]}}
{% endif %}
We need to loop the rest of the items of the array.
Since we are splitting by <h2> if there is no content before the <h2> it will return an empty array for the first item and we don't need that one. So we will offset the for loop by 1 item:
{% for item in content_arr offset: 1 %}
// items here
{% endfor %}
Now we need to return the opening <h2> tag (since we removed it from the content) and show the rest of the content.
It's easy as writing <h2> before the {{item}}:
<div class="class_{{forloop.index}}">
<h2>{{item}}
</div>
And that's all.
Here is the full code:
{% assign content = product.description %}
{% if content contains '<h2>' %}
{% assign content_arr = content | split: '<h2>' %}
{% if content_arr[0] != '' %}
{{content_arr[0]}}
{% endif %}
{% for item in content_arr offset: 1 %}
<div class="class_{{forloop.index}}">
<h2>{{item}}
</div>
{% endfor %}
{% else %}
{{content}}
{% endif %}
You may try this (not tested but it should work):
{% assign desc_parts = product.description | split:'<h2>' %}
{% for part in desc_parts offset:1 %}
<div class="class_{{ forloop.index }}">
{{ part | prepend:'<h2>' }}
</div>
{% endfor %}
Explanations:
As you do not have clean separator in product description, let's use
h2 tag.
Then, you create an array with this separator (split
function).
Then you loop through your array, with an offset to 1 to
avoid the empty first elem (or you may use it later or before to display it in a separated div it there is something before the first h2 tag). To display separately the first elem, use {{ desc_parts.first }}.
To get a unique class or id, you may
use the loop index.
As the h2 tag is the separator used to create
the array, you need to prepend your elem with it.
Please note that you should also think about the case with a product description without h2 and manage this case in your code.

Show and hide text of different posts

I have several posts each of them composed of three parts : a title, a username/date and a body. What I want to do is to show the body when I click on either the title or the username/date and hide it if I click on it again. What I've done so far works but not as expected because when I have two or more posts, it only shows the body of the last post even if I click on another post than the last one. So my goal is only to show the hidden text body corresponding to the post I'm clicking on. Here is my code:
{% extends 'base.html' %}
{% block header %}
<h1>{% block title %}Test page{% endblock %}</h1>
<a class="action" href="{{ url_for('main_page.create') }}">New</a>
{% endblock %}
{% block content %}
{% for post in posts %}
<article class="post">
<header>
<script language="JavaScript">
function showhide(newpost)
{var div = document.getElementById(newpost);
if (div.style.display !== "block")
{div.style.display = "block";}
else {div.style.display = "none";}}
</script>
<div onclick="showhide('newpost')">
<h1>{{ post['title'] }}</h1>
<div class="about">by {{ post['username'] }} on {{ post['created'].strftime('%d-%m-%Y') }}</div>
</div>
</header>
<div id="newpost">
<p class="body">{{ post['body'] }}</p>
</div>
</article>
{% if not loop.last %}
<hr>
{% endif %}
{% endfor %}
{% endblock %}
Of course I looked for a solution as much as I could but I'm kind of stuck plus I'm a complete beginner in HTML/JS/CSS. And one last thing, I'm currently using Python's framework Flask. Thank you by advance.
You need to give each of your posts a unique id for your approach to work.
Change your code to
<div id="{{post_id}}">
<p class="body">{{ post['body'] }}</p
</div>
where post_id is that post's unique id e.g. its id in the database you are using that you pass to the template in your view. Then, change the call to the onclick event handler to
<div onclick="showhide('{{post_id}}')">
If you don't have a unique id you can also use the for loop's index: replace all post_id instances above with loop.index. See Jinja's for loop docs for more information.

How do I populate dynamically a StringField with the value of a SelectField in Wtforms

in a wtforms, I would like my SelectField to fill up with its selected value a StringField.
I use flask, bootstrap, and python 3.7
My HTML code is as follow:
{% block body %}
<h3>Edit Bloomberg ticker details</h3>
{% from "includes/forms/_form_helpers.html" import render_field %}
<div class="form-group" id="company_id" onchange="myFunction(event)">
{{render_field(form.company_names, class_="form-control")}}
</div>
<div class="form-group" id="isin_id">
{{render_field(form.isin_id, class_="form-control")}}
</div>
<script>
function myFunction(e) {
document.getElementById("isin_id").value = e.target.value
}
</script>
{% endblock %}
And the pyhon behind is as follow:
class DefTickerForm(_Form):
choices_companies = [(1,'Facebook'), (2, 'Google') ]
company_names = _SelectField(label='Company names:', choices=choices_companies, coerce=int)
isin_id = _StringField(label='isin_id', validators=[_validators.DataRequired], default=-1)
I would like that when the user select 'Facebook', the isin SelectField to be equal to 1. But so far it does nothing.
Note that if if code:
alert(e.target.value)
I get the wanted value. so issue is to set the TextField value.
my render field code is as followed (from a youtube tutorial):
{% macro render_field(field) %}
{{ field.label }}
{{ field(**kwargs)|safe }}
{% if field.errors %}
{% for error in field.errors %}
<span class="help-inline"> {{ error }}</span>
{% endfor %}
{% endif %}
{% endmacro %}
Any help would be much apreciated as google isn't so good on these.
Best
apparently TextField only accepts strings (I guess obvious if you are used to javascript)
so code working is as follow in case someone get he same problem:
<div class="form-group" onchange="myFunction(event)">
{{render_field(form.company_names, class_="form-control")}}
</div>
<div class="form-group">
{{render_field(form.isin_id, class_="form-control")}}
</div>
<script>
function myFunction(e) {
var x = e.target.value;
alert(x);
document.getElementById("isin_id").value = x.toString();
}
</script>
As a note, Jinja or anyway my render, use the fields names as default IDs (wich i realised using Chrome inpector. Meaning I didn't have to add an id for each Div. Anyway that is the thoughts of a beginenr in Javascripts.

If Statement in Nunjucks Macro Not working

I'm new to Nunjucks. Am loving it so far but ran into an issue.
I just made a macro that will output a title & description. I also have if statement inside of my macro to show a particular div "if" I'm on a certain page.
My issue is that my "if statement" isn't working at all. Is it not possible to do a "if statement" in a macro like this? I know the if statement is working correctly. It works if I include the .njk template as an include. Here is an example:
{% macro titleInfo(title, description) %}
<div class="header-title text-white">
<div class="container">
<div class="row align-items-center">
<div class="col-sm-12 col-lg-9">
<h1>
{{title}}
</h1>
<p>
<small>
{{description | safe}}
</small>
</p>
</div> <!-- /.col -->
{% if active_page == 'check' %} {# this isn't working right now #}
<div class="col-sm-12 col-lg-3 text-lg-right frames-in-stock">
<p>
<strong>000</strong> of <strong>000</strong>
</p>
</div> <!-- /.col -->
{% endif %}
</div> <!-- /.row -->
</div> <!-- /.container -->
</div> <!-- /.header-title -->
{% endmacro %}
I'm including the macro and implementing it on my page like so:
{% extends "layout.njk" %}
{% set active_page = "check" %}
{% import "../templates/components/header-title.njk" as title %}
{% block content %}
{% include "components/header.njk" %}
{# {% include "components/header-title.njk" %} #}
{{
title.titleInfo (
'Your Inventory',
'Please check all that apply.'
)
}}
{% include "components/main-nav.njk" %}
{% endblock %}
Is it not possible to have an if statement in a macro? If it is possible, any direction on what I'm doing wrong would be great!
A macros doesn't have access to global scope when it define in separated file. You must pass active as variable to macros.
{% macro titleInfo(title, description, active) %} ...
Another way is using custom loader to substitution macros to main render page on run-time.
....
// rendered template
{% set active = true %}
{% enable SOMEMACRO %}
...
// somemacro.njk
{% macro SOMEMACRO %}
...
{% endmacro %}
...
// the custom loader implementation to support {% enable regexp-macro-name %}
let macros = fs.readdirSync('templates/macros')
.reduce(function (res, f) {
res[f] = fs.readFileSync(`templates/macros/${f}`, 'utf8');
return res;
}, {});
let CustomLoader = nunjucks.FileSystemLoader.extend({
getSource: function(name) {
let result = nunjucks.FileSystemLoader.prototype.getSource.call(this, name);
if (!result)
return null;
result.src = result.src.replace(/{%\senable\s(\S+)\s%}/g,
function(str, match, offset, s){
return Object.keys(macros)
.filter((key) => (new RegExp(match)).test(key))
.map((key) => macros[key] || '')
.join('\n');
}
);
return result;
}
});
let env = new nunjucks.Environment(new CustomLoader(['templates/']), {autoescape: true});
env.express(app);

Django-endless-pagination: apply javascript to whole html after "show more"?

views.py
class PortfolioListView(AjaxListView):
model = Portfolio
context_object_name = "portfolios"
template_name = "portfolios/portfolio_list.html"
page_template = 'portfolios/portfolio_list_page.html'
portfolio_list.html
{% extends "skeleton/base.html" %}
{% load el_pagination_tags %}
{% block content %}
<div class="test-class">
hi-1
</div>
<section>
<div class="container">
<div class="row">
<div class="col-md-offset-1 col-md-10">
{% include page_template %}
</div>
</div>
</div>
</section>
{% endblock %}
{% block custom_js %}
<script src="{% static 'el-pagination/js/el-pagination.js' %}"></script>
<script>$.endlessPaginate({});</script>
<script type="text/javascript">
$(document).ready(function(){
$(".test-class").click(function(){
alert("ji");
});
});
</script>
{% endblock %}
portfolio_list_page.html
<div>
{% lazy_paginate portfolios %}
<div class="test-class">
hi-2
</div>
<div class="test-class">
hi-3
</div>
{% show_more %}
</div>
When I load portfolio_list.html page and click hi-1, it shows alert.
But when I click show more and click hi-2 or hi-3, it doesn't show alert.
I want to show alert even I clicked hi-2 or hi-3.
How can I implement this?
p.s Actually, this is a kinda very simple code for showing clearly what I want to do.
What I eventually want to do is to execute whole javascripts code(e.g _owl_carousel(), _flexslider(), _popover(), _lightbox(), _mixitup(),, etc) after loading portfolio_list_page so that this whole javascript function also can be applied to newly loaded page

Categories

Resources