...
As written in the PlatformUI technical introduction, pages in PlatformUI can be generated either by the browser based on the REST API (or any other API) responses or by doing part of the rendering on the server side for instance with some Twig templates called by a Symfony controller. Both options are perfectly valid and choosing one or the other is mainly a matter of taste. In this This step will examine both strategies even if the later steps will be based on the server side rendering.
...
Then in ezconf-listviewservice.js
, we can write the minimal view service:
...
This is the minimal view service, it only writes a "hello world" message in the console when instantiated but for now it's not used anywhere in the application.
...
After doing that, Y.eZConf.ListViewService
becomes available in the application plugin code and we can change the eZConfList
route to be:
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
app.route({ name: "eZConfList", path: "/ezconf/list", view: "ezconfListView", service: Y.eZConf.ListViewService, // constructor function to use to instantiate the view service sideViews: {'navigationHub': true, 'discoveryBar': false}, callbacks: ['open', 'checkUser', 'handleSideViews', 'handleMainView'], }); |
After doing this change, the Y.eZConf.ListViewService
is used when a user reaches the eZConfList
route.
...
A view service is responsible for fetching data so they it can be rendered. For a given route, the view service is instantiated the first time the route is accessed and then the same instance is reused. On that instance, the _load
method is automatically called. This is where the loading logic should be in most cases. This method also receives a callback as its only parameter. This callback function should be called once the loading is finished. Typically, a view service will use the JavaScript REST Client to request the eZ Platform REST API. To do that, a JavaScript REST Client instance is available in the capi
attribute of the view service.
...
At this point, if you refresh the PlatformUI application and follow the link added in the previous step, you should see a new REST API request to /api/ezp/v2/views
in the network panel of the browser:
The Locations are build built in the application but not yet used anywhere.
...
Now that we have the Locations, we have to give them to the view. For that, we have to implement the _getViewParameters
method in the view service. This method is automatically called when the view service loading is finished, it should return an object that will be used as a configuration object for the main view.
...
With that code, the view will receive the Location list as an attribute under the name locations
.
Info | ||
---|---|---|
| ||
When implementing a custom view service, you should always implement the protected |
...
The view now receives the Location list in the locations
attribute so we have to change the view to take that into account. For now, let's change the view it to just display the Location it receives as an unordered HTML list of links to those Locations. To do that, we have to:
- Declare the
locations
attribute in the view - Give that list to the template in a form it can understand
- Update the template to generate the list
The point #2 Point 2 is required because Handlebars is not able to understand the complex model objects generated by YUI. So we have to transform those complex object to into plain JavaScript objects. After doing the changes #1 in steps 1 and #22, the view looks like this:
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
YUI.add('ezconf-listview', function (Y) { Y.namespace('eZConf'); Y.eZConf.ListView = Y.Base.create('ezconfListView', Y.eZ.TemplateBasedView, [], { initializer: function () { console.log("Hey, I'm the list view"); }, render: function () { this.get('container').setHTML( this.template({ locations: this._getJsonifiedLocations(), }) ); return this; }, _getJsonifiedLocations: function () { // to get usable objects in the template return Y.Array.map(this.get('locations'), function (loc) { return loc.toJSON(); }); }, }, { ATTRS: { locations: { value: [], } } }); }); |
The Then the template has then to be changed to something like:
...
PlatformUI provides a path
template helper that allows you to generate a route URL in PlatformUI. It expects a route name and the route parameters.
Tip | ||
---|---|---|
| ||
The resulting code can be been in the The rest of this tutorial is focused on the server side rendering strategy. Completing the browser side rendering strategy to get the expected features is left as an exerciceexercise. |
Server side rendering
In this case a part of the rendering is delegated to the server. When building a PlatformUI page this way, the application will just do one or several more AJAX request(s) and inject the result in the UI. The PlatformUI Admin part is build built this way. To be easily usable in the JavaScript application, the server response has to be structured so that the application can retrieve and set the page title, the potential notifications to issue and the actual page content. This is done by generating an HTML fragment, in the following way:
...
In the case, the minimal view service is exactly the same as the one produced in the previous Minimal view service paragraph.
...
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
{% extends "eZPlatformUIBundle::pjax_admin.html.twig" %} {% block header_title %} <h1 class="ezconf-list-title">List view</h1> {% endblock %} {% block content %} <ul class="ezconf-list"> {% for value in results.searchHits %} <li><a href="">{{ value.valueObject.contentInfo.name }}</a></li> {% endfor %} </ul> {% endblock %} {% block title %}List{% endblock %} |
Again, it's a quite a regular template but to ease the generation of the expected structured HTML fragment, this template extends eZPlatformUIBundle::pjax_admin.html.twig
and redefines a few blocks, the main one being content
where the actual page content is supposed to be generated.
...
We now have a Symfony Controller able to generate our Location list but this list is not yet available in the application. As in Fetching Locations from the view service and Passing the Location to the view, we have to add the code in the view service. But in the case of a server side rendering, we can reuse Y.eZ.
ServerSideViewService
which provides the base API to parse an HTML fragment which also provides a ready to use _getViewParameters
method. All we have to do then is to implement the loading logic in _load
:
...
At this point, you should see the same list as the one that was generated in Browser side rendering section. The only difference lies in the non-working links being generated in the server side solution.
...
The server side code has no knowledge of the JavaScript application routing mechanism as a result, it can not directly generate any PlatformUI Application URI, but we know while generating the HTML fragment that we want each link to allow the navigation to the viewLocation
route for the Location being displayed. We can then change the Twig template to add the necessary metadata on each link so that the application have way to guess has a way of guessing where the user is supposed to go when clicking on the link:
...
Info |
---|
In PlatformUI code, the Locations, Content items and Content Types are identified by their REST id ie , that is the REST resource URL which allows you to fetch the object in the REST API. That's why we are using the |
...
Info |
---|
The DOM event handling is one of the main YUI View featurefeatures. It is documented in the YUI View guide. |
So now Now the click on the Location link is transformed in a navigateTo
event, we have to subscribe to that event in the view service to trigger the expected navigation:
...
Info | ||
---|---|---|
| ||
PlatformUI uses a lots lot of custom application level events thanks to the EventTarget YUI component. Those events are very similar to DOM events but they are attached to the application components instead of the DOM elements. Like for DOM events, there is a bubbling mechanism. For instance, here the view is firing an event and unless the propagation of the event is stopped, it will bubble to the view service and then to the application. The event basically follows the components tree until the application. An application level event is way for a deeply nested component to communicate with a higher level component. |
...