I use Symfony3 with Twig template and VueJs 2.
My symfony controller returns a Twig view with Articles component :
{% extends 'base.html.twig' %}
{% block body %}
<Articles></Articles>
{% endblock %}
And my Articles.vue component :
<template>
<section class="articleapp">
<header>
<h1>Mes articles</h1>
</header>
<div class="main">
<li class="article" v-for="article in articles">
{{ article.title }}
</li>
</ul>
</div>
</section>
</template>
<script>
export default {
data () {
return {
articles: [{title: "Voici le nom d'un article"}]
}
}
}
</script>
I would like translate my title "Mes articles" with Sf/Twig i18n like {{ "Mes articles"|trans({}, "") }} to benefit of translate catalogs.
With Single page component it's impossible.
But with X-Template, the problem can be resolve :
index.html.twig :
{% extends 'base.html.twig' %}
{% block title %}{{ "Mes posts"|trans({}, 'app') }}{% endblock %}
{% block body %}
<div id="app">
<post-form></post-form>
</div>
{% endblock %}
{% block components %}
{{ include("components/form.html.twig", {"form": form }) }}
{% endblock %}
components/form.html.twig :
<script id="post-form-template" type="text/x-template">
<div>
{{ "Mon formulaire"|trans({}, '') }}
{{ form(form) }}
<h1>${ greetings }</h1>
</div>
</script>
And my app.js :
import Vue from "vue";
import VueResource from "vue-resource"
Vue.use(VueResource);
Vue.component('post-form', {
delimiters: ['${', '}'],
template: '#post-form-template',
data: function () {
return {
greetings: 'Hello'
};
}
});
new Vue({
delimiters: ['${', '}'],
el: "#app"
});
Related
I have posts in my 11ty blog - books, whose names started on some letters. For example, i have 12 books: 4 with first letter "A", 4 with first letter "F", 4 with first letter "Q".
And I have a code for printing all books (I use njk in blog layouts):
<div>
{% for book in collections.books %}
<a class="book" href="{{ book.url }}">
<article>
<h3 class="text-link">
{{ book.data.name }}
</h3>
<p>{{ book.data.text }}</p>
</article>
</a>
{% endif %}
{% endfor %}
</div>
I need to print in front of all books with the letter "A":
<h2>A</h2>
Аfter that there will be 4 books with the letter "A". And after this books, will be the same heading, but with letter "F":
<h2>F</h2>
How to do it? In njk, it is difficult to assign/rewrite and manipulate variables. Maybe there is some normal example?
You can use Nunjucks's set tag to create and modify a variable that keeps track of the previous initial letter:
<div>
{% set previousInitial = null %}
{% for book in collections.books %}
{% if book.data.name[0] != previousInitial %}
<h2>{{ book.data.name[0] }}</h2>
{% endif %}
{% set previousInitial = book.data.name[0] %}
<a class="book" href="{{ book.url }}">
<article>
<h3 class="text-link">
{{ book.data.name }}
</h3>
<p>{{ book.data.text }}</p>
</article>
</a>
{% endfor %}
</div>
Demo:
const data = {
collections: {
books: [
{ data: { name: 'ABC Book', text: 'Learn the alphabets!' }, url: '/book/abc-book/' },
{ data: { name: 'Another A book', text: 'A nice book' }, url: '/book/another-a-book/' },
{ data: { name: 'Foo...', text: '...bar and baz' }, url: '/book/foo/' },
],
},
}
const template = `
<div>
{% set previousInitial = null %}
{% for book in collections.books %}
{% if book.data.name[0] != previousInitial %}
<h2>{{ book.data.name[0] }}</h2>
{% endif %}
{% set previousInitial = book.data.name[0] %}
<a class="book" href="{{ book.url }}">
<article>
<h3 class="text-link">
{{ book.data.name }}
</h3>
<p>{{ book.data.text }}</p>
</article>
</a>
{% endfor %}
</div>
`
console.log(nunjucks.renderString(template, data))
.as-console-wrapper { max-height: 100% !important; }
<script src="https://unpkg.com/nunjucks#3.2.3/browser/nunjucks.min.js"></script>
If the items in collections.books are not already sorted by their names, you can sort them with Nunjucks's sort filter:
{% for book in collections.books|sort(attribute='data.name') %}
An alternative is to create a new collection. Consider creating a collection of books already categorized by their initial letters, so you can reduce the amount of logic in your Nunjucks template. Example of how the new collection and template could look like:
const data = {
collections: {
booksCategorizedByInitialLetters: {
A: [
{ data: { name: 'ABC Book', text: 'Learn the alphabets!' }, url: '/book/abc-book/' },
{ data: { name: 'Another A book', text: 'A nice book' }, url: '/book/another-a-book/' },
],
F: [
{ data: { name: 'Foo...', text: '...bar and baz' }, url: '/book/foo/' },
],
},
},
}
const template = `
<div>
{% for initialLetter, books in collections.booksCategorizedByInitialLetters %}
<h2>{{ initialLetter }}</h2>
{% for book in books %}
<a class="book" href="{{ book.url }}">
<article>
<h3 class="text-link">
{{ book.data.name }}
</h3>
<p>{{ book.data.text }}</p>
</article>
</a>
{% endfor %}
{% endfor %}
</div>
`
console.log(nunjucks.renderString(template, data))
.as-console-wrapper { max-height: 100% !important; }
<script src="https://unpkg.com/nunjucks#3.2.3/browser/nunjucks.min.js"></script>
(I'm not sure whether it's possible to create a collection whose type is an object, but IIRC it should be possible.)
I'm using Symfony 5 / twig with webpack.encore and vue.js.
I passing datas (translation texts) to the used twig-template like this:
Controller:
class HomepageController extends AbstractController
{
const PAGE_ID = 2; // constants PageId
/**
* #Route("{_locale}/homepage", name="app_homepage")
*
*/
public function index(Request $request,SpracheRepository $spracheRepository)
{
$arrTrans = $spracheRepository->findAllOfSpecificPage(self::PAGE_ID,$request->getLocale());
return $this->render('core/homepage/homepage.html.twig', [
'headline' => $arrTrans['lb_overview'],
'pageId' => self::PAGE_ID,
'arrTrans' => $arrTrans
]);
}
}
...and I'd like to access the datas "arrTrans" in the vue file.
The html.twig is looking like:
{% block content %}
<div id="app"></div>
{% endblock %}
{% block stylesheets %}
{{ parent() }}
{{ encore_entry_link_tags('homepage') }}
{% endblock %}
{% block javascripts %}
{{ parent() }}
{{ encore_entry_script_tags('homepage') }}
{% endblock %}
The corsponding .js file:
import Vue from 'vue';
import App from '../vue/homepage'
new Vue({
render: (h) => h(App),
}).$mount('#app');
...and the vue file themselfe: (I'd like to use the arrTrans in the div of the "button-overview-lable")
<template>
<div class="button-overview">
<div class="button-overview-lable">{{ arrTrans['lb_planning'] }}</div>
</a>
</div>
</template>
<script>
export default {
name: "Homepage",
}
</script>
<style lang="scss">
#import "../../../core/app/css/helper/variables";
#import "../css/homepage";
</style>
If you need to pass data from your template file to a javascript file you can use data attribute.
<div id="app" data-trans="{{ arrTrans | json_encode() }}"></div>
new Vue({
el: '#app',
data: {
trans: null
},
mounted: function() {
this.trans = this.$el.getAttribute('data-trans');
}
});
You can create a JS variable in twig template and pass json-encoded array. Like this (also we disable automatic html-escaping using raw filter):
{% block content %}
<script>
const arrTrans = JSON.parse('{{ arrTrans|json_encode()|raw }}')
</script>
<div id="app"></div>
{% endblock %}
But you have to deal with ' character in your locale strings because it will cause JS syntax error. Replace raw filter with escape('js').
So, the final twig template is:
{% block content %}
<script>
const arrTrans = JSON.parse('{{ arrTrans|json_encode()|escape('js') }}')
</script>
<div id="app"></div>
{% endblock %}
Then in vue app you will just use this variable in data:
<template>
<div class="button-overview">
<div class="button-overview-lable">{{ arrTrans.lb_planning }}</div>
</a>
</div>
</template>
<script>
export default {
name: 'Homepage',
data() {
return {
arrTrans,
};
},
};
</script>
<style lang="scss">
#import "../../../core/app/css/helper/variables";
#import "../css/homepage";
</style>
Thanks for the answers, but I had some problems to implement them. What's now working for me was:
Homepage.html.twig
<div ref="translation" data-translation="{{ arrTrans|json_encode() }}"></div>
Homepage.js
new Vue({
el: '#app',
render: (h) => h(App),
})
homepage.vue
<template>
<div>
<div>{{ trans }}</div>
<div>{{ trans['lb_planning'] }}</div>
</div>
</template>
export default {
name: 'HomePage',
data() {
return {
trans: {}
};
},
mounted() {
let elTrans = document.querySelector("div[data-translation]");
let myTrans = JSON.parse(elTrans.dataset.translation);
this.trans= myTrans;
}
}
I'm trying to set flash message from ContactAction then redirect on Homepage, but on it, I can't see my flash message, maybe my session is reset ? Can I have some help, I'm a beginer on Symfony.
CoreController that contain both index and contact functions :
<?php
namespace OC\CoreBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class CoreController extends Controller
{
public function indexAction()
{
$ServiceAdverts = $this->container->get('oc_core.listAdverts');
$adList = $ServiceAdverts->getListAdverts();
return $this->render("OCCoreBundle:Core:index.html.twig", array(
'listAdverts' => $adList
));
}
public function contactAction()
{
$this->addFlash('info', 'Contact page not ready yet !');
return $this->redirectToRoute('oc_core_homepage');
}
}
Twig template (homepage) :
{% block body %}
<div>
Messages flash :
{% for msg in app.session.flashBag.get('info') %}
<div class="alert alert-success">
{{ msg }}
</div>
{% endfor %}
</div>
<h2>Liste des annonces</h2>
<ul>
{% for advert in listAdverts %}
<li>
<a href="{{ path('oc_platform_view', {'id': advert.id}) }}">
{{ advert.title }}
</a>
par {{ advert.author }},
le {{ advert.date|date('d/m/Y') }}
</li>
{% else %}
<li>Pas (encore !) d'annonces</li>
{% endfor %}
</ul>
Contact
{% endblock %}
Symfony 3.3 made improvements to flash messages so your Twig template should look different. The app.session.flashBag.get() call is now replaced by app.flashes().
So your Twig code would now be:
{% for msg in app.flashes('success') %}
<div class="alert alert-success">
{{ msg }}
</div>
{% endfor %}
Try this, works for me in 3.2 and 3.4
{% for type, flash_messages in app.session.flashBag.all %}
{% for msg in flash_messages %}
<div class="alert alert-{{ type }}">
{{ msg }}
</div>
{% endfor %}
{% endfor %}
Another thing is that once you called the flashBag it turns empty so you can't use it twice. Check your code that it hasn't been called on another page right before a second redirect ...
Need to use a variable that is in the parent template.
I have layout:
<html>
<head>
</head>
<body>
{%block content %}
{% set com = 0 %}
DEFALUT CONTENT
{% endblock %}
</body>
</html>
And children template:
{% extends '::layout.html.twig' %}
{% block content %}
HOW USE LAYOUT VARIABLE HERE? LIKE: {{ com }}
{% endblock %}
Thanks in advance!
Use the parent() twig function.
{% extends '::layout.html.twig' %}
{% block content %}
{{ parent() }}
{{ com }}
{% endblock %}
The above code will render a template like :
<html>
<head>
</head>
<body>
{% block content %}
{% set com = 0 %}
DEFAULT CONTENT
{{ com }}
{% endblock %}
</body>
</html>
I have this
base.html.twig
<html>
{% block template_scope_vars %}
{% endblock %}
<head></head>
<body>
{% block content %}
{% endblock %}
</body>
</html>
user.html.twig
extends base...
{%block template_scope_vars %}
{% set test= "bippo" %}
{%endblock%}
{%block content %}
{{ test }}
{% endblock %}
Error
Variable "test" does not exist
Ok please please don't tell me that I don't have to use blocks for this, the use case is more difficult than this simple example obviously. The idea is that test gets available in the base.html.twig as well, as well as any sub-templates of it that I might be using in future
for the special use case I had I could use this. It is a shame though, there is no neater solution. All other solution I could come up with, would require to create a new file for each set of template scope variables. Which plain sucks
base.html.twig
<div id="menu"> //this is like a sidebar menu
<div class="menuitem {% block foo %}{% endblock %}">
Foo
</div>
<div class="menuitem {% block bar %}{% endblock %}">
Bar
</div>
<div>
{% block content %}
{% endblock %}
Then in
foo.html.twig
{% extends "MyBundle::base.html.twig" %}
{% block foo %}active{% endblock %}
{% block content %}
Some content specific for foo
{% endblock %}
bar.html.twig
{% extends "MyBundle::base.html.twig" %}
{% block bar%}active{% endblock %}
{% block content %}
Some content specific for bar
{% endblock %}
and then you set the css for the background of e.g.
CSS
menuitem.active {
background-color: red;
}
base.html.twig
<div id="menu"> //this is like a sidebar menu
<div class="menuitem{{ active == 'foo' ? ' active' : '' }}">
Foo
</div>
<div class="menuitem{{ active == 'bar' ? ' active' : '' }}">
Bar
</div>
<div>
{% block content %}
{% endblock %}
foo.html.twig
{% extends "MyBundle:Whatever:base.html.twig" %}
{% block content %}
{{ content }}
{% endblock %}
Controller
/**
* #Route("/foo", defaults={"param" = null}, name="foo")
* #Route("/foo/{param}", name="foo_with_param")
* #Template()
*/
public function fooAction($param)
{
// This would really be getting data from the Model
if ($param == 'something') {
$content = 'Some content';
$active = 'foo';
} else {
$content = 'Different content';
$active = 'bar';
}
return array(
'content'=> $content,
'active' => $active,
);
}
When rendering foo.html.twig, base.html.twig will get the variable "active".