AngularJS UI Bootstrap rating control - angularjs

I am trying to bind directive's attribute to a scope variable. I would like my UI Bootstrap control to dynamically change appearance when this scope variable changes value.
Here is the plunker:
http://plnkr.co/edit/gEjerpXhy3IuT5qRNeL8?p=preview
Every time you press on some of the checkmarks, $scope.max is increased by 1.
But, since that same $scope.max is passed to the 'rating' element as the 'max' attribute, I would like this 'rating' element to put additional checkmark on the right every time I click on some of the existing checkmarks.
I guess I am trying to re-draw this 'rating' element with new parameters. Is this possible and how?

I was able to simulate the behavior you want by adding an ng-if to the element, and modifying a boolean value when the rating changes.
<rating ng-if="render" value="rating.x" max="rating.max"...
$scope.rating.max += 1;
$scope.render = false;
$timeout(function() {
$scope.render = true;
}, 0, true);
Here is a demo, forked from your original plunkr.
For what it is worth, I think you would be better off customizing the directive's source or just writing your own. This workaround isn't going to scale, and it wouldn't be too hard to sprinkle in the functionality you need.

Related

How do I return focus to an element when the entire page changes?

I have a complicated setup. My application is driven by a set of "rules" which dictate what the user interface is. The UI is rendered by looping through the rules and creating the individual dropdowns. Initially, everything renders properly. However, once a user makes a change to the UI, other rules may be affected. In my application, an api call is made, which then returns a modified set of rules. In the attached plunker, I've simplified things such that only the new set of rules is applied, which causes the page to re-render. The problem is that my users would like to be able to tab between all of the entries on the page and make changes. However, once the page is re-rendered, the currently selected page element is now gone nothing has the focus. I've tried to put the focus back on the proper element by tracking a common Id, but to no avail.
Using either of these doesn't seem to work.
var el = document.getElementById(focusId);
el.focus();
angular.element(el).focus();
I've also tried using the autofocus attribute on the dropdown that I want to have focus, but that didn't work either. I'm using angularjs 1.2. Any ideas are appreciated.
http://plnkr.co/edit/ND9PKqULIOlixWR4XChN?p=preview
If you want to assign auto focus dynamically to a element on the DOM from angular you can use this:
var myEl = angular.element(document.querySelector('select'));
myEl.attr('autofocus',"attr val");
You can also pass in an id like: angular.element(document.querySelector('#focusId'));
You can look here for a prior answer which may be of some more help!
-Cheers!
Problem here is, you are trying to focus the element before the blur event completes. So you need to execute the focus code after blur event. Async execution of your focus code would solve the problem. You can use either setTimeout or $timeout.
setTimeout(function(){
angular.element('#'+focusId).focus();
/* //or
var el = document.getElementById(focusId);
el.focus();
*/
});
or
$timeout(function(){
angular.element('#'+focusId).focus();
/* //or
var el = document.getElementById(focusId);
el.focus();
*/
});
dont forgot to inject $timeout to your controller if you are using second code. Hope this helps :)

Angular scope variable update not reflected in UI

We are working on an HTML page which makes use of a Bootstrap tooltip on a certain <span> tag. For those who have not heard of tooltip, it is a popup of sorts which appears when hovering over the element to which it is attached. Here is a screenshot showing the <span> in question, and what happens on hover:
The premise behind adding the tooltip was that in the event that we truncate the text, the tooltip would provide an option for viewing the entire text.
However, we would now like to condtionally show the tooltip only when there is no ellipsis in the text. We defined the tooltip-enable property in the <span>:
<span uib-tooltip="{{someName}}" tooltip-placement="right" tooltip-enable="{{showToolTip}}">{{someNameShortened}}</span>
The key thing here is tooltip-enable="{{showToolTip}}", which binds the property to a scoped variable in the controller for this page. And here is the relevant (and abbreviated) controller code:
mainApp.controller('repoListController',['$scope', '$rootScope', ...,
function($scope,$rootScope, ...) {
$scope.showToolTip = false;
var repositoryList= function(){
repositoryService.getRepositoryList(function(data) {
var repoList = data;
repoList.shortenedDisplayName = repositoryService.getShortRepoName(repoList.repoName, DISPLAY_NAME_MAX_LENGTH);
// if the repository's name be sufficiently large (i.e. it has an ellipsis)
// then show the tooltip. Otherwise, the default value is false (see above)
if (repoList.repoName.length > DISPLAY_NAME_MAX_LENGTH) {
$scope.showTooltip = true;
}
});
}
repositoryList();
}]);
Based on the research I have done, the common solution for why a change to a scoped variable is not reflected in the UI is to run $scope.$apply(), or some variation on this. Running apply(), as I understand it, will tell Angular JS to do a digest cycle, which will propagate changes in the scope to the UI. However, trying to do an apply() from the code which toggles showToolTip resulted in errory. I inspected the value of $scope.$root.$$phase while running the code which updates the showToolTip variable, and the phase was digest.
So now I am at a loss to explain this. If the code is already in a digest, then why would changes not be reflected in the UI? Also, if the code is already in digest, then how could I force Angular to sync the changes to the UI?
Two things need fixing...
Don't use string interpolation for your boolean showToolTip
<span uib-tooltip="{{someName}}" tooltip-placement="right"
tooltip-enable="showToolTip">{{someNameShortened}}</span>
JavaScript variables / properties are case sensitive. In your getRepositoryList handler, you have $scope.showTooltip. It should be $scope.showToolTip (two capital "T"s)
Crappy Plunker demo ~ http://plnkr.co/edit/W7tgJmeQAJj0fmfT72PR?p=preview

Angular custom directive not repainting when the datasource changes

I am working on a custom directive in angular (1.4.8) to display an array of items in a vertical list with gradient background colors. I have put together a plunker to demonstrate. The directive takes an initial RGB value (separate values for r,g, and b), converts to HSL, then decreases the saturation value of the HSL for each additional index in the array.
The Good
The directive works fine for a static list of items...
The Bad
...but in my use case items will be added to the array by the user, and the directive should account for changes to the array and re-render the list with the color gradient adjusted accordingly. At the moment I am having a mental block about how to get this part working.
The directive right now sits inside of an <li> that ng-repeats through the array, and I have given it an isolate scope that has knowledge of the array item and the index of the item within that <li> element. I am also passing in the full array as a scope item so that I can $watch for changes to it, which feels hacky and isn't working, to boot:
return {
...
scope: {
origArray: '=',
item: '=',
idx: '#'
},...
}
scope.$watchCollection(function() { return scope.origArray; }, function(newVal) {
if (newVal) {
console.log('there was a new item added');
removeGradient(); // function to set element background to grey
renderGradient(); // function to set element background to index-appropriate gradient color
}
});
If you have any thoughts about how I might achieve the dynamic re-rendering, I'd really appreciate it. Thank you!
The problem is in your listLength variable. It's true that you were tracking the collection but you're not updating listLength that renderGradient is depending on.
So the fix is simple but first of all, there is no need to compute the list's length by querying the dom. You can simply do listLength = scope.origArray.length.
The fix:
// In function decrementHsl, the first line: update listLength
listLength = scope.origArray.length;
...
The updated plunker here.

Angularjs form.$dirty

I'm able to find form data is changed or not using $dirty.
ex: I changed text box or drop down and then $dirty become true. If I reverted to old data still it is true. I need to know if my changes are reverted or not. Do we have any property in Angularjs? If property is true I want to enable save button otherwise it should be disable.
https://docs.angularjs.org/api/ng/type/form.FormController
I need to implement around 10 pages and each page has 10 text boxes and a couple of drop downs. So I don't want track each control manually in my pages.
You can try using this module: https://github.com/betsol/angular-input-modified
From the README file:
This Angular.js module adds additional properties and methods to the
ngModel and ngForm controllers, as well as CSS classes to the
underlying form elements to provide end-user with facilities to detect
and indicate changes in form data.
This extra functionality allows you to provide better usability with
forms. For example, you can add decorations to the form elements that
are actually changed. That way, user will see what values has changed
since last edit.
Also, you can reset an entire form or just a single field to it's
initial state (cancel all user edits) with just a single call to the
reset() method or lock new values (preserve new state) just by calling
overloaded $setPristine() method.
DISCLAIMER: I haven't tried it myself and I notice the author overwrites the ngModel directive instead of adding a decorator, which could be dangerous...but at the very least, you can look at the source and get an idea of how to write your own service or directive with similar functionality.
Even though it does not follow the usage of $dirty, but an implementation similar to this might be helpful for you in the case of a Save button on update.
Inside your html:
<form name="testForm" ng-controller="ExampleController" ng-submit=" save()">
<input ng-model="val" ng-change="change()"/>
<button ng-disabled="disableSave">Save</button>
</form>
Inside your controller:
.controller('ExampleController', ['$scope', function($scope) {
$scope.disableSave = true; // Keep save button disabled initially
$scope.val = 'Initial'; // Initial value of the variable
var copyVal = $scope.val; // Copy Initial value into a temp variable
$scope.change = function() {
$scope.disableSave = $scope.val === copyVal;
};
$scope.save = function() {
// Save the updated value (inside $scope.val)
console.log($scope.val);
// Re-disable the input box (on successful updation)
copyVal = $scope.val;
$scope.disableSave = true;
};
}]);
Here is a working plunkr for the same.

ng-repeat how to handle a button element separately

I am using ng-repeat to populate data in a table so far so good. What i am trying to accomplish is to use a button and change it's text according to the userId (cust.id). I am trying to understand how to use a $scope inside a repeat method and modify it separately from the other elements.
On the following demo when i click to button with (userid value = 1) then i would like to change the specific button text and NOT every button in my ng-repeat
<button ng-click="perLineText(cust.id)">{{buttonText}}</button>
Live Demo
I am trying to figure out how to handle specific elements in my ng-repeat. Any help much appreciated.
You can do that by just using the this in you controller function instead of $scope.
$scope.perLineText = function(customerId){
if (customerId === 1) {
this.buttonText = 'Stop';
};
See the updated fiddle http://jsfiddle.net/u5swjwv1/
On a click callback this points to the scope of nested repeat element. If you use $scope you are actually referring to the parent scope object.
Look at this explanation too 'this' vs $scope in AngularJS controllers
A good way to handle this problem is using $index. Just pass it when calling your perLineText($index) function so you know which row you are in. Now you can simply work with the appropriate index object in your array which might my $scope.customers[index]
You can also use the ng-bind="perLineText(cust.id)" instead of the {{buttonText}} so the ng-click will toogle something and the perLineText will return Start or Stop.

Resources