Twig custom function with parameters - twig

I read twig documentation, but I am little confused about custom functions and filters. I understand how to add custom functions. But I don't understand how to write a function that accepts some parameters, may be also some optional parameters.
For example, I have following pseudo code for function named sqare.
$twig = new Twig_Environment($loader);
$function = new Twig_SimpleFunction('square', function () {
if param2 present?
return param1*param2;
else
return param1;
});
$twig->addFunction($function);
Now what I want is that, param1 should have a default value 1 and param2 should be optional. The square function will return the product of the two parameters. I also want that if user do not pass the second parameter then param1 will be returned, that is the first parameter will be returned. How can I implement this? Also, should I call the function in the twig template as {{ square(5, 10) }}?

You need to define the parameters in your closure.
Twig will pass the parameters accordingly
$function = new Twig_SimpleFunction('square', function ($param1, $param2 = null) {
return isset($param2) ? $param1 * $param2 : $param1;
});
Then you call this function in Twig with :
Only one param : {{ square(5) }}
Two params : {{ square(5, 2) }}

Related

Call to a member function createView() on null

I am using symfony 5, within my Controller I have this :
$devisFrais = $devisRepository->getDevisPosteFDFrais($idDevis);
$formFrais = $this->createForm(DevisDisciplineQualifType::class, $devisFrais, [
'AT' => $AtNotAt,
'type' => 'FRAIS'
]);
return $this->render('devis.html.twig', [
'formFrais' => $formFrais->createView(),
]);
As $devisFrais is Null so formFrais is Null.
I can't create a form because I have this error : "Call to a member function createView() on null".
Is it possible to create a blank form, in this case how to manage it within the Twig
You are getting an error that your variable $formFrais is null.
What you need to do is to simply check your value for being null / empty,
with a ternary operator, and if it is give it is, execute the method, and if not, implement whatever logic you need after the ":".
You can do it in the Controller, no need to do any manipulation at the level of twig.
The basic logic is:
//...
return $this->render('devis.html.twig', [
'formFrais' => $formFrais ? $formFrais->createView() : $formFrais = "some_value",
]);

Sending variable from volt to custom function

I have created a custom function I can access from the volt. The function seems to work fine, but I cannot manage to send the variable to the function. It sends the variable as text instead of its value.
The twig function:
$volt->getCompiler()->addFunction('getusergroup', function ($user) {
return \Models\User::getUserGroup($user);
});
The function in the Model:
public static function getUserGroup($user) {
return UserGroup::find(array('conditions' => 'user_id = ' . $user));
}
The lines in Twig to call the function:
{% for member in getusergroup(staff.id) %}
{{ member.Group.name }}
{% endfor %}
The error I get:
'Scanning error before 'staff->id' when parsing: SELECT
[Models\UserGroup].* FROM [Models\UserGroup] WHERE user_id =
$staff->id (78)' (length=131)
As you can see, in stead of $staff->id being an integer, it's the text.
How do I go about sending the actual ID to the function?
By the way, I am using twig in combination with Phalcon and followed the instructions in this article: http://phalcontip.com/discussion/60/extending-volt-functions
If you dump $user inside of your method public static function getUserGroup($user), you will receive this $staff->id, but you actually want something like 42.
To avoid this register the Volt function like this:
$volt->getCompiler()->addFunction('getusergroup', function ($resolvedArgs, $exprArgs) {
return 'Models\User::getUserGroup(' . $resolvedArgs . ')';
});
More info on Extending Volt Functions

Is it possible to log variable name in JavaScript?

Is it possible to log variable name (not value) in JavaScript?
var max_value = 4;
console.log(max_value); // should log "max_value" as a string
UPDATE: I need a testing function that should be able to log any variable name (passed as an argument) as a string, not just this one variable.
There is a solution that can help you. I grabbed this function from this stackoverflow answer, which is able to get the name of the function parameters:
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
var ARGUMENT_NAMES = /([^\s,]+)/g;
function getParamNames(func) {
var fnStr = func.toString().replace(STRIP_COMMENTS, '');
var result = fnStr.slice(fnStr.indexOf('(')+1, fnStr.indexOf(')')).match(ARGUMENT_NAMES);
if(result === null)
result = [];
return result;
}
then all you need to do now, is to use the name of your variable as a parameter of an anonymous function and pass all the function as argument of the getParamNames :
variablesNames = getParamNames(function (max_value, min_value) {});
This will return an array like this :
result => ["max_value", "min_value"];
Let's make it practical, first change the name of the getParamNames function to something easy and small like this :
function __ (func) {
// code here ...
}
second thing, instead of returning an array, just return the first element of the array, change this :
return result;
to this :
return result.shift();
now, you can get the name of your variable like this :
__(function( max_value ){});

Twig variables in twig variable

I have a twig variable html. To show it in a twig template I do {{html}}.
That variable looks like:
<div>{{region_top}}</div><div>{{region_center}}</div>
region_* is a variable too. When Twig parses my html variable, it doesn't parse the inner variables (regions).
What I should do?
I have twig variable html. To show it in twig template I do {{html}}. That variable look like {{region_top}}{{region_center}}. region_* is variables too. When twig parse my html variable he didn't parse inner variables (regions). What can I should do?
Twig takes your strings as a literal string, meaning you'll see the content of the variable, escaped. If you want it to be able to display {{region_top}} as well, I'd recommend something like this:
{{html|replace({'{{region_top}}': region_top, '{{region_center}}': region_center})}}
If the content of your html variable is also dynamic (meaning it can contain more than just those two variables), I'd write a twig plugin which can do what you want. Writing plugins is pretty easy to do.
EDIT: Here's the extension I just finished writing.
EDIT 2: The extension now uses the environment to render the string, so it evaluates the string, instead of just replacing variables. This means your variable can contain anything a template can, and it will be render and escaped by Twig itself. I'm awesome.
<?php
/**
* A twig extension that will add an "evaluate" filter, for dynamic evaluation.
*/
class EvaluateExtension extends \Twig_Extension {
/**
* Attaches the innervars filter to the Twig Environment.
*
* #return array
*/
public function getFilters( ) {
return array(
'evaluate' => new \Twig_Filter_Method( $this, 'evaluate', array(
'needs_environment' => true,
'needs_context' => true,
'is_safe' => array(
'evaluate' => true
)
))
);
}
/**
* This function will evaluate $string through the $environment, and return its results.
*
* #param array $context
* #param string $string
*/
public function evaluate( \Twig_Environment $environment, $context, $string ) {
$loader = $environment->getLoader( );
$parsed = $this->parseString( $environment, $context, $string );
$environment->setLoader( $loader );
return $parsed;
}
/**
* Sets the parser for the environment to Twig_Loader_String, and parsed the string $string.
*
* #param \Twig_Environment $environment
* #param array $context
* #param string $string
* #return string
*/
protected function parseString( \Twig_Environment $environment, $context, $string ) {
$environment->setLoader( new \Twig_Loader_String( ) );
return $environment->render( $string, $context );
}
/**
* Returns the name of this extension.
*
* #return string
*/
public function getName( ) {
return 'evaluate';
}
}
Example usage:
$twig_environment->addExtension( new EvaluateExtension( ) );
In the template:
{% set var = 'inner variable' %}
{{'this is a string with an {{var}}'|evaluate}}
See http://twig.sensiolabs.org/doc/functions/template_from_string.html
It seems that this is frequently missed as most folks think (and search for) "eval" when expecting a filter/function to evaluate in the current language they're drafting in. Template from string isn't the first search query that comes to mind.
One option is to render your templates as strings. You can do that like this:
$env = new \Twig_Environment(new \Twig_Loader_String());
echo $env->render(
"Hello {{ name }}",
array("name" => "World")
);
I'll leave it to you to decide how exactly to structure your code to make this work, but it might go something like this:
1) Fetch the inner template text that contains the variables that aren't being replaced.
2) Render that inner template text into an $html variable. Be sure to pass in any vars you need.
3) Render your original template that contains {{html}}. Be sure to pass in 'html' => $html in the vars array
You can also pass an array or an object to the view, and then use the twig attribute() method: http://twig.sensiolabs.org/doc/functions/attribute.html
{% if attribute(array, key) is defined %}
{{ attribute(array, key) }}
{% endif %}

How to pass variable parameters to an XPages SSJS function?

If I have a function in SSJS and I want to pass one "firm" parameter and a list of others that can change, what's the best way to do that? With some kind of hashMap or JSON or something else?
for example given something like:
myfunction( code:string, paramList:??) {
// do stuff here
}
Basically the function will create a document. And sometimes I'll have certain fields I'll want to pass in right away and populate and other times I'll have different fields I will want to populate.
How would you pass them in and then parse out in the function?
Thanks!
Use the arguments parameter... In JavaScript you are not required to define any of your parameters in the function block itself. So, for example, the following call:
myFunction(arg1, arg2, arg3, arg4);
can legally be passed to the following function:
myFunction () {
// do stuff here...
}
when I do this, I usually place a comment in the parens to indicate I am expecting variable arguments:
myFunction (/* I am expecting variable arguments to be passed here */) {
// do stuff here...
}
Then, you can access those arguments like this:
myFunction (/* I am expecting variable arguments to be passed here */) {
if (arguments.length == 0) {
// naughty naughty, you were supposed to send me things...
return null;
}
myExpectedFirstArgument = arguments[0];
// maybe do something here with myExpectedFirstArgument
var whatEvah:String = myExpectedFirstArgument + ": "
for (i=1;i<arguments.length;i++) {
// now do something with the rest of the arguments, one
// at a time using arguments[i]
whatEvah = whatEvah + " and " + arguments[i];
}
// peace.
return whatEvah;
}
Wallah, variable arguments.
But, more to the point of your question, I don't think you need to actually send variable arguments, nor go through the hassle of creating actual JSON (which is really a string interpretation of a javascript object), just create and send the actual object then reference as an associative array to get your field names and field values:
var x = {};
x.fieldName1 = value1;
x.fieldName2 = value2;
// ... etc ...
then in your function, which now needs only two parameters:
myFunction(arg1, arg2) {
// do whatever with arg1
for (name in arg2) {
// name is now "fieldName1" or "fieldName2"
alert(name + ": " + x[name]);
}
}
Hope this helps.
I would do this with a JSON object as the second parameter...
function myfunction(code:String, data) {
// do stuff here...
var doc:NotesDocument = database.CreateDocument();
if(data) {
for (x in data) {
doc.replaceItemValue(x, data[x]);
}
}
// do more stuff
doc.save(true, false);
}
Then you call the function like this:
nyfunction("somecode", {form:"SomeForm", subject:"Whatever",uname:#UserName()});
Happy coding.
/Newbs
I don't think that is possible in SSJS. I think the best option you have is to pass a hashmap or your own (java) object. I think a custom java object would be the nicest option because you can define some 'structure' on how your function can process it. A hashmap can be easily extended but it is not easy if you have a lot of code that create a lot of different hashmap structures...

Resources