Combining Smarty and Vue.js in EMPS Templates

EMPS allows you to combine server-side Smarty templating with client-side Vue.js directives seamlessly in a single HTML template. This integration is achieved by using the same delimiters—{{ and }}—for both Smarty and Vue.js. However, EMPS leverages a subtle behavior in Smarty to differentiate between the two.


How It Works

Smarty vs. Vue.js Delimiters

  • Smarty's Delimiter Behavior:
    By default, Smarty uses {{ and }} to mark its tags. However, Smarty only treats these delimiters as tags if the opening {{ is immediately followed by a non-space character.

    • Smarty Tag Example:

      {{$user.name}}

      This is processed by Smarty and evaluated on the server.

    • Vue.js Expression Example:

      {{ user.name }}

      Here, the space after {{ signals Smarty to ignore this tag, allowing it to pass through unchanged to the client. Vue.js will then process it as a reactive literal expression.

  • Smarty Comments:
    Smarty provides its own commenting mechanism using the syntax {{* comment *}}. This is especially useful because HTML comment tags (<!-- -->) would be visible in the page source. With Smarty comments, you can add non-displaying notes in your template.

Processing Order

  1. Server-Side Evaluation:
    When a template is processed on the server, all Smarty tags (without a leading space) are evaluated first. This means that any dynamic content that needs to be rendered by PHP is resolved before the template is sent to the client.

  2. Client-Side Evaluation:
    Once the Smarty engine completes its work, the resulting HTML—which still contains Vue.js expressions—is sent to the client. At that point, Vue.js takes over and processes any remaining directives or expressions (which were left untouched due to the space after the opening delimiters).


Real-Life Examples

Pagination Component Template

The following example shows how you can mix Smarty assignments with Vue.js directives. In this pagination component template, Smarty is used to set default values, while Vue.js handles the dynamic behavior for pagination:

{{if !$pages_var}}
    {{assign var="pages_var" value="pages"}}
{{/if}}
{{if !$roll_to}}
    {{assign var="roll_to" value="roll_to"}}
{{/if}}

<nav class="field pagination" role="navigation" aria-label="pagination" v-if="{{$pages_var}}.first !== undefined">
    <a class="pagination-previous" @click="{{$roll_to}}({{$pages_var}}.prev)">
        <i class="fa fa-chevron-left"></i>
    </a>
    <a class="pagination-next" @click="{{$roll_to}}({{$pages_var}}.next)">
        <i class="fa fa-chevron-right"></i>
    </a>
    <ul class="pagination-list">
        <li v-if="{{$pages_var}}.pl.length > 0 && {{$pages_var}}.first.page < {{$pages_var}}.pl[0].page">
            <a class="pagination-link" @click="{{$roll_to}}({{$pages_var}}.first)" v-html="{{$pages_var}}.first.page"></a>
        </li>
        <li v-if="{{$pages_var}}.pl.length > 0 && ({{$pages_var}}.pl[0].page - {{$pages_var}}.first.page) > 1">
            <span class="pagination-ellipsis">&hellip;</span>
        </li>
        <li v-for="(p, pidx) in {{$pages_var}}.pl">
            <a :class="['pagination-link', {'is-current': p.sel}]"
               @click="{{$roll_to}}(p)">{{ p.page }}</a>
        </li>
        <li v-if="{{$pages_var}}.pl.length > 0 && ({{$pages_var}}.pl[{{$pages_var}}.count - 1].page - {{$pages_var}}.last.page) < -1">
            <span class="pagination-ellipsis">&hellip;</span>
        </li>
        <li v-if="{{$pages_var}}.pl.length > 0 && {{$pages_var}}.last.page > {{$pages_var}}.pl[{{$pages_var}}.count - 1].page">
            <a class="pagination-link" @click="{{$roll_to}}({{$pages_var}}.last)" v-html="{{$pages_var}}.last.page"></a>
        </li>
    </ul>
</nav>

In this example:

  • Smarty Part:
    Smarty conditionals and assignments (e.g., {{if !$pages_var}} and {{assign var="pages_var" value="pages"}}) run on the server to set default values.
  • Vue.js Part:
    Vue.js expressions (e.g., {{ p.page }}) are preserved because of the deliberate use of spaces in the opening delimiters when needed. These expressions become reactive when the Vue app initializes on the client.

Including Other Templates

EMPS also allows you to split templates into smaller, reusable parts using the Smarty {{include ...}} syntax. For instance:

<div class="column is-5-tablet is-3-desktop"
     @click="toolbar_click"
     v-if="rt == 'info'">
    {{include file="db:_comp/editor/props,info"}}
</div>

Here, the {{include ...}} statement is processed by Smarty to insert the specified template before the HTML is delivered to the client. Once inserted, Vue.js can further process any Vue-specific directives or expressions within that template.

More on Internal References →


Summary

EMPS leverages the similarities between Smarty and Vue.js delimiters to create a powerful templating system where server-side and client-side rendering coexist seamlessly. By ensuring that Smarty only processes tags without a leading space, you can embed Vue.js directives in your templates without conflict. This integration allows you to take advantage of the strengths of both technologies: dynamic content rendering on the server with Smarty and reactive, client-side interactivity with Vue.js. The result is a flexible templating approach that supports complex, reusable components and enhances developer productivity.