V9: how to reuse chunks of code: elements and views?

I have a pretty big chunk of code in a block view.php which has to be displayed with some variations based on certain conditions. Instead of dumping 5 nearly the same chunks with hundreds of lines of code in one file, how can I break it into either elements or views?

I can see 2 folders in the concrete core: elements and views. They have some code. But I can’t find it anywhere how to use all those pieces. How do I stick either an element or a view into my block’s view.php into a ‘< div >’?

What’s the difference between Element and View? Is there any example or documentation on using these two?

Elements are just “chunks” of bigger code, whatever you call it in your preferred framework: elements/partials/includes.
By glancing a “concrete/views” folder it seems it contains files that are mostly responses from ajax (for example dialog boxes). They are not just only “parts” of some php file/view but whole “response” since the difference.

In your block view you can use:

<?php $this->inc('elements/your_file.php'); ?>

and if you want to pass variable:

<?php $this->inc('elements/your_file.php', ['testVariable' => $testVariable]); ?>

Code above is just kind of a wrapper for good old php include:

<?php include('elements/your_file.php'); ?>

Also you don’t have to name folder inside block “elements” (I think it’s good naming convention, but it has nothing to do with “elements” folder outside your block).

“elements” folder comes into play when you are using:

View::element('file_inside_any_elements_folder');

because it will search for file inside “elements” folder in various places (concrete/elements, application/elements, theme/elements, packages/your_package/elements). It will not search inside your block folders though.

For files in “views” folder, you would usually want to register custom route first (for example in bootstrap/app.php) and render your view in controller. This way you can display it in browser by typing url or use in ajax request.

Personally I would only put something in “application/elements” if it was used across many places or if it was some kind of dashboard functionality.
For example I have used “application/views” folder when making template for auto-generating pdf.
But most of the time I like to keep things in one place, be it in block, package or theme folder.

@Parasek , thank you for your reply.

I forgot to mention the block is part of my package. While ‘include’ works in general, one of the Concrete CMS Marketplace submission rules is a package “Doesn’t make use of include statements”.

Is there a core block which uses Elements?

Yeah, that’s why you want to use $this->inc(‘form.php’) instead include(). Many blocks in core use that function (including form.php in edit.php).

And also I don’t think any core block is big enough that is divided into smaller elements/partials.

I thought “Doesn’t make use of include statements” applies to ‘$this->inc(‘form.php’)’ too.

So do I just do this:

<div><?php ```View::element('file_inside_any_elements_folder'); ?></div>

Does an Element need its own controller or do I pass all php variables to the element from the block controller?

Yes, that rule only applies to php function include().

Use

<?php $this->inc('elements/your_file.php'); ?>

so you can just provide file path relative to current block directory.

And if you really want to use View::element() (for example some code is shared between multiple blocks in your package), then you can:

  1. Create folder “packages/your_package_name/elements”
  2. Create file inside that folder, for example test.php
  3. Load element in view
<?php View::element('test', null, 'your_package_handle'); ?>

View::element() nor $this->inc() don’t need controller. You can always pass variables as second argument in both functions, if they are not available in your included element.

@Parasek , thank you very much!

To avoid setting view variables in the block controller first, then creating an array of data and then passing it to the View::element, I’m trying to set the variables in an ElementController.

And the very first problem is the $app is not set by this:

class AllPosts extends ElementController
{
    public function getElement()
    {
        return 'all_posts';
    }

    public function on_start()
    {
        parent::on_start();
        $this->set('app', $this->app);
    }

I get a “Call to a member function make() on null” when I use the $app. In fact it doesn’t set any variable for the element view, all those variables are null.

What’s wrong here?

If you are using View::element() I think you have to explicitly pass every variable ($app too) in array as second argument.
Use $this->inc() instead, which passes most of the variables automatically.