Working with Entities
CUBA provides a number of components for CRUD operations with entities.
Before we proceed, it’s worth to remind some basic concepts related to entities:
-
Entity name uniquely identifies the class of entity. It consists of two parts divided by "$":
{namespace$concept}
. For example:taxi$Driver
,statistics$FieldDescription
, etc. On the middleware, an entity name is specified in the@Entity
annotation of the entity Java class. -
Entity view is a descriptor of what attributes of the entity and its related entities should be loaded from the database. For the sake of performance, a view should contain a minimal possible number of attributes. See more in the Developer’s Manual.
- Entity browse
-
-
The cuba-entities component is designed for loading a list of entities.
-
The cuba-entity component is designed for loading one entity instance by its id.
Below is an example of how these components can be used - a book browser application. A user sees a list of books and can select any book to see its details.
The list of books is loaded by
cuba-entities
,cuba-entity
is used to re-load a particular book. When we show a list of books we load as little information as possible for better performance. When a single book is selected by the user, we can afford loading a lot more: author biography, editions, even a photo of a cover page.Book browser
Source code
index.html<html> <head> <link rel="import" href="src/cuba/entity/books-browser.html"> <script src="bower_components/webcomponentsjs/webcomponents-loader.js"></script> </head> <body> <books-browser></books-browser> </body> </html>
src/cuba/entity/books-browser.html<link rel="import" href="../../../bower_components/polymer/polymer-element.html"> <link rel="import" href="../../../bower_components/polymer/lib/elements/dom-if.html"> <link rel="import" href="../../../bower_components/polymer/lib/elements/dom-repeat.html"> <link rel="import" href="../../../bower_components/cuba-app/cuba-app.html"> <link rel="import" href="../../../bower_components/cuba-data/cuba-entities.html"> <link rel="import" href="../../../bower_components/cuba-data/cuba-entity.html"> <link rel="import" href="../../../bower_components/paper-spinner/paper-spinner.html"> <dom-module id="books-browser"> <template> <style> .book-option { color: #0b6ec7; padding: 5px 0; } .book-option-value { cursor: pointer; } .book-option:before { content: '\25cf'; } .book-description { padding-top: 10px; } </style> <cuba-app api-url="/app/rest/"></cuba-app> <!-- Entities will be loaded automatically on the component initialization. --> <!-- The result of the request will be stored into 'books' property. --> <cuba-entities view="bookBrowse" entity-name="cuba$Book" data="{{books}}"></cuba-entities> <!-- auto="false" indicates that no loading happens on initialization. --> <!-- So, we need to use this component programmatically. --> <!-- While a request is going on the 'bookLoading' property will be set to true. --> <cuba-entity view="bookEdit" id="entityLoader" entity-name="cuba$Book" auto="false" loading="{{bookLoading}}"></cuba-entity> <h3> Please select a book to see more info: </h3> <template id="bookRepeater" is="dom-repeat" items="[[books]]"> <div class="book-option"> <span class="book-option-value" on-click="_onBookSelect">[[item.title]]</span> </div> </template> <div class="book-description"> <template is="dom-if" if="[[bookLoading]]"> <paper-spinner active></paper-spinner> </template> <template is="dom-if" if="[[selectedBook]]"> Book <b>[[selectedBook.title]]</b> of [[selectedBook.genre]] genre was written by [[selectedBook.author.name]] ([[selectedBook.author.born]] - [[_formatDeathDate(selectedBook.author.died)]]) and published in [[selectedBook.publicationYear]] year. </template> </div> </template> <script> class BooksBrowser extends Polymer.Element { static get is() { return 'books-browser'; } static get properties() { return { books: { type: Array, value: function() { return []; } }, selectedBook: Object, bookLoading: Boolean }; } _onBookSelect(e) { this.set('selectedBook', null); // It's how we can obtain a clicked item from 'dom-repeat' const bookId = this.$.bookRepeater.modelForElement(e.target).get('item.id'); this.$.entityLoader.entityId = bookId; this.$.entityLoader.load().then(function(book) { this.set('selectedBook', book); }.bind(this)); } _formatDeathDate(date) { return !!date ? date : 'Present days'; } } customElements.define(BooksBrowser.is, BooksBrowser); </script> </dom-module>
In this example we have omitted a code for login simplicity. In real applications, REST API won’t work until you login or enable anonymous access.
-
- Entity creation
-
The
cuba-entity-form
component provides an ability to create new entities. Basically, you have to provide an entity name and an entity instance you want to persist. After that, you can call thesubmit
method and the entity will be saved.Book creator
Source code
index.html<html> <head> <link rel="import" href="src/cuba/entity/book-creator.html"> <script src="bower_components/webcomponentsjs/webcomponents-loader.js"></script> </head> <body> <book-creator></book-creator> </body> </html>
src/cuba/entity/book-creator.html<link rel="import" href="../../../bower_components/polymer/polymer-element.html"> <link rel="import" href="../../../bower_components/polymer/lib/elements/dom-if.html"> <link rel="import" href="../../../bower_components/cuba-app/cuba-app.html"> <link rel="import" href="../../../bower_components/cuba-form/cuba-entity-form.html"> <link rel="import" href="../../../bower_components/iron-input/iron-input.html"> <link rel="import" href="../../../bower_components/paper-spinner/paper-spinner.html"> <dom-module id="book-creator"> <template> <cuba-app api-url="/app/rest/"></cuba-app> <cuba-entity-form id="bookForm" on-cuba-form-response="_onSaveComplete" entity-name="cuba$Book" entity="[[book]]"> <label> Title <br/> <iron-input bind-value="{{book.title}}"> <input /> </iron-input> </label> <br/> <br/> <label> Genre <br/> <iron-input bind-value="{{book.genre}}"> <input /> </iron-input> </label> <br/> <br/> <label> Publication Year <br/> <iron-input bind-value="{{book.publicationYear}}"> <input /> </iron-input> </label> </cuba-entity-form> <div> <template is="dom-if" if="[[_savingInProgress]]"> <paper-spinner active></paper-spinner> <br/> </template> </div> <button on-click="_createBook"> Create book </button> </template> <script> class BookCreator extends Polymer.Element { static get is() { return 'book-creator'; } static get properties() { return { book: Object, _savingInProgress: { type: Boolean, value: false } }; } _createBook() { this.set('_savingInProgress', true); this.$.bookForm.submit(); } _onSaveComplete() { this.set('_savingInProgress', false); this.set('book', null); alert('The book has been successfully created'); } } customElements.define(BookCreator.is, BookCreator); </script> </dom-module>
- Entity removal
-
The
cuba-entities
component has methodremove()
, which you can use to remove an entity instance. - Entity update
-
Entity can be updated using the
cuba-entity-form
component. - Conclusion
-
That was an overview of how CUBA Polymer components can be used to work with entities. But the components provide more functionality than was described in this section. For example,
cuba-entity
allows you to set thedebounce
parameter to avoid excessive requests to a server;cuba-entities
can sort entities by any field; and so on. To learn more, check out the public API at https://cuba-elements.github.io/cuba-elements and the source code of components.