Variable variables in Twig - twig

I'm trying to write a menu with 1 level submenu using hash in Twig. My code is:
{% set regiones = {
patagonia: { title: "Patagonia"},
pampa: { title: "Pampa"},
cuyo: { title: "Cuyo"},
noreste: { title: "Noreste"},
noroeste: { title: "Noroeste"}
} %}
{% set patagonia = {
neuquen: { title: "Neuquén"},
rionegro: { title: "Río Negro"},
chubut: { title: "Chubut"},
santacruz: { title: "Santa Cruz"},
tierradelfuego: { title: "Tierra del Fuego"}
} %}
{% set pampa = {
buenosaires: { title: "Buenos Aires"},
cordoba: { title: "Córdoba"},
lapampa: { title: "La Pampa"},
santafe: { title: "Santa Fe"}
} %}
{% set cuyo = {
mendoza: { title: "Mendoza"},
sanjuan: { title: "San Juan"},
sanluis: { title: "San Luis"}
} %}
<nav>
<ul>
{% for slug, item in regiones %}
<li>{{ item.title }}
<ul>
{% for slugg, itemm in {{ slug }} %}
<li>{{ itemm.title }}</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
</nav>
This line: " {% for slugg, itemm in {{ slug }} %} " don't work.
How I can write in twig a variable variable like ${$slug} in PHP?
Thanks,
Mikel

_context holds the variables in the current context, so you can do:
{% for slugg, itemm in _context[slug] %}
Because you have not set variables noreste and noroeste, you need to surround the for block with an if block (otherwise Twig will throw an exception as _context doesn't have the keys noreste and noroeste):
{% if _context[slug] is defined %}
<ul>
{% for slugg, itemm in _context[slug] %}
<li>{{ itemm.title }}</li>
{% endfor %}
</ul>
{% endif %}
See TwiggFiddle
An alternative approach is to set the cities to a new hash like this:
{% set cities = {
patagonia: {
neuquen: { title: "Neuquén"},
rionegro: { title: "Río Negro"},
chubut: { title: "Chubut"},
santacruz: { title: "Santa Cruz"},
tierradelfuego: { title: "Tierra del Fuego"}
},
pampa: {
buenosaires: { title: "Buenos Aires"},
cordoba: { title: "Córdoba"},
lapampa: { title: "La Pampa"},
santafe: { title: "Santa Fe"}
},
cuyo: {
mendoza: { title: "Mendoza"},
sanjuan: { title: "San Juan"},
sanluis: { title: "San Luis"}
}
} %}
And then modify your loop like this:
{% if cities[slug] is defined %}
<ul>
{% for slugg, itemm in cities[slug] %}
<li>{{ itemm.title }}</li>
{% endfor %}
</ul>
{% endif %}
And one more approach is to put regions and cities in the same hash:
{% set regiones = {
patagonia: {
title: "Patagonia",
cities: {
neuquen: { title: "Neuquén"},
rionegro: { title: "Río Negro"},
chubut: { title: "Chubut"},
santacruz: { title: "Santa Cruz"},
tierradelfuego: { title: "Tierra del Fuego"},
},
},
pampa: {
title: "Pampa",
cities: {
buenosaires: { title: "Buenos Aires"},
cordoba: { title: "Córdoba"},
lapampa: { title: "La Pampa"},
santafe: { title: "Santa Fe"},
},
},
cuyo: {
title: "Cuyo",
cities: {
mendoza: { title: "Mendoza"},
sanjuan: { title: "San Juan"},
sanluis: { title: "San Luis"},
},
},
noreste: {
title: "Noreste",
},
noroeste: {
title: "Noroeste",
},
} %}
<nav>
<ul>
{% for slug, item in regiones %}
<li>
{{ item.title }}
{% if item.cities is defined %}
<ul>
{% for slugg, itemm in item.cities %}
<li>{{ itemm.title }}</li>
{% endfor %}
</ul>
{% endif %}
</li>
{% endfor %}
</ul>
</nav>

Related

Content in tab does not show in Shopware 6

I have added a custom tab on my module’s details page, however, the tab is rendered but the content for the tab isn’t showing. There isn’t any error in the console log as well.
index.js
import './page/wt-shopfinder-list';
import './page/wt-shopfinder-detail';
import './view/wt-shopfinder-detail-base';
import './view/wt-shopfinder-detail-review';
const { Module } = Shopware;
Module.register('example-shopfinder', {
type: 'plugin',
name: 'ExampleShopFinder',
title: 'example-shopfinder.general.mainMenuItemGeneral',
description: 'example-shopfinder.general.descriptionTextModule',
version: '1.0.0',
targetVersion: '1.0.0',
color: '#9AA8B5',
icon: 'default-shopping-paper-bag',
entity:'wt_shop_finder',
routes: {
index: {
components: {
default: "wt-shopfinder-list"
},
path: 'index',
},
detail: {
component: 'wt-shopfinder-detail',
path: 'detail/:id',
redirect: {
name: 'example.shopfinder.detail.base',
},
children: {
base: {
component: 'wt-shopfinder-detail-base',
path: 'base',
meta: {
parentPath: 'example.shopfinder.index'
},
},
review: {
component: 'wt-shopfinder-detail-review',
path: 'review',
meta: {
parentPath: 'example.shopfinder.index'
},
},
},
meta: {
appSystem: {
view: 'detail',
},
},
props: {
default(route) {
return {
shopFinderId: route.params.id,
};
},
},
}
},
navigation: [{
id: 'wt-shopfinder',
label: 'example-shopfinder.menu.mainMenuItemGeneral',
color: '#ff3d58',
icon: 'default-shopping-paper-bag-product',
path: 'example.shopfinder.index',
parent: "sw-marketing",
position: 100,
}],
});
wt-shopfinder-detail/wt-shopfinder-detail-html.twig
<sw-tabs
class="wt_shopfinder-detail-page__tabs"
position-identifier="wt-shopfinder-detail"
>
{% block wt_shopfinder_detail_content_tabs_general %}
<sw-tabs-item
:route="generalRoute"
:title="$tc('sw-customer.detail.tabGeneral')"
:disabled="false"
>
{{ $tc('sw-promotion-v2.detail.tabs.tabGeneral') }}
</sw-tabs-item>
{% endblock %}
{% block wt_shopfinder_detail_content_tabs_general2 %}
<sw-tabs-item
:route="reviewRoute"
:title="$tc('sw-customer.detail.tabGeneral')"
:disabled="false"
>
Review
</sw-tabs-item>
{% endblock %}
</sw-tabs>
wt-shopfinder-detail/index.js
//Sharing only the url part for tab navigation
generalRoute() {
console.log("ID = "+this.shopFinderId);
return {
name: 'webbytroops.shopfinder.detail.base',
params: { id: this.shopFinderId },
};
},
wt-shopfinder-detail-base/index.js
import template from './wt-shopfinder-detail-base.html.twig';
const { Component } = Shopware;
const { Criteria } = Shopware.Data;
Component.register('wt-shopfinder-detail-base', {
template,
inject: ['repositoryFactory'],
metaInfo() {
return {
title: "Custom"
};
}
});
wt-shopfinder-detail-base/wt-shopfinder-detail-base.html.twig
<sw-card title="Custom">
Hello world!
</sw-card>
The correct pattern for the route would be {moduleName}.{routeName}.{childName} and in the module name dashes are replaced by dots. So the correct route in your case should be example.shopfinder.detail.base.
Also, unless you omitted it, you're missing the router-view tag after the sw-tabs component.
<sw-container>
<sw-tabs>
...
</sw-tabs>
<router-view />
</sw-container>

How to submit matrix and category fields in frontend

So i have an entry to submit games.
The games entry has a lot of different fields, and one of the fields is:
a field with fieldType "category" linked to a category
a field with fieldType "matrix" called materialList that looks like this:
The "MaterialName" has fieldType categories and is linked to a category
So now I am trying to submit a form in my frontend.
So far I managed to correctly save regular text fields, but I failed to correctly save something as a matrix, and I failed to properly save a category that is correctly linked.
I already found an example on https://craftcms.com/knowledge-base/entry-form but this only shows me the regular text fields.
This is the code I have so far:
<form method="post" accept-charset="UTF-8">
{{ csrfInput() }}
{{ actionInput('entries/save-entry') }}
{{ redirectInput('spelen/{slug}') }}
{{ hiddenInput('sectionId', '1') }}
{{ hiddenInput('enabled', '1') }}
<label for="title">Title</label>
{{ input('text', 'title', entry.title, {
id: 'title',
}) }}
{{ _self.errorList(entry.getErrors('title')) }}
<label for="youngestAge">Minimum leeftijd</label>
{{ tag('input', {
id: 'youngestAge',
type: 'number',
name: 'fields[youngestAge]',
text: entry.youngestAge,
}) }}
{{ _self.errorList(entry.getErrors('youngestAge')) }}
<label for="oldestAge">Maximum leeftijd</label>
{{ tag('input', {
id: 'oldestAge',
type: 'number',
name: 'fields[oldestAge]',
text: entry.oldestAge,
}) }}
{{ _self.errorList(entry.getErrors('oldestAge')) }}
<label for="leastAmountOfPlayers">Minimum aantal spelers</label>
{{ tag('input', {
id: 'leastAmountOfPlayers',
type: 'number',
name: 'fields[leastAmountOfPlayers]',
text: entry.leastAmountOfPlayers,
}) }}
{{ _self.errorList(entry.getErrors('leastAmountOfPlayers')) }}
<label for="mostAmountOfPlayers">Maximum aantal spelers</label>
{{ tag('input', {
id: 'mostAmountOfPlayers',
type: 'number',
name: 'fields[mostAmountOfPlayers]',
text: entry.mostAmountOfPlayers,
}) }}
{{ _self.errorList(entry.getErrors('mostAmountOfPlayers')) }}
<label for="groups">Aantal groepen</label>
{{ tag('input', {
id: 'groups',
type: 'number',
name: 'fields[groups]',
text: entry.groups,
}) }}
{{ _self.errorList(entry.getErrors('groups')) }}
<label for="gametime">Speelduur</label>
{{ tag('input', {
id: 'gametime',
type: 'time',
name: 'fields[gametime]',
text: entry.gametime,
}) }}
{{ _self.errorList(entry.getErrors('gametime')) }}
<label for="intro">Intro</label>
{{ tag('input', {
id: 'intro',
type: 'text',
name: 'fields[intro]',
text: entry.intro,
}) }}
{{ _self.errorList(entry.getErrors('intro')) }}
<label for="purpose">Doel van het spel</label>
{{ tag('input', {
id: 'purpose',
type: 'text',
name: 'fields[purpose]',
text: entry.purpose,
}) }}
{{ _self.errorList(entry.getErrors('purpose')) }}
<label for="explenation">Speluitleg</label>
{{ tag('input', {
id: 'explenation',
type: 'text',
name: 'fields[explenation]',
text: entry.explenation,
}) }}
{{ _self.errorList(entry.getErrors('explenation')) }}
<label for="gameType">Spel type</label>
{{ tag('input', {
id: 'gameType',
type: 'text',
name: 'fields[gameType]',
value: 6,
}) }}
{{ _self.errorList(entry.getErrors('gameType')) }}
<label for="materialItem">Material</label>
{{ tag('input', {
id: 'gameType',
type: 'text',
name: 'fields[materialItem]',
value: entry.materialItem,
}) }}
{{ _self.errorList(entry.getErrors('materialItem')) }}
<label for="isPublic">Publiek</label>
{{ tag('input', {
id: 'isPublic',
type: 'checkbox',
name: 'fields[isPublic]',
text: false,
}) }}
{{ _self.errorList(entry.getErrors('isPublic')) }}
<label for="body">Body</label>
{{ tag('textarea', {
id: 'body',
name: 'fields[body]',
text: 'test bericht',
}) }}
{{ _self.errorList(entry.getErrors('body')) }}
<button type="submit">Publish</button>
</form>
So these are the 2 I need help with:
The first input needs to properly save linked categories
The second one needs to properly save a matrix
<label for="gameType">Spel type</label>
{{ tag('input', {
id: 'gameType',
type: 'text',
name: 'fields[gameType]',
value: 6,
}) }}
{{ _self.errorList(entry.getErrors('gameType')) }}
<label for="materialItem">Material</label>
{{ tag('input', {
id: 'gameType',
type: 'text',
name: 'fields[materialItem]',
value: entry.materialItem,
}) }}
{{ _self.errorList(entry.getErrors('materialItem')) }}
So I found out how to save a category, but I'm still stuck on the matrix part.
This is the code for the category:
{# Include a hidden input first so Craft knows to update the existing value
if no checkboxes are checked. #}
{{ hiddenInput('fields[myFieldHandle]', '') }}
{# Get all of the possible category options #}
{% set possibleCategories = craft.categories()
.group('food')
.all() %}
{# Get the currently related category IDs #}
{% set relatedCategoryIds = entry is defined
? entry.myFieldHandle.ids()
: [] %}
<ul>
{% nav possibleCategory in possibleCategories %}
<li>
<label>
{{ input(
'checkbox',
'fields[myFieldHandle][]',
possibleCategory.id,
{ checked: possibleCategory.id in relatedCategoryIds }
) }}
{{ possibleCategory.title }}
</label>
{% ifchildren %}
<ul>
{% children %}
</ul>
{% endifchildren %}
</li>
{% endnav %}
</ul>

sw-text-editor field doesn't shop up as config field

I'm trying to add an wysiwyg field with the sw-text-edtior component for my cms-element as an config field, but it doesn't show up at all. If I try an sw-text-field instead of an sw-text-editor component it does show up. I can't seem to find it in the docs if I am missing something.
My code:
config/sw-cms-el-config-collapse.html.twig
{% block sw_cms_element_collapse_config %}
<sw-text-editor
v-model="blockContent"
:allow-inline-data-mapping="true"
sanitize-input
#element-update="onElementUpdate">
</sw-text-field>
{% endblock %}
config/index.js
import template from './sw-cms-el-config-collapse.html.twig';
Shopware.Component.register('sw-cms-el-config-collapse', {
template,
mixins: ['cms-element'],
computed: {
blockContent: {
get() {
return this.element.config.blockContent.value;
},
set(value) {
this.element.config.blockContent.value = value;
},
},
},
created() {
this.createdComponent();
},
methods: {
createdComponent() {
this.initElementConfig('s7-collapse');
},
onElementUpdate(element) {
this.emitChanges(element);
},
},
});

Apostrophecms: parseAggregate: expected colon after dict key

I am new in apostrophecms i am building my first app, i want to define widgets in home using macro but When the JS calls nunjucks render, I get the following error::
parseAggregate: expected colon after dict key
this is my Home page:
{#apostrophe-pages/views/home.html#}
{% extends "layout.html" %}
{% import "macros/utils.html" as areas %}
{% block title %}Home{% endblock %}
{% block main %}
<main>
{{ areas.columns() }}
</main>
{% endblock %}
{#views/macros/utils.html#}
{% macro columns() %}
{{ apos.area(data.page, 'main', {
blockLevelControls; true,
widgets: {
'one-column': {},
'two-column': {}
}
}
}) }}
{% endmacro %}
{% macro column(parent, name, imageSize) %}
{{ apos.area(parent, name, {
widgets: {
'apostrophe-rich-text': {
toolbar: [ 'Styles', 'Bold', 'Italic', 'Link', 'Unlink' ],
styles: [
{ name: 'Heading', element: 'h3' },
{ name: 'Subheading', element: 'h4' },
{ name: 'Paragraph', element: 'p' }
]
},
'apostrophe-images': {
size: 'imageSize'
},
'apostrophe-video': {},
'test': {}
}
}) }}
{% endmacro %}
You have a typo in your literal object declaration. Instead of
{{ apos.area(data.page, 'main', {
blockLevelControls; true,
widgets: {
'one-column': {},
'two-column': {}
}
}
}) }}
it should be
{{ apos.area(data.page, 'main', {
blockLevelControls: true, // <- Here is a colon now
widgets: {
'one-column': {},
'two-column': {}
}
}
}) }}

How to pass Data from Test1 Component to Test2 Component in Vue.js?

I have a component called Test1.vue and in it i have a value total:120, i want this value to pass to Test2.vue? how i could to that?
Test1.vue:
<Test2 v-bind:totalPrice='totalValue'></Test2>
data() {
return {
totalValue: 120,
};
},
Test2.vue:
<h3>{{ totali }}</h3> --> Not working
props: {
totalPrice: {
type: Number,
}
},
likes {{ totalPrice }}
in Test2.vue , right?
Try sending your value through props
<Test2 :totalPrice="totalValue"></Test2>
and then use the props in the Test2 Component
export default {
props: {
totalPrice: {
type: Number,
}
}
}
and in the template
<template>
<h2>{{ totalPrice }}</h2>
</template>

Resources