Yesterday I explained how you could retrieve documents through a search and adapt them with business objects, all using the REST API. Today I’ll show you how I used that to create a nice infinite scrolling page using AngularJS.
Start by forking nuxeo-angular-sample on GitHub. It’s a simple application that allows you to browse, edit, delete or create documents in a Nuxeo instance. Follow the readme to have it running; it’s quick and easy.
Now if you haven’t already, browse into the yo folder. The first thing I did was to modify the app/scripts/app.coffee file (yes, the sample is written in CoffeeScript). I added a new call to the when method in the $routeProvider object.
"use strict" angular.module("nuxeoAngularSampleApp", ['nxSession','ui.bootstrap','blueimp.fileupload']) .value("nxUrl", "/nuxeo/api/v1" ) .factory("nxSession", ["nxSessionFactory","nxUrl",(nxSessionFactory,nxUrl)-> nxSessionFactory( apiRootPath: nxUrl ) ]) .config ($routeProvider) -> $routeProvider .when("/nav/*path/edit" templateUrl: "views/edit.html" controller: "EditCtrl" ) .when("/nav/*path/new" templateUrl: "views/edit.html" controller: "CreateCtrl" ) .when("/nav/*path" templateUrl: "views/main.html" controller: "MainCtrl" ) .when("/slideshow/:tag" templateUrl: "views/slideshow.html" controller: "SlideshowCtrl" ) .otherwise redirectTo: "/nav/"
This means that each time someone hits the http://localhost:9000/#/slideshow/anyTag URL, he sees the content of the app/views/slideshow.html template included in the index.html file. This view is automatically bound to the SlideshowCtrl controller. So I need to create the app/views/slideshow.html file and add a new SlideshowCtrl controller.
As you probably guessed already, the HTML file goes into the app/views sub-folder. I’ll start by a very simple file containing only an h2 tag. The double braces are how you insert objects from your controller inside a template.
<h2>{{searchTag}}</h2>
If you go to http://localhost:9000/#/slideshow/anyTag, you will see {{searchTag}}
and this is normal since we have not declared our controller yet. So to declare our controller, we will edit the app/scripts/controllers/main.coffee file. Just add this at the end:
.controller("SlideshowCtrl", ['$scope' ($scope) -> $scope.searchTag = 'hello world' ])
Now you should see a nice Hello World message on your page. It works because we’ve assigned the String value ‘hello world’ in the searchTag variable of our controller $scope object. As you can see we don’t need to instantiate the $scope. AngularJS injects it for us. Now what would be nice is to display the tag instead of the hello world message.
The when method call we added at the beginning was made on the $routeProvider object. Notice that all object from the AngularJS framework start with a ‘$’ char. We’re now going to inject this object just like we did for the scope.
.controller("SlideshowCtrl", ['$scope','$routeParams' ($scope,$routeParams) -> $scope.searchTag = $routeParams.tag ])
And as you can see it’s really easy to retrieve the tag segment of the URL. Now you should see something like this if you go to http://localhost:9000/#/slideshow/islands
.
Remember that our goal is to display some documents resulting from a search. So we’re going to add a new service that will handle search. Let’s create a new file app/scripts/services/nxSearch.coffee to hold our search logic.
The code is pretty straightforward. Notice that we inject two AngularJS objects and one Nuxeo object: $http, $q and nxURL (see? no ‘$’ char there). nxURL does not come out of nowhere, it’s been defined in the app/scripts/app.coffee file.
angular.module('nuxeoAngularSampleApp') .factory "nxSearch", ["$http","$q","nxUrl",($http,$q,nxUrl)-> nxSearch = {} nxSearch.items = [] nxSearch.busy = false nxSearch.isNextPageAvailable = true nxSearch.currentPageIndex = 0 nxSearch.pageSize = 20 nxSearch.query = undefined nxSearch.bo = undefined nxSearch.setBOAdapter = (bo)-> nxSearch.bo = bo nxSearch.setPageSize = (pageSize)-> nxSearch.pageSize = pageSize nxSearch.setQuery = (query)-> nxSearch.query = query nxSearch.nextPage = ()-> if !nxSearch.query? $q.reject("You need to set a query") return if !nxSearch.isNextPageAvailable return if nxSearch.busy return nxSearch.busy = true url = nxUrl + "/path/@search" if nxSearch.bo? then url += "/@bo/" + nxSearch.bo url += "?currentPageIndex="+nxSearch.currentPageIndex+"&pageSize="+nxSearch.pageSize+"&query=" + nxSearch.query; $http.get(url).then (response) -> docs = response.data if(angular.isArray(docs.entries)) nxSearch.currentPageIndex = docs.currentPageIndex + 1 nxSearch.isNextPageAvailable = docs.isNextPageAvailable nxSearch.items.push doc for doc in docs.entries nxSearch.busy = false else nxSearch.busy = false $q.reject("just because") nxSearch ]
This new CoffeeScript file will be automatically compiled to JavaScript by Grunt, but we still need to add the resulting JavaScript file to our code. Edit the index.html file to add this line at the end of the file:
<script src="scripts/services/nxSearch.js"></script>
The next step is to use this code in our controller. Let’s start with something simple, like a list of documents:
<ul> <li ng-repeat='item in items'> <span>{{item.title}}</span> </li> </ul>
Let’s modify our controller accordingly. I need to inject the nxSearch service and put an items variable in the scope.
.controller("SlideshowCtrl", ['$scope','$routeParams','nxSearch' ($scope,$routeParams,nxSearch) -> $scope.searchTag = $routeParams.tag # set our query nxSearch.setQuery("SELECT * FROM Document WHERE ecm:tag = '" + $scope.searchTag + "'") # get the first result page nxSearch.nextPage() # expose our search items in the scope $scope.items = nxSearch.items ])
You should have something like this:
What about that infinite scroll I told you about? Because we only have the first page of results right now. Well it appears that there is an AngularJS module just for that. We’ll use Bower to install it. Simply type bower install nginfinitescroll --save
while you’re in the yo directory. This will install the ngInfiniteScroll module and update the bower.json file where all the module dependencies are stored. We still have some work to do to enable that module. We need to add it to our module configuration in the app/scripts/app.coffee file.
angular.module("nuxeoAngularSampleApp", ['nxSession','ui.bootstrap','blueimp.fileupload','infinite-scroll'])
And we also need to include the JavaScript library at the end of our index.html file:
<script src="scripts/services/nxSearch.js"></script>
Now we can start using the module. Go to your template and modify the <ul> tag like this:
<h2>{{searchTag}}</h2> <ul infinite-scroll='nextPage()' infinite-scroll-disabled='busy' infinite-scroll-distance='1'> <li ng-repeat='item in items'> <span>{{item.title}}</span> </li> </ul> <div ng-show='busy'>Loading data...</div> </div>
This won’t work as is, we need to expose the busy variable and the nextPage method from our scope. Now we can modify our controller like this:
.controller("SlideshowCtrl", ['$scope','$routeParams','nxSearch' ($scope,$routeParams,nxSearch) -> $scope.searchTag = $routeParams.tag nxSearch.setQuery("SELECT * FROM Document WHERE ecm:tag = '" + $scope.searchTag + "'") $scope.items = nxSearch.items # Expose the busy variable $scope.busy = nxSearch.busy # Expose the nextPage method $scope.nextPage = ()-> nxSearch.nextPage() ])
And that’s it. You now have infinite scrolling of a document search. In the next part I’ll show you how to handle the Business Object adapter and show a nice thumbnail grid :-)
The post Creating a Thumbnail Browsing Module with AngularJS and the REST API – Part 2 appeared first on Nuxeo Blogs.