Enhance Angular.js Directive - ngRepeat

Recently I started a social Phone Gap project for a client and decided it would be wise to use a JavaScript framework. It was first suggested that I look into Backbone.js. I investigated it and did a little research and digging. I looked at Backbone.js, jQuery Mobile, Ember.js, various others, and Angular.js. The long and the short is I read a slew of reviews, some documentation, a bunch or tutorials, and in the end I decided to go with Angular.js for my framework of choice.

I’ve spent the last 2+ years developing in Python / Django and I found Angular.js to be familiar and very enjoyable to work in. The actual project I selected it for will be called ‘Dealtribus’. The client’s core concept is that business entrepreneurs and the like can use this to network their business deals. Similar to Linkedin only for deals instead of job networking.

Mobile Login

Slider View

One of the clients requirements was that they be able to browse through the list of deal result summaries one at a time by swiping left and right to navigate between each one. I approached this by selecting a responsive JavaScript jQuery carousel plugin and building the list of results using the Angular.js ngRepeat directive.

HTML Partials

<!-- parent template -->
<div id="slider-container fixed">
<div id="browser" class="row-fluid flexslider">
	<!--Body content-->
<ul class="deals slides" ng-include="'app/partials/deal/_list-summary.html'"></ul>
	</div>
</div>
<!-- include app/partials/deal/_list_summary.html -->
<li class="well well-small " ng-repeat="deal in deals">
  <div class="fixed">
  	<!-- some content here -->
	</div>
</li>

The first partial HTML template is included via the ngView directive and the $scope is passed to the Controller for any processing after which the template is compiled / rendered with the $scope from the controller which you’ll see hereafter. The second partial HTML template is the individual deals which will be viewed individually as a single slide.

JavaScript Controller

/** app/js/controllers.js **/
var DealListSummaryCtrl = ['$scope', '$routeParams', 'Deal', function($scope, $routeParams, Deal) {
    $scope.deals = Deal.query();
}];

Right now is a very basic controller which retrieves the Deal models and passes them to the view in the $scope. Simply put the deals will be listed using the ngRepeat directive as seen in the _list-summary.html as ng-repeat="deal in deals". The only problem was there is no event emitted when the repeat part is complete. There is an event fired for the ng-incldue directive in the first template, which is $includeContentLoaded, it however was firing before the repeat directive had finished and so the slider plugin wouldn’t initialize everything correctly.

Directives

You can create your own directives for just about anything. In this case I needed one that would fire / emit an event after the loop had finished.

/** app/js/directves.js **/
'use strict';
/* Directives */
angular.module('dealtribusDirectives', [])
	.directive('ngRepeatFinished', function() {
		return function($scope, element, attrs) {
			if($scope.$last) {
				$scope.$emit('$ngRepeatFinished', {});
			}
		};
	});
<!-- app/partials/deal/_list-summary.html modified -->
<li class="well well-small " ng-repeat="deal in deals" ng-repeat-finished>

To the best of my current understanding this declares a directive which will be applied to an HTML element when the HTML view is compiled and rendered, such that for each new deal that is added to the DOM this directive is called with the $scope, the current element, and any additional attributes. As ngRepeat loops it adds some loop variables to the $scope, one of which is $scope.$last, a boolean. It will be false until it reaches the last item in the deals array. And then the $ngRepeatFinished event is fired / emitted.

Now we can change the controller to add the Flexslider plugin.

/** app/js/controllers.js modified **/
$scope.$on('$ngRepeatFinished', function() {
		$scope.slider = $('.flexslider').flexslider({
			animation: 'slide',
			animationLoop: false,
			slideshow: false,
			controlsContainer: '#slider-container',
			controlNav: false,
			itemWidth: $('.flexslider').width(),
			startAt: ($routeParams.index) ? parseInt($routeParams.index) : 0,
		});
	});
$scope.settings = settings;

Now the slider plugin will be initialized more reliably than if I had tried to add a call to setTimeout.

Deal Summary Slider

Related Posts

Super Dad

My daughter, Olivia, has of late been growing like some plant that has been sprayed with Miracle Grow. Which also means nearly all of her clothes are too small and most of the jeans have holes in one or both of the knees. In addition to that she has been really into wearing dresses lately so she’s been looking kinda hussy and creates a problem when Raylene or I tell her she can’t wear her favorite clothes because they are too small.

Read More

Django Class Based Views + Mixins

For the pass two years I’ve been developing in Python with the Django framework and I have thoroughly enjoyed. As much as possible I have attempted to build tools that follow the DRY, Don’t Repeat Yourself, principle. With Django 1.3+ they released Classed Based Views Classed Based Views (CBV), which, in my opinion lend themselves more to generic applications than do the Function Based Views Function Based Views (FBV).

Read More
Switch to Hugo

Switch to Hugo

Over the years, there have been numerous apps and libs for managing one’s own website. Collectively these can be referred to as content management systems (CMS) and the majority started out as a combination of some programming language paired with a storage backend (database).

Read More