How to do JPA query from view template in play - templates

I'm trying to do a JPA query from a view template but it's not working (I've verified that there are records using phpMyAdmin). I know this should normally be done through the controller and passed in via render, but this is part of building a menu which will appear on every page and I don't want to have to modify every controller to accomplish this.
What I'm currently trying is
<ul>
%{
import models.Building;
List<Building> buildings = Building.findAll();
}%
#{list items: buildings, as: 'building'}
<li>${building}</li>
#{/list}
</ul>
but I'm getting the error The template /app/views/Networks/grid.html does not compile : unexpected token: ( referencing the line which calls findAll(). What's the right way to do this?

Instead of trying to do this in the page (bad practice) or add it to every Controller you should add it to one parent controller in a method annotated with #Before. This will get called on each page so you only need to do the code once.
Eg. The parent controller (aka interceptor) would look like:
public class ControllerInterceptor extends Controller {
#Before
public static void intercept() {
RenderArgs.current().put("buildings", Building.findAll());
}
}
Then each controller would add the following annotation:
#With(ControllerInterceptor.class)
public class MyController extends Controller {
...
}
And your page code would then refer to it much as you're already doing:
<ul>
#{list buildings, as: 'building'}
<li>#{a #Buildings.edit(building.code)}${building}#{/a}</li>
#{/list}
</ul>
As for why your original code didn't work, I'm not sure. Possibly something to do with how the Model class is enhanced by Play?

Discovered how to work around it, but I'd still be interested to know what was wrong with the original code. I got it working by just doing
<ul>
#{list items: models.Building.findAll(), as: 'building'}
<li>#{a #Buildings.edit(building.code)}${building}#{/a}</li>
#{/list}
</ul>

Related

How to include Service-Menu in Sidebar Navigation?

I've tried to include the service-menu-block into the twig file of the sidebar, which is loaded in category-pages, but for some reason the this doesn't work. its the same code, thats works in the footer section. If I write some static html in there it shows, but but the pages from the menu aren't listed. could somebody help out?
This is the code from the service menu:
{% sw_include '#Storefront/storefront/layout/navigation/my-service-menu.html.twig'%}
The footer navigation content, i assume thats the content you want to display inside your category page sidebar, is only loaded during the FooterPageletLoader and added during the GenericPageLoader to the page.footer variable for twig.
You can test that by adding {{ dump(page.footer) }} to the template where you wanted to include the service menu. If the dump is empty, your current page is not using the GenericPageLoader or does not have access to the data. If the dump is not empty, the required data for the template might need to be passed differently. For example:
{% sw_include '#Storefront/storefront/layout/navigation/my-service-menu.html.twig'' with {
data: page.test
} %}
Shopwares GenericPageLoader can be injected if its missing from your Controller/PageLoader. In my example, MyPageStruct extends Struct and provides just one function.
public function __construct(
private readonly GenericPageLoaderInterface $genericLoader
) { }
public function load(Request $request, SalesChannelContext $salesChannelContext): MyPageStruct
{
$page = $this->genericLoader->load($request, $salesChannelContext);
$page = MyPageStruct::createFrom($page);
$page->setDataFunction();
return $page;
}
Complete examples can be found in shopwares core code: Shopware\Storefront\Page\Account\Login\AccountLoginPageLoader

Common logic accessible from any twig template

In SF3, I would like to know how to perform some logic that would be common to any page in my website.
FOSUserBundle, global arguments / function before templating is a begin of answer, but it talks about SF2, and i'm not sure if the OP is really expecting the same thing as me.
For the moment, I defined a custom base class (BasePageController) from which any controller inherits in my project.
The common logic is written there, and each controller calls a common method to retrieve
some custom variables to send to the twig templates.
Here is how it looks (I tried to make the smallest example as possible):
My base controller class:
<?php
// src/AppBundle/Controller/BasePageController.php
namespace AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class BasePageController extends Controller
{
public function getCommonParams()
{
// This assignement may be replaced by a complex logic,
// evolving user-specific data, session variables... :
$val = 1234;
return [ 'important_val' => $val ];
}
}
A controller example which inherits from my base class:
<?php
// src/AppBundle/Controller/HomeController.php
namespace AppBundle\Controller;
use AppBundle\Controller\BasePageController;
class HomeController extends BasePageController
{
public function homeAction()
{
$params = $this->getCommonParams();
return $this->render('home.html.twig', $params);
}
}
A basic base template:
{# app/Resources/views/base.html.twig #}
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>SO example</title>
</head>
<body>
{% block body %}{% endblock %}
</body>
</html>
The template used by my controller example:
{# app/Resources/views/home.html.twig #}
{% extends 'base.html.twig' %}
{% block body %}
<h1>Welcome</h1>
<p>
Important value : {{ important_val }}
</P>
{% endblock %}
I'm not really sure if it's the common way to handle common logic in a SF website (advices are welcome about it).
But what is certain, is that problems start to appear when I try to use FOSUserBundle over this scheme.
Indeed, every controller inside the FOSUserBundle obviously doesn't inherit from my custom
controller class. So, even if redefine the FOSUserBundle templates, my common logic will
not be accessible from the login/register/... pages.
The only kinds of workarounds I can imagine now are:
Include a whole controller with {% render %} (inherited from my custom class) inside the templates, where
the common logic would be accessible; but the common logic would still be unavailable inside
the main template (i.e. inside login.html.twig...);
redefine all FOSUserBundle controllers by replacing their standard inheritance (Controller)
by my own custom controller class; but i'm sure it's the worst idea of the year (I would
loose all the flexibility offered by the composer upgrade system).
There might be a perfect solution if there was a magical twig tag that would import variables
from a PHP method, something like this:
{# app/Resources/views/base.html.twig #}
{% import_my_variables_from('AppBundle:BasePageController:getCommonParams()') %}
<!doctype html>
<html>
....
So how is it possible to make some common logic accessible, even for external bundles like FOSUserBundle?
I am always a bit skeptical over the notion that you really need common parameters for every single page. The requirement often goes away as the application design is refined.
But assuming you really need this then move the common parameter functionality to it's own service.
class CommonParameters {
public function getParams() {
return [whatever];
Wire it up as a service: http://symfony.com/doc/current/book/service_container.html
services:
common_parameters:
class: AppBundle\Common\CommonParameters
From inside a controller, access the parameters with:
$params = $this->get('common_parameters')->getParams();
You could even define a trait to add this to your controllers and get rid of your custom base controller classes which happens to be another one of those ideas that sound really good but in practice often turn out to more painful that they are worth.
But what about twig? This is where a twig extension can come in: http://symfony.com/doc/current/cookbook/templating/twig_extension.html. You can inject your parameter service into your twig extension and twig will have access to these variables without involving controllers at all. Plug in your own FOSUserBundle templates and off you go.
And of course, you can also inject this stuff into whatever other services might need it. Think services. Not globals.
Final note: Symfony is a secular framework. Magical thinking won't get you very far.

Waiting for template render in Meteor, inconsistent

Been scratching my head on this, so here's the simplest way to view it - I'm using this:
Template.today.rendered = function() {
console.log(this.firstNode.children.length);
};
Simply to try and get the count of items that are supposedly rendered. The template looks like:
<template name="today">
<div class="todaySlider">
{{#each dayOfWeek}}
{{> singleDay}}
{{/each}}
</div>
</template>
and if it's of any importance, singleDay looks like:
<template name="singleDay">
<div class="day {{isCurrent}}">
<h2 class="date">{{date}}</h2>
{{#each items}}
{{> item }}
{{/each}}
</div>
</template>
I'm trying to wait for all the "singleDays" to render, however that count I'm logging is usually different on refresh. I'll get anything from 0 to the correct value, and I don't understand why. This seems to be the right place to call it, I fear that maybe the double "each" is too slow?
I've tried timers (which I honestly shouldn't) and even DOM Mutation Observers (which seem like overkill) but surely there is a pure Meteor approach to this, any ideas?
Template.rendered happens when the template is rendered, but that doesn't mean there'll be any data in it.
I'm pretty sure you'll need to do this inside a helper.
each helpers don't have to return cursors, they can also return an array. If the number of "singleDays" is short, you could send an array to the template instead of a cursor. It's kind of ugly, and there might be a better way to do this, but I think this will work.
Template.today.helpers({
dayOfWeek: function() {
var days = DaysCollection.find({}).fetch();
if (days[days.length - 1]) days[days.length - 1].isLast = true;
return days;
}
});
I assume {{isCurrent}} is where you add the extra class that you're talking about. If so, just have the isCurrent helper look for this.isLast to be true.
Seems like what is happening here is that the template is being rendered before the Mongo collection is sent to the client. To be more specific, meteor renders your template as fast as possible, which means that it has no concept of 'waiting' for any data to be sent from the server to the client. Therefore, if you place anything indirectly regarding database queries inside of a non-reactive call (Template.rendered), then it will execute with the data as undefined.
I'm assuming your dayOfWeek helper looks something like this:
Template.today.helpers({
daysOfWeek: function () {
var today = CollectionName.findOne();
return today.daysOfWeek;
}
})
(Or maybe you are using the router to pass the day directly to the template)
Either way, within your router you need to wait for the Mongo collection item to be sent to the client before any rendering takes place. If you are using Iron-Router, you simply have to 'wait' for your data/subscription.
More information can be found here: https://github.com/iron-meteor/iron-router/blob/devel/Guide.md#wait-and-ready
If you are still using autopublish, then you can replace the subscription with your database query.

How to create a directory-like structure with meteor templates?

Consider this mockup of my intended output:
The data structure behind this in a Mongo DB looks like this - I did not nest the subcategories inside the document because I still want to be able to update subdocuments atomically - and I also want to allow a dynamic amount of layers below. From what I've seen, Mongo currently does not allow for easy, dynamic access of nested documents.
topmost_category = {
_id : "foo",
category_name : "Example",
parent_category: null,
subcatories: [ "sub_foo1", "sub_foo2", ... ]
}
child_category = {
_id = "sub_foo1",
category_name : "Example 2",
parent_category: "foo",
subcategories: []
}
The underlying HTML consists simply of nested -branches. The currently selected category gets an "active" class, and the icons in front are named "icon-folder-close" and "icon-folder-open" (<i class="icon-folder-close"></i>).
Now, I could use the answer to this to create a complete tree-like structure. However, all the branches would be "open". How do I make it so that only the currently selected branch is visible like shown in my mockup (and still have it reactive to boot)?
You can see the implementation of a very similar approach to that described by Hubert here:
http://www.meteorpedia.com/read/Trees
It's the code to the (working) category browser functionality of the wiki (you can see a demo there).
The main difference to Hubert's answer is that it uses a publish/subscribe with a session variable to fetch the data of opened children in real time.
The easiest way would be to add a boolean flag for branches denoting whether or not they belong to current path, and therefore should be open.
<template name="branch">
{{#if open}}
<div class="openBranch">
...
{{#each children}}
...
{{/each
</div>
{{else}}
<div class="closedBranch">
...
</div>
{{/if}}
</template>
Now, the setting and management of this flag depends on the logic of your application. Where do you store the currently selected branch?
I assume the state is limited to a single client. In this case, one solution is to:
maintain an array of opened branches ids, scoped to a single file,
implement the open flag as a helper:
Template.branch.open = function() {
return _.indexOf(openedBranches, this._id) !== -1;
}
when user changes selection, clear the array and populate it with path from the selected branch to the top.

Inject variable from the views to my angular controller?

I'm trying to create a website with articles. I have a page that displays a list of all the articles, and I try to do one that displays the detail of an article. I use angular.js to get the json of my datas. I don't have any problem to get the list of my articles since I only need to do :
function ArticleListCtrl($scope, $http) {
$http.get('/articles/?format=json').success(function(data) {
$scope.articles = data;
});
}
But now I only want to access for example the article with id 4. How do I do that ? Is there a way to inject the primary key entered in the url into the javascript ? I'm new to angular, and I'm pretty sure there is an easy way!
If I understand correctly, what you need is to define a partial template and a route,
for the detail view of your article.
An example is here
More specifically what you'd need is something like the following.
angular.module('myapp', []).
config(['$routeProvider', function($routeProvider) {
$routeProvider.
when('/articles/:articleId', {templateUrl: 'partials/article-detail.html', controller: ArticleDetailCtrl}).
otherwise({redirectTo: '/articles'});
}]);
What the above does, is to hijack urls of the form /articles/5/ and instead of actually
performing that GET request to your server, it will just ask for partials/article-detail.html. This of course will be your article detail template, which will be handled
by your ArticleDetailCrtl controller.
In your ArticleDetailCrtl controller function, don't forget to include the $routeParams
service. This will give you access to the url parameters, such as articleId, which we
defined above.
The final thing to do is to generate these links in your article list template. e.g:
<ul>
<li ng-repeat="article in articles">{{article.title}}
</ul>