An extensible Hi-Framework UI component for data listing - easy pagination, filtering and ordering
- is extensible
- is configurable
- uses bootstrap components
- is ours (including you)
- is awesome!
<repository>
<id>Emerjoin</id>
<name>maven-repo</name>
<url>https://github.com/Emerjoin/maven-repo/raw/master</url>
</repository><dependency>
<groupId>org.emerjoin.webcomponents</groupId>
<artifactId>HiList</artifactId>
<version>2.0.1</version>
</dependency><list name="list1" each="row" src="SomeFrontier.methodName" repeat-element=".myElement">
<item class="myElement">
{{row.attribute}}
</item>
</list>All the attributes present on that list element are mandatory. There shouldn't be a list without any of that attributes.
It defines the name of your list instance and you can use such name to access the list public API from your view. Checkout the following example:
//some code here
$scope.list1.publicMethod(params);
//some code hereDefines the variable name that is used within the repeatable element to access the row's attributes.
Defines the frontier method that should be used as data source.
Defines the element that should be repeated for each row. Receives a jquery selector.
A valid frontier method to be used as a list data source should receive 4 arguments: the desired page, the maximum items per page, the filter details and the ordering settings.
@Frontier
@ApplicationScoped
public class SomeFrontier{
public Map methodName(int pageNumber, int itemsPerPage, Map filter, Map ordering){
Object[] pageRows = // fetch current page's rows from database
int totalMachedRows = //count matched rows in database
return HiList.listEncode(pageRows, totalMachedRows, pageNumber, itemsPerPage)
}
} <list ...>
<!--Your repeatable element and other stuff might be placed here-->
<input type="text" ng-model="filter.text"/>
<!--Other elements might also be placed here-->
</list> ...
<input type="text" ng-model="myList.filter.text"/>
...A change on the filter.text value will result in a refresh.
Hi-List will set the variable $delaying to true once it detects the server delay. You can have a DOM element that is displayed when the delay is detected. Check the following example:
<list ...>
<!--Your repeatable element and other stuff might be placed here-->
<div ng-show="$delaying">
<p>Please wait...</p>
</div>
<!--Other elements might also be placed here-->
</list>You can also define the delay detection time in milliseconds. All you have to do is to set the attribute delay on your list element. Check the following example, the server delay will be detected after 1 second waiting for the server reply:
<list ...delay="1000" ...>
<!--Your repeatable element and other stuff might be placed here-->
</list>Hi-List will set the variable $empty to true once it receives an empty array of rows. You can also have a DOM element that is displayed is such situations. Check the following example:
<list ...>
<!--Your repeatable element and other stuff might be placed here-->
<div ng-show="$empty">
<p>There is no match on the database</p>
</div>
<!--Other elements might also be placed here-->
</list>When the server returns an empty array of rows, the pagination is not displayed.
What is the fetch operation? Is when the Hi-List component attempts to fetch data from Server.
Why would you want to intercept such operation? To change the input or the outcome.
Changing the input means: add filtering properties. Changing the outcome means: changing the rows composition or even adding rows.
There two ways this can be achieved. The first one is using an extension and the second is using a function on your view scope. The first approach is discussed on the extensions section. Lets see how we can change the inputs of the fetch operation from a function of a view scope:
...
//This interceptor will add a date parameter to the filter
$scope.interceptor = function(filter){
filter.date = "2016-07-28";
};
... <list ...preFetch="interceptor(filter)"...>
<!--Your repeatable element and other stuff might be placed here-->
</list>MAKE SURE THE PARAMETER YOU PASS TO YOUR FUNCTION IS ALWAYS "filter".
There are also two ways this can be achieved. The first one again is using an extension and the second one is using a function on your view's scope. Let's see how do achieve this from a view's scope function.
...
//This will add a new property (checked) to every matched row
$scope.transformer = function(result){
result.data.forEach(function(row){
row.checked = true;
});
}
.... <list ...postFetch="transformer(result)"...>
<!--Your repeatable element and other stuff might be placed here-->
</list>MAKE SURE THE PARAMETER YOU PASS TO YOUR FUNCTION IS ALWAYS "result".
Check the extensions sections to see how to achieve the same goal from an extension.
Hi-List allows you to catch failures by setting the onFail callback:
<list... onFail="failure(err)">
$scope.failure = function(err){
//Do some magick here
}
MAKE SURE YOUR FUNCTION ALWAYS GETS the "err" parameter so that you can have access to the error object.
There two ways this can be done: from the markup and from javascript code.
<!--Setting 78 as the maximum items per page value-->
<list ...per-page="78"...>
<!--Your repeatable element and other stuff might be placed here-->
</list> ...
//Setting 78 as the maximum items per page value
$scope.myList.show.maxItems = 78;
...//Refreshing the current page
$scope.listName.refresh();
//Going to specific page
$scope.listName.goToPage(number);
//Go to the last page
$scope.listName.goToLastPage();
//Go to the first page
$scope.listName.gotToFirstPage();
//Go to the next page
$scope.listName.goToNextPage();
//Go to the previous page
$scope.listName.goToPreviousPage(); <!--Configure the list to not auto-load-->
<list ...autoload="false" ...>
<!--Your repeatable element and other stuff might be placed here-->
</list> //Initialize the list when you want it.
myList.load();The Hi-List is extensible. This means you can extend its capabilities creating your own extensions.
Just add the attribute extensions and set as value a list of extensions separated by space. Example:
<list ... extensions="extension1 extension2" ...>
<!--You list content-->
</list>hiList.extend(function(extension){
return {
name:"extension1", //The extension name
singleton:false, //If true, there will only be one instance of this extension for all the lists
build: function(){
//Override the instance methods here
extension.$startup = function(){
//This method is invoked when creating the extension instance
};
extension.apiSetup = function($scope, attrs){
//Your extension can add methods to the list instance scope from here
};
extension.transformRepeatable = function($scope,attrs,transformable){
//This method allows your extension to change the list repeatable element.
//Use transformable.repeatable to access the jQuery DOM element object.
};
extension.transformHtml = function($scope,attrs,transformable){
//This method allows your extension to change list generated HTML.
//Use transformable.html to access the jQuery DOM element object.
};
extension.preFetch = function($scope, filter){
//Invoked before the fetch operation begin.
//Allows an extension to manipulate the filter
};
extension.postFetch = function($scope, result){
//Invoked when the fetch operation finishes successfully.
//Allows an extension to manipulate the result before it gets displayed
};
extension.fetchFail = function($scope, filter, ordering){
//Invoked when the fetch operation fails.
};
extension.fetchFinished = function($scope){
//Invoked when the fetch operation finishes.
//Doesnt matter if it failed or not, this function is always invoked
};
extension.invalidResult = function($scope){
//This function is invoked when the list receives an invalid result from frontier method
};
return extension;
}
};
});hiList.extend(function(extension){
return {
name:"extension1", //The extension name
inject : ['$compile'],
singleton:false, //If true, there will only be one instance of this extension for all the lists
build: function($compile){
//...
extension.$startup = function(){
};
//...
return extension;
}
};
});