Move if/then logic into view or model or keep in template? - backbone.js

after creating a web app using backbone for an engine design firm, I'm wondering if I should move the "if/then" logic out of the html templates.
To help clarify what I mean, here are two examples that I am currently using in production.
If I move the if/then logic out of the template, I would move it to the view, but I'm not sure if that's the "right" way or "backbone" way of doing things.
Am I making poor design decisions, or is what I've done OK?
Thanks!
Simple Example 1:
In the view:
//m is the model used by the view
return Backbone.View.extend({
template: _.template(tmpl, null, { variable: 'm' }),
In the template:
{% if(m.title) { %}
<h4> {%- m.title %} </h4>
{% } else { %}
<h4>Experiment Title Goes Here</h4>
{% } %}
Complex Example 2:
In the view:
//args are the model attributes passed into the view
initialize: function (args) {
this.currentEngine = args.currentEngine;
this.engineDisplay = args.engineDisplay;
this.engineType = args.engineType;
this.isCurrent = this.model.isCurrent(this.currentEngine);
},
render: function () {
this.$el.html(this.template({
engineDisplay: this.engineDisplay,
engineType: this.engineType,
isCurrent: this.isCurrent;
}));
In the template:
{% if(!engineDisplay) { %}
{% if (m.isCurrent && (engineType === 'GAS' || engineType === 'ECO')) { %}
<span>Not Available</span>
{% } else { %}
<span>
<span>Click here to select</span>
</span>
{% } %}
{% } %}

I think your first implementation was correct. Keep the logic out of the view. The "correct" way, or the "backbone" way is to keep the logic where it needs to be:
the model/collection houses code of "where" the data needs to come from.
the view houses code of "what" it needs to do/display. (what needs to happen if event X happens)
the template should house the code of "how" it needs to be displayed.
I'm sure im missing stuff.. i'll wait until the comments tells me how wrong i am and then i'll correct it.
-Sheers

Related

In Twig: Check if array element exists. If not set this one element

In Twig, I pass an array params to a macro. Within the macro I check if all required elements (here: required_element) of params are set. If not, I want to set that to a default value.
This code is working, but I think there must be a better way:
{% macro my_macro(params) %}
{% if not params.required_element is defined %}
{% set params = params|merge({'required_element: 'value'}) %}
{% endif %}
I thought of something shorter like {% params.required_element ?: value %}, but that does not work.
For various reasons I do not want pass them one by one as parameters to the macro, so this is not an alternative:
{% macro my_macro(required_element, another_element, yet_another, puh_another, next_element) %}
Nope, that is the only (vanilla) way. If you want to make it shorter u'd need to extend twig with something like this:
<?php
$twig = new \Twig\Environment($loader);
$function = new \Twig\TwigFunction('set_array_value', function (&$context, $array_name, $index, $value) {
if (!isset($context[$array_name])) return;
/**
- If value is array just do arr[index] = value
- Otherwise test if object
-> test if method $index exists
-> test if there is a setter for the property $index
**/
if (is_array($context[$array_name])) $context[$array_name][$index] = $value;
elseif(is_object($context[$array_name])) {
if (method_exists($context[$array_name], $index)) $context[$array_name]->{$index}($value);
elseif(method_exists($context[$array_name], 'set'.$index)) $context[$array_name]->{'set'.$index}($value);
}
}, ['needs_context' => true, ]);
$twig->addFunction($function);
{% macro foo(my_array) %}
{% if not array.foo is defined %}{% do set_array_value('my_array', 'foo', 'bar') %}{% endif %}
{{ my_array.foo }}
{% endmacro %}
{% import _self as sandbox %}
{{ sandbox.foo({}) }} #output: bar
{{ sandbox.foo({'foo': 42,}) }} #output: 42

Change wagtail admin form fields based on user input

I've got a model called 'Block', which has multiple fields:
type. This is a dropdown where you can select 3 options.
URL
Search
Based on the type field, the URL or Search must be shown.
The search field needs to preform a search using a variable: API_HOST.
I've written JS to do make the form dynamic and I've extended the wagtailadmin/pages/edit.html template to inject the JS.
However, I'm not sure how to pass the API_HOST (defined in dev.py) down to the template.
dev.py
API_HOST = "http://localhost:8000/"
wagtailadmin/pages/edit.html:
{% extends "wagtailadmin/pages/edit.html" %}
{% block extra_js %}
{{ block.super }}
<script>
var api_host = "{{api_url}}";
console.log("api_host:", api_host);
</script>
<script src="/static/js/blocks_hook.js">
</script>
{% endblock %}
blocks_hook.js
$(function () {
var $type = $('#id_type');
var $search = $('#id_search');
var $url = $('#id_url');
var api = new API(api_host);
hide($search);
hide($url);
$type.on('change', function(){
if ($type.val() == 'search') {
show($search);
hide($url);
api.getProduct(function(product){
// .. do something with the product
});
} else if($type.val() == 'url') {
show($url);
hide($search);
}
});
How should I approach this situation?
I managed to fix this using the insert_editor_js hook mechanism, provided by Wagtail.
Make sure the variable you're passing in the method (#hooks.register) is actually present in your configuration file or else the hooks system crashes without any actionable debug information.

Twig: Append to array inside array/object

I have an object which, in JSON, would look like this:
{
'class': ['nav-link', 'dropdown-toggle'],
'data-toggle': ['dropdown']
}
I need to then be able to append another class to the class array inside the object.
This code doesn't seem to work; it just overwrites the class array.
{% set link_attribs = { 'class' : ['nav-link', 'dropdown-toggle'], 'data-toggle':'dropdown'} %}
{% set link_attribs = link_attribs|merge({'class': ['highlighted']}) %}
Really I want to do something like this, but it just throws a punctuation error.
{% set link_attribs.class = link_attribs.class|merge(['highlighted']) %}
Any ideas?
Using Twig, you can't set object properties directly, so "set (...).class" will never work. But instead, you can create a new variable that will inherit from both default and options values (just like in most JavaScript codes).
For example:
{%
set options = link_attribs | merge({
'class': link_attribs.class | merge(['highlighted'])
})
%}
{% for class in options.class %}
{{ class }}
{% endfor %}
Will display:
nav-link
dropdown-toggle
highlighted
See fiddle.
This looks like it works:
{% set c = link_attribs.class %}
{% set c = c|merge(['highlighted']) %}
{% set link_attribs = link_attribs|merge({'class': c}) %}
Not sure if its the most elegant way though.

How to display multiple data sets in Symfony2

I am totally new to Symfony and i d like to know how I manage to display multiple data sets from my db in a twig...
untill now my attempt is the following:
class ProjController extends Controller
{
/**
* #Route("/", name="_projekte")
* #Template("FHBingenBundle:projekte:list.html.twig")
*
*/
public function indexAction()
{
$projekte = $this->getDoctrine()
->getRepository('FHBingenBundle:Projekt')->findAll();
return $projekte;
}
}
to get all the data sets. Here is where my Problem starts... how do I extract the data from the array? (the entity has multiple columns but i only want two of them, name and description)
{% extends "FHBingenBundle::layout.html.twig" %}
{% block content %}
<table>
<?php foreach ($liste as $projekt ?>
<tr><p>{{ $projekt->getName() }}</p></tr>
<?php endforeach;?>
</table>
{% endblock %}
thats how I tried to do it but apperently I am not allowed to use $ inside of {{}}? atleast thats what the error says
You should consider reading the cookbook.
Since you are using twig, think about using twig templating system.
{% for item in navigation %} // equivalent to foreach($navigation as $item) {
{{ item.name }} // equivalent to $item->name or $item->getName() or $item->hasName()
{% endfor %} // equivalent to }
EDIT : I don't really remember but it seems that you have to return an array for the twig templating system. return array('projects' => $projects);

Change meta page titles on Big Cartel dynamically

I have a custom theme in Big Cartel and I need to create an if statement using BC's own tags that looks at the page URL and then displays a tag.
For example, if url is homepage show this tag, if it is product page a, show this tag and so on...
Anyone have any idea how I can write that?
At the moment the theme has this:
<title>{{ page.name | remove: '-footer-' | remove: '-hide-' | remove: '-sidebar-' | remove: '-subnav-' | remove: '-f1-' | remove: '-f2-' | remove: '-f3-' }} | {{ store.name }}</title>
There are a number of different conditions you could use for this, like checking the page name, page permalink, the full URL, or others. A full variable reference is available here: http://help.bigcartel.com/customer/portal/articles/772750-variables#page
Here's a couple examples:
{% if page.name == 'Home' %}code here {% endif %}
{% if page.permalink == 'home' %}
code here
{% elsif page.permalink == 'product' %}
more code here
{% endif %}

Resources