i would like to reuse parts of the url in the content of the webpage
as example
domain.com/specialist/brand/location/
where i want to reuse "brand" multiple times in the content of the webpage
and also "location" multiple times in the content of the webpage.
first i tried it with variables
url/?specialist=brand&bezorgen=city
but this does not work with the following method, because it renders them only once
i've tried the
<script>
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
const specialist = urlParams.get('specialist');
const bezorgen = urlParams.get('bezorgen');
document.getElementById("specialist").innerHtml = specialist;
document.getElementById("bezorgen").innerHtml = bezorgen;
</script>
and in the html
<span id=specialist></span>
<span id=bezorgen></span>
What would be the fastest way to get this working
technology = twig and js
Thanks in advance
Twig
As you are using a custom framework I'd suggest you to mimick the app.request.get behavior that is available in Symfony.
First create a class that delivers the same logic
Request.php
class Request {
public function __construct() {}
public function get($key) {
return isset($_GET[$key]) ? $_GET[$key] : null;
}
public function post($key) {
return isset($_POST[$key]) ? $_POST[$key] : null;
}
public function url() {
$http = 'http'.(isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 's': '');
return $http.'://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
}
}
Register the class in twig
The easiest way to add an instance of the Request class is just to add it as a global
<?php
...
$twig->addGlobal('app', [ 'request' => new Request(), ]);
Access the class inside a template
<p>this {{ app.request.get('brand') }} bike is pretty awesome and we can deliver this at {{ app.request.get('location') }}. This {{ app.request.get('brand') }} is really trustworthy</p>
mandatory note
Please be aware it is possible that clients will try to inject/generate unsafe output. However twig will prevent this as long as you don't mark the content as safe with e.g. the filter raw
Javascript
The problem with your javascript is probably because you are creating elements with the same id over and over again, which is why only the first element with the correct id will be replaced correctly
An easy solution would be to switch to classes
var foo = 'foo';
var bar = 'bar';
document.querySelectorAll('.foo').forEach(span => {
span.innerHTML = foo;
});
document.querySelectorAll('.bar').forEach(span => {
span.innerHTML = bar;
});
span {
display: block;
}
span + span {
margin: 10px 0 0 0;
}
span.foo {
color: red;
}
span.bar {
color: green;
}
<span class="foo"></span>
<span class="foo"></span>
<span class="bar"></span>
<span class="foo"></span>
<span class="foo"></span>
<span class="bar"></span>
I might be misunderstanding, but the OP seems to ask two things: (1) how to extract info from the url -- either from within the path or the query, (2) how to alter the dom with that info.
Looking at (1), you can get a lot of information from the expression new URL(window.location.href);
// using the path
// these window-named vars to simulate the browsers' window global
let windowA = {
location: {
href: "http://example.com/specialist/mybrand/mylocation"
}
};
const urlA = new URL(windowA.location.href);
console.log(urlA.pathname.split('/').slice(-2));
// using the query
let windowB = {
location: {
href: "http://example.com/specialist/?brand=mybrand&location=mylocation"
}
};
const urlB = new URL(windowB.location.href);
const params = [urlB.searchParams.get('brand'), urlB.searchParams.get('location')]
console.log(params);
Looking at (2), the OP code looks fine, except the div's ids need to be placed in quotes...
let _window = {
location: {
href: "http://example.com/specialist/mybrand/mylocation"
}
};
const url = new URL(_window.location.href);
const components = url.pathname.split('/').slice(-2);
document.getElementById("brand").innerHTML = `<h1>${components[0]}</h1>`;
document.getElementById("location").innerHTML = `<h1>${components[1]}</h1>`;
<!-- notice the quoted id expressions -->
<div id="brand"></div>
<div id="location"></div>
twig only solution:
https://www.url.com/service/bikespecialist/trek/amsterdam/
{% set brand %}{{ request.url| split('/', 7)[5]| trim('/') }}{% endset %}
{% set location %}{{ request.url| split('/', 7)[6]| trim('/') }}{% endset %}
in the text
this {{ brand }} bike is awesome, come and buy it at {{ brand }} {{ location }}
To all who replied, many thanks! this problem had me bugged for the last couple weeks
and in the case of no brand or location in the url
{% set band %}{{ request.url| split('/', 7)[5]| trim('/') |capitalize }}{% endset %}
{% if brand is empty %}
{% set brand %}qualitybikes {% endset %}
{% endif %}
{% set loation %}{{ request.url| split('/', 7)[6]| trim('/') |capitalize }}{% endset %}
{% if location is empty %}
{% set location %}entire country {% endset %}
{% endif %}
Related
The below code is slightly different than what is rendered, I haven't pushed my changes yet. So keep that in mind.
It is supposed to
iterate through the YAML data file.
place a link everywhere that is required of it.
However, currently, it is only placing the last element seen by the for loop everywhere I want a link.
I have a YAML file with two links, and instead of placing the link in the block associated with its PID, it is placing the second link in all link locations.
I am using REGEX to parse, so anywhere in the YAML file that is aaaa a link gets replaced at that location. That works, it just doesn't place the right link.
Now the even more confusing part is that the first console.log console.log(faqLink{{ faq_link_pid }}); prints all of the links, correctly and in the order, they are in the file.
<h1 class="fs-6 fw-500" align="center">Frequently Asked Questions <br> Welcome to our FAQ!</h1>
<div class="accordiobody">
{% for faq in site.data.faq_yaml.frequently_asked_questions %}
<div class="accordion">
<hr>
<div class="container">
<div class="question fw-400">
<h1 class="fs-5 fw-500">
{{ faq.question }}
</h1>
</div>
<div class="answer">
<blockquote>
<span class="answerspan">
{{ faq.answer }}
</span>
</blockquote>
</div>
</div>
</div>
{% endfor %}
</div>
<script type="text/javascript">
{% assign faq_link = "" %}
{% assign faq_link_description = "" %}
{% assign faq_link_pid = 0 %}
{% for faq in site.data.faq_yaml.frequently_asked_questions %}
{% if faq.hyper_link != null and faq.hyper_link != "" %}
{% assign faq_link = faq.hyper_link %}
{% assign faq_link_pid = faq.faq_link_pid %}
const faqLink{{ faq_link_pid }} = "{{ faq_link }}";
{% if faq.link_description != null and faq.link_description != "" %}
{% assign faq_link_description = faq.link_description %}
const faqLinkDescription{{ faq_link_pid }} = "{{ faq_link_description }}";
console.log(faqLink{{ faq_link_pid }});
function createElement() {
for (let x in accordion) {
const link{{ faq_link_pid }} = `${faqLinkDescription{{ faq_link_pid }}}`;
console.log(link{{ faq_link_pid }});
replaceLink(link{{ faq_link_pid }});
}
}
{% endif %}
window.addEventListener('load', function () {
createElement();
});
{% endif %}
{% endfor %}
const accordion = document.getElementsByClassName('container');
for (i = 0; i < accordion.length; i++) {
accordion[i].addEventListener('click', function () {
this.classList.toggle('active');
})
}
function replaceLink(str) {
const link = document.querySelectorAll('.answerspan');
const all_link = link.forEach(function (link) {
const hyper_link = link.innerHTML;
link.innerHTML = hyper_link.replace(/aaaa./g, str);
});
}
</script>
The YAML file:
---
# Use this YAML file to create a list of FAQ questions and answers.
- question: "How will this work?"
answer: "Currently, a camera is mounted inside the headset for each eye. The camera streams through wifi to a PC client which processes and sends eye-tracking data to VR Chat."
hyper_link: ""
link_description: ""
faq_link_pid: 3
- question: "What features will be supported?"
answer: "The goal is eye tracking with eye openness and some form of pupil dilation. A far away aspiration of this project is some form of weak foveated rendering because it's cool and any small performance increase in VR is welcome."
hyper_link: ""
link_description: ""
faq_link_pid: 4
- question: "When will this be completed?"
answer: "When it's done 😉 I have a semi-busy life so development may slow and speed up inconsistently. I have a goal to be done with all base features in June."
hyper_link: ""
link_description: ""
faq_link_pid: 5
- question: "Will IR damage my eyes?"
answer: "This project has safety in mind. If you do all of the safety measures we put in place and visually test the amount of IR light you will be fine. Please note we have not finished development of all safety stuff so be careful aaaaa ."
hyper_link: "https://dammedia.osram.info/media/bin/osram-dam-2496608/AN002_Details%20on%20photobiological%20safety%20of%20LED%20light%20sources.pdf"
link_description: "here is a pdf with safety information"
faq_link_pid: 6
- question: "How expensive will this be?"
answer: "My goal is to keep it as cheap as possible with around $75 as the absolute max, with current projections being around $25-40"
hyper_link: ""
link_description: ""
faq_link_pid: 7
- question: "How do I set up my avatar?"
answer: "Check out the VR Chat face tracking wiki on our GitHub. Keep in mind that we currently only support float parameters. aaaa "
hyper_link: "https://google.com"
link_description: "Google"
faq_link_pid: 8
---
Why would you use regex + javascript to place the link? It's best to just put everything you want hidden in the HTML inside a <DIV class=hidden"> and then toggle a CSS property display:none for the DIV using javascript.
This would also work better with browsers with js turned off. More specifically: write everything in the HTML and leave the display on the DIVs you want hidden (i.e. do not set any display on the CSS file). Then use js to place upon the page loading a display:none in all these DIV elements; then use js to toggle the display for a single element.
E.g. using jquery, you would do something like
jQuery(document).ready(function($) {
//Set default open/close settings
var divs = $('.hidden').hide(); //Hide/close all containers
and then on the clickable element use slideToggle() to toggle the display of the next element (you will have to look at your DOM tree to get exactly which element)
I solved my issue by delting all of the javascript and REGEx and simply using the Liquid replace function. No JS needed. I was just over-complicating it because i hadn't read the liquid documetnation well enough :)
<span class="answerspan{{ faq.faq_link_pid }}">
{%- capture editable_part -%}
{{ faq.link_description }}
{%- endcapture -%}
{% if faq.answer contains 'aaaa ' %}
{{ faq.answer | replace: 'aaaa ', editable_part }}
{% else %}
{{ faq.answer }}
{% endif %}
</span>
I've been exploring and testing for hours how to retrieve the metafield values from a specific product handle.
I've tested using AJAX calls and using getJSON neither of which have had any success with all seem to throw the same error.
I'm aware that you most definitely can't access the product metafields directly as they're somewhat protected and therefore are excluded from the usual handle.js file I'm currently successfully able to get.
I am seeking someones help to essentially point me in the direction of how to accomplish what I'm looking for.
product-view.js file
$(document).ready(function () {
$.getScript("//cdnjs.cloudflare.com/ajax/libs/fancybox/2.1.5/jquery.fancybox.min.js").done(function() {
createProductData();
});
});
function createProductData() {
$(".product-view").click(function () {
if ($('#product-view').length == 0){
$("body").append('<div id="product-view"></div>');
}
var product_handle = $(this).data('handle');
$('#product-view').addClass(product_handle);
//Default Product Fields
jQuery.getJSON('/products/' + product_handle + '.js', function (product) {
var images = product.images;
var title = product.title;
var price = product.price;
var desc = product.description;
});
//Product Meta Fields
jQuery.getJSON('/products/' + product_handle + '?view=metafields.json', function (product) {
var dish_type = product.metafields.arena.dish;
console.log(dish_type);
//error: Cannot read properties of undefined (reading 'arena')
});
});
}
product.metafields.liquid file
{% layout none %}
{{ product.metafields.arena | json }}
So I managed to find a website finally that used what they described as a 'hack' because they don't believe this is the way that {% capture %} however, it did achieve the require outcome that I needed and hopefully is useful for others in future:
theme.liquid file
{% capture 'dishMetaFields' %}
{
{% for product in collections["all-dishes"].products %}
"{{ product.handle }}" : {
product_chinese_title : "{{product.metafields.arena.chinese_title}}",
product_feeds : "{{product.metafields.arena.feeds}}",
product_spice_level : "{{product.metafields.arena.spice}}",
product_dish : "{{product.metafields.arena.dish}}",
product_proteins : "{{product.metafields.arena._proteins}}",
product_diet : "{{product.metafields.arena._diet}}",
product_cooking_time : "{{product.metafields.arena._diet}}",
product_cooking_video : "{{product.metafields.arena._method_video}}",
product_cooking_text : "{{product.metafields.arena._method}}",
product_nutrients : "{{product.metafields.arena._nutrition}}",
product_allergies : "{{product.metafields.arena._allergies}}",
{% unless product.metafields.arena._bundle_dishes == blank %}
{%- assign dishCounter = 1 -%}
product_bundle_dish : {
{% for value in product.metafields.arena._bundle_dishes %}
"dish-{{ dishCounter }}" : "{{ value }}",
{%- assign dishCounter = dishCounter | plus:1 -%}
{% endfor %}
}
{% endunless %}
},
{% endfor %}
}
{% endcapture %}
<script type = "text/javascript">
let dishMetaFields = {{ dishMetaFields }}
</script>
You can then access the dishMetaFields from the javascript file you're using to access the product object to get the defined metafields should the handles match.
product-view.js
$(document).ready(function () {
$.getScript("//cdnjs.cloudflare.com/ajax/libs/fancybox/2.1.5/jquery.fancybox.min.js").done(function() {
createProductData();
});
});
function createProductData() {
$(".product-view").click(function () {
if ($('#product-view').length == 0){
$("body").append('<div id="product-view"></div>');
}
var product_handle = $(this).data('handle');
$('#product-view').addClass(product_handle);
jQuery.getJSON('/products/' + product_handle + '.js', function (product) {
//Default Product Fields
var images = product.images;
var title = product.title;
var price = product.price;
var desc = product.description;
//Product Meta Fields
var metafields = dishMetaFields[product_handle];
var dish_type = metafields.product_dish;
});
});
}
You can also do it like this following your initial approach. In this scenario, you don't need to iterate over all metafields of a collection but only for the required product. This should be better comparatively in terms of performance. The idea is same, that you generate the JSON object in Liquid snippet and then parse the JSON in JavaScript.
{% layout none %}
{
"product_chinese_title" : "{{product.metafields.arena.chinese_title}}",
"product_feeds" : "{{product.metafields.arena.feeds}}",
"product_spice_level" : "{{product.metafields.arena.spice}}",
"product_dish" : "{{product.metafields.arena.dish}}",
"product_proteins" : "{{product.metafields.arena._proteins}}",
"product_diet" : "{{product.metafields.arena._diet}}",
"product_cooking_time" : "{{product.metafields.arena._diet}}",
"product_cooking_video" : "{{product.metafields.arena._method_video}}",
"product_cooking_text" : "{{product.metafields.arena._method}}",
"product_nutrients" : "{{product.metafields.arena._nutrition}}",
"product_allergies" : "{{product.metafields.arena._allergies}}"
{% unless product.metafields.arena._bundle_dishes == blank %}
,
{%- assign dishCounter = 1 -%}
"product_bundle_dish" : {
{% for value in product.metafields.arena._bundle_dishes %}
"dish-{{ dishCounter }}" : "{{ value }}"
{% if forloop.last == false %}
,
{% endif %}
{%- assign dishCounter = dishCounter | plus:1 -%}
{% endfor %}
}
{% endunless %}
}
And for JavaScript part, use simple GET and then parse the response.
jQuery.get('/products/' + product_handle + '?view=metafield', function (product) {
console.log(product)
console.log(JSON.parse(product))
});
I have the following in my nunjucks file,
{% set inProgressJobs = [] %}
{% for job in jobs %}
{% set inProgressJobs = (setProgressJobs.push([
{
text: "<b>{{ job.jobTitle}}</b>"
}
]), inProgressJobs) %}
When I use this in by view I would expect to see Web Designer but I am actually seeing {{ job.jobTitle }} how I interprolate my var into the text attribute?
You try to coding inside a template. It's a possible (see below) but out of template ideology. Try to put necessary logic to filters and global functions.
const nunjucks = require('nunjucks');
const env = nunjucks.configure();
env.addFilter('print', console.log); // for debug
const html = env.renderString(`
{% set arr = [] %}
{% for i in range(1, 10) %}
{{ '' if arr.push(i) }} {# arr.push always returns non-zero so result will be '' #}
{% endfor %}
{{ arr | print }} {# print arr to console #}
`);
// console.log(html); // html is a pack of spaces and tabs
New to Django and its templates.
I'm trying to set a variable given a specific situation, and that part I think ill be able to do, the code below isn't the exact conditions its just there as a demo. The part im stuck on is how do i create a variable name, and then use that name variable elsewhere. Such as within a div or within a method or anywhere else within the html file, and withing different <Script> tags to run methods and for really any purpose.
demo scenario :
{% for row in table.rows %}
{% if row == 2 %}
{% var name = row.name %}
{% endif %}
{% endfor %}
{% if name %}
<div>{{name}}</div>
{% endif %}
my actual code Im trying to implement:
<script type="text/javascript">
function map_init_basic(map, options) {
var markerClusters = L.markerClusterGroup({ chunkedLoading: true });
{% for row in table.rows %}
var x = "{{ row.cells.x }}"
var y = {{ row.cells.y }}
var m = L.marker([y, x])
m.bindPopup("{{row.cells.first_name}} {{row.cells.last_name}} <br>{{row.cells.chgd_add}}");
m.on("click" , ()=>{
//console.log(`${first_name} ${last_name}`)
{% var first_name = {{row.cells.first_name}} %}
})
//changed row.cells.chgd_add to row.cells.Chgd_add to make sure its matching the table
markerClusters.addLayer(m);
{% endfor %}
markerClusters.addTo(map);
}
{% if first_name %}
console.log("{{first_name}}");
{% endif %}
</script>
Django templates are intended to render html but you are trying to render javascript. It can work but the way you are trying to do it is not a good practice; very soon you won't be able to maintain your code.
If you want to pass data directly from python/django to javascript, an accceptable way to do so is to put the data in a dictionary, translate it to a json string and have it set to a var in your javascript.
in a view:
data = dumps(some_dictionary)
return render(request, 'main/template.html', {'data': data})
in the template:
<script>
var data = JSON.parse("{{data|escapejs}}");
</script>
https://docs.djangoproject.com/fr/3.1/ref/templates/builtins/#escapejs
Alternative: You can use django to generate a widget with data attributes and have a javascript operating on this widget and do whaterever needs to be done.
I'm doing the same as explained here: http://symfony.com/doc/current/cookbook/form/form_collections.html
But in my case I want to add new "tags" not manually with clicking on a link, but automatically. I give to my template an array with items and for each of this items I want to add a new form - the number of items should be equal to the number of forms.
If it's possible, I'd prefer a solution like this:
{% for i in items %}
{{ i.name }} {{ form_widget(form.tags[loop.index0].name) }}
{% endfor %}
But how to automatically create objects in the controller, too? It tells me that there is no obeject with index=1, and yes - there isn't, but isn't there a way to create them automatically and not need to create for example 10 empty objects of the same kind in my controller? :(
Another thing I was thinking was something like this:
{% for i in items %}
<ul class="orders" data-prototype="{{ form_widget(form.orders.vars.prototype)|e }}">
{{ i.name }} and here should be a field from the form, for example tag.name
</ul>
{% endfor %}
I suggest that the js given in the cookbook should be changed to do this, but I'm not good in js and my tries didn't do the job.
I tried putting this in the loop:
<script>
addTagForm(collectionHolder);
</script>
and this in a .js file:
var collectionHolder = $('ul.orders');
jQuery(document).ready(function() {
collectionHolder.data('index', collectionHolder.find(':input').length);
function addTagForm(collectionHolder) {
var prototype = collectionHolder.data('prototype');
var index = collectionHolder.data('index');
var newForm = prototype.replace(/__name__/g, index);
collectionHolder.data('index', index + 1);
var $newFormLi = $('<li></li>').append(newForm);
}
});
Assuming that your main class has addTag($tag) method, you can add different 'new' tags to it.
In class Task
public function addTag($tag){
$this->tags[]=$tag;
return $this;
}
In your Controller (assuming 10 tags here)
$task=new Task();
for($i=0;i<10;i++){
$task->addTag(new Tag());
}
$form->setData($task);
In your view
{% for tag in form.tags %}
<ul class="orders">
<li>{{ form_widget(tag.name) }}</li>
</ul>
{% endfor %}
If you don't need the manually click, you can remove the JavaScript part.