Skip to content
This repository has been archived by the owner on May 29, 2019. It is now read-only.

feat(rating): plug into ngModel controller #1546

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/rating/docs/demo.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div ng-controller="RatingDemoCtrl">
<h4>Default</h4>
<rating value="rate" max="max" readonly="isReadonly" on-hover="hoveringOver(value)" on-leave="overStar = null"></rating>
<rating ng-model="rate" max="max" readonly="isReadonly" on-hover="hoveringOver(value)" on-leave="overStar = null"></rating>
<span class="label" ng-class="{'label-warning': percent<30, 'label-info': percent>=30 && percent<70, 'label-success': percent>=70}" ng-show="overStar && !isReadonly">{{percent}}%</span>

<pre style="margin:15px 0;">Rate: <b>{{rate}}</b> - Readonly is: <i>{{isReadonly}}</i> - Hovering over: <b>{{overStar || "none"}}</b></pre>
Expand All @@ -10,6 +10,6 @@ <h4>Default</h4>
<hr />

<h4>Custom icons</h4>
<div ng-init="x = 5"><rating value="x" max="15" state-on="'glyphicon-ok-sign'" state-off="'glyphicon-ok-circle'"></rating> <b>(<i>Rate:</i> {{x}})</b></div>
<div ng-init="y = 2"><rating value="y" rating-states="ratingStates"></rating> <b>(<i>Rate:</i> {{y}})</b></div>
<div ng-init="x = 5"><rating ng-model="x" max="15" state-on="'glyphicon-ok-sign'" state-off="'glyphicon-ok-circle'"></rating> <b>(<i>Rate:</i> {{x}})</b></div>
<div ng-init="y = 2"><rating ng-model="y" rating-states="ratingStates"></rating> <b>(<i>Rate:</i> {{y}})</b></div>
</div>
4 changes: 2 additions & 2 deletions src/rating/docs/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ Rating directive that will take care of visualising a star rating bar.

#### `<rating>` ####

* `value` <i class="icon-eye-open"></i>
* `ng-model` <i class="icon-eye-open"></i>
:
The current rate.

* `max`
_(Defaults: 5)_ :
Changes the number of icons.

* `readonly`
* `readonly` <i class="icon-eye-open"></i>
_(Defaults: false)_ :
Prevent user's interaction.

Expand Down
41 changes: 28 additions & 13 deletions src/rating/rating.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,22 @@ angular.module('ui.bootstrap.rating', [])
})

.controller('RatingController', ['$scope', '$attrs', '$parse', 'ratingConfig', function($scope, $attrs, $parse, ratingConfig) {
var ngModelCtrl = {$setViewValue: angular.noop};

this.maxRange = angular.isDefined($attrs.max) ? $scope.$parent.$eval($attrs.max) : ratingConfig.max;
this.stateOn = angular.isDefined($attrs.stateOn) ? $scope.$parent.$eval($attrs.stateOn) : ratingConfig.stateOn;
this.stateOff = angular.isDefined($attrs.stateOff) ? $scope.$parent.$eval($attrs.stateOff) : ratingConfig.stateOff;

this.init = function(ngModelCtrl_) {
ngModelCtrl = ngModelCtrl_;
ngModelCtrl.$render = this.render;

$scope.range = this.buildTemplateObjects(
angular.isDefined($attrs.ratingStates) ? $scope.$parent.$eval($attrs.ratingStates) : new Array(this.maxRange)
);
};

this.createRateObjects = function(states) {
this.buildTemplateObjects = function(states) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just thinking this doesn't need to be a public function, does it? Users can't override this either because it's called during initialization and never again.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, you are right. Probably need to move the $scope.range = ... into the init function. I think it's useful to allow override of this function.

var defaultOptions = {
stateOn: this.stateOn,
stateOff: this.stateOff
Expand All @@ -24,12 +34,10 @@ angular.module('ui.bootstrap.rating', [])
return states;
};

// Get objects used in template
$scope.range = angular.isDefined($attrs.ratingStates) ? this.createRateObjects(angular.copy($scope.$parent.$eval($attrs.ratingStates))): this.createRateObjects(new Array(this.maxRange));

$scope.rate = function(value) {
if ( $scope.value !== value && !$scope.readonly ) {
$scope.value = value;
if ( !$scope.readonly ) {
ngModelCtrl.$setViewValue(value);
ngModelCtrl.$render();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just wondering if this call was necessary as render is called when viewValue differs from modelValue, and during mouseLeave, render is called.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure about the $render called by the $setViewValue function?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Argh, you're right, I'm misreading everything tonight heh. Although it's unlikely that rate() can be called before enter() (mouseenter usually occurs before click). It's safer to have this here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No worries, 4-eyes principle :-)

}
};

Expand All @@ -41,13 +49,13 @@ angular.module('ui.bootstrap.rating', [])
};

$scope.reset = function() {
$scope.val = angular.copy($scope.value);
$scope.val = ngModelCtrl.$viewValue;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or just call ctrl.render or ngModelCtrl.$render()? (can't use this here heh)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can, but IMO this way we tightly couple reset method with render. So if someone wants to override render, would probably need to override reset also.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. Okay, your argument makes sense.

$scope.onLeave();
};

$scope.$watch('value', function(value) {
$scope.val = value;
});
this.render = function() {
$scope.val = ngModelCtrl.$viewValue;
};

$scope.readonly = false;
if ($attrs.readonly) {
Expand All @@ -60,13 +68,20 @@ angular.module('ui.bootstrap.rating', [])
.directive('rating', function() {
return {
restrict: 'EA',
require: ['rating', 'ngModel'],
scope: {
value: '=',
onHover: '&',
onLeave: '&'
},
controller: 'RatingController',
templateUrl: 'template/rating/rating.html',
replace: true
replace: true,
link: function(scope, element, attrs, ctrls) {
var ratingCtrl = ctrls[0], ngModelCtrl = ctrls[1];

if ( ngModelCtrl ) {
ratingCtrl.init( ngModelCtrl );
}
}
};
});
18 changes: 9 additions & 9 deletions src/rating/test/rating.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ describe('rating directive', function () {
$compile = _$compile_;
$rootScope = _$rootScope_;
$rootScope.rate = 3;
element = $compile('<rating value="rate"></rating>')($rootScope);
element = $compile('<rating ng-model="rate"></rating>')($rootScope);
$rootScope.$digest();
}));

Expand Down Expand Up @@ -71,22 +71,22 @@ describe('rating directive', function () {
});

it('shows different number of icons when `max` attribute is set', function() {
element = $compile('<rating value="rate" max="7"></rating>')($rootScope);
element = $compile('<rating ng-model="rate" max="7"></rating>')($rootScope);
$rootScope.$digest();

expect(getStars().length).toBe(7);
});

it('shows different number of icons when `max` attribute is from scope variable', function() {
$rootScope.max = 15;
element = $compile('<rating value="rate" max="max"></rating>')($rootScope);
element = $compile('<rating ng-model="rate" max="max"></rating>')($rootScope);
$rootScope.$digest();
expect(getStars().length).toBe(15);
});

it('handles readonly attribute', function() {
$rootScope.isReadonly = true;
element = $compile('<rating value="rate" readonly="isReadonly"></rating>')($rootScope);
element = $compile('<rating ng-model="rate" readonly="isReadonly"></rating>')($rootScope);
$rootScope.$digest();

expect(getState()).toEqual([true, true, true, false, false]);
Expand All @@ -106,7 +106,7 @@ describe('rating directive', function () {

it('should fire onHover', function() {
$rootScope.hoveringOver = jasmine.createSpy('hoveringOver');
element = $compile('<rating value="rate" on-hover="hoveringOver(value)"></rating>')($rootScope);
element = $compile('<rating ng-model="rate" on-hover="hoveringOver(value)"></rating>')($rootScope);
$rootScope.$digest();

getStar(3).trigger('mouseover');
Expand All @@ -116,7 +116,7 @@ describe('rating directive', function () {

it('should fire onLeave', function() {
$rootScope.leaving = jasmine.createSpy('leaving');
element = $compile('<rating value="rate" on-leave="leaving()"></rating>')($rootScope);
element = $compile('<rating ng-model="rate" on-leave="leaving()"></rating>')($rootScope);
$rootScope.$digest();

element.trigger('mouseleave');
Expand All @@ -128,7 +128,7 @@ describe('rating directive', function () {
beforeEach(inject(function() {
$rootScope.classOn = 'icon-ok-sign';
$rootScope.classOff = 'icon-ok-circle';
element = $compile('<rating value="rate" state-on="classOn" state-off="classOff"></rating>')($rootScope);
element = $compile('<rating ng-model="rate" state-on="classOn" state-off="classOff"></rating>')($rootScope);
$rootScope.$digest();
}));

Expand All @@ -145,7 +145,7 @@ describe('rating directive', function () {
{stateOn: 'heart'},
{stateOff: 'off'}
];
element = $compile('<rating value="rate" rating-states="states"></rating>')($rootScope);
element = $compile('<rating ng-model="rate" rating-states="states"></rating>')($rootScope);
$rootScope.$digest();
}));

Expand Down Expand Up @@ -175,7 +175,7 @@ describe('rating directive', function () {
ratingConfig.max = 10;
ratingConfig.stateOn = 'on';
ratingConfig.stateOff = 'off';
element = $compile('<rating value="rate"></rating>')($rootScope);
element = $compile('<rating ng-model="rate"></rating>')($rootScope);
$rootScope.$digest();
}));
afterEach(inject(function(ratingConfig) {
Expand Down