Directives, ngModelController and viewValue update - angularjs

I'm currently refactoring an old directive of ours, which basically displays a date in the local date format, while keeping an ISO date as the underlying model. I rely on ngModel controller, parsers, formatters and so on. It works mostly as expected, except for one thing.
One datepicker can have a startBoundary argument. When set, if the startBoundary date is in the future, then the current value of the directive should be set to this startBoundary, and the value displayed in the input field updated as well, of course. But so far, even though my modelValue is correctly updated, the value displayed in the field isn't. I've though of a digest issue, but no luck with this so far.
Here's a codepen demonstrating what happens : http://codepen.io/pabuisson/pen/dPRNbb
Any idea how I could solve this ? I don't get it. Thanks a lot guys !

You just need to call $render:
modelCtrl.$setViewValue moment.utc(start).format( dateFormat )
modelCtrl.$render()
Demo: http://codepen.io/anon/pen/xbrPbB

Related

angularJS $dirty when value unchanged

I have a form with multiple fields that the user can alter. I need to keep track of the state of the field and only allow the user to save if the value is changed. Now it looks like $dirty is set to true the moment when the value is altered even if it were changed back to the original value. Is there any way to use $dirty or something similar to be true only if the value is changed from it's original value?
*edit: okay it doesn't seem like it's possible with built in functions. Would I be better off storing all the original values? I'd avoid using $watch.
Angular FormController doesn't have any property you can use to achieve this. $dirty can only tell you if user has already interacted with the form. You have to watch the value and 'manually' check if it has changed.
use $pristine No fields have been modified yet

How to clear the md-autocomplete cache?

I'm using md-autocomplete to show results of an api query. Attribute md-items is iterating over a promise: item in getItems(searchText).
This is working well, and using the cache subsequent uses of the same search text return immediately with the same results.
But I need to be able to clear the cache at some points, when other search parameters change. How can I do this? By accessing the md-autocomplete controller perhaps? Although that seems non-standard and I'm not sure how.
As of version 1.0.5 of angular-material this isn't possible. I didn't find any acceptable workarounds, so I'm just disabling the cache with md-no-cache="true".
I've logged an issue for this on the angular-material project including a suggestion on how it could work.
It is definitely possible to reset the md-no-cache attribute programmatically anytime on your md-autocomplete directive.
If you have a boolean variable on your controller, let's say:
$scope.noCacheResults = false;
Then on your directive you can bind this variable to the md-no-cache attribute:
<md-autocomplete ...
md-no-cache="noCacheResults">
</md-autocomplete>
Like this, whenever your search parameters change you can set the $scope.noCacheResults to true or false depending whether you want to keep caching the query results or not.
Something that worked for me. Put an ng-if on your autocomplete. Then, in the code that changes the value of the other fields affecting this field, set that value to false, and then within a timeout, set it to true again. This will effectively remove the item from the DOM and put it back all nice and new with no cache.

Get pristine value for form element in Angular

Does Angular have anything built in that returns the pristine value of an input element?
I see that there's a $setPristine(), but there's no function to get the pristine value? Right now, I just create a copy of the pristine value in my controller when the controller initializes. I really can't believe that's correct—that there isn't anything that will give me the original form field's value.
The form field is dirty but the value is the same as it was when the form field was pristine. The user has dirtied the field, but when the user leaves the field, the value is the same as it was before the field was dirtied. What does Angular provide that will tell me that?
When your input element is dirty angular applies the ng-dirty class to it. You can check to see if this class is present on the element. It also applies the ng-pristine class on the element before it has been changed.
One thing to note: if you change the models value and set it back to it's original value, it will still be dirty. That tripped me up a bit.
not sure I understood what you're asking. but if you want to check if the element is clean or dirty just $scope.formName.inputName.$dirty (or $pristine) this will return true or false accordingly.
and if you want the value...well thats also simple :)

Using Angular-Bacons $scope.$watchAsProperty(property) in Angulars ng-repeat

I´m trying to find a good way to use Baconjs together with Angularjs in conjuctions with Angular-Bacon.
Now digesting from Bacon to the Angular scope works perfectly fine but I´m stumbling with Angular-Bacons $scope.$watchAsProperty(property) within Angulars ng-repeat:
Let´s say I have the Angular scope $scope.databaserecords and render it out with:
<div ng-repeat="record in databaserecords">
Each of the records has a record.checked property and I want to handle all checked records together in one Bacon stream, for example to add a certain tag to all records at once.
At this point using $scope.$watchAsProperty(databaserecords) I get no Bacon events when checking or unchecking certain records, so how could I accomplish to receive these changes back in Bacon?
I might also mention, that using $scope.$watchAsProperty(property) out of ng-repeat, for example for input fields, works well without any problem.
Thanks for your help! :)
If I've understood correctly, your actual databaserecords remains the same throughout the scope, so you'll need to invoke $watchAsProperty with the objectEquality argument set to true:
$scope.$watchAsProperty("databaserecords", true)
By default angular opts to compare objects with a simple object equality check. In your case the list object remains the same, so a deeper check is necessary. This is already implemented in angular-bacon but it seems I've omitted it from the docs.

Angular JS ngModel: Transform data

So we have a model with a 'time' in it, formatted like '14:00:00'. We are using angular-boostrap's timepicker, but it expects a JS date object or an ISO datetime string.
My first thought was to do a directive that would add $parser/$formatter to ngModel controller that would convert it on the way in and way out to the format we need. This works smashingly for output, but not for input.
I've made a fiddle here http://jsfiddle.net/HB7LU/1820/
myApp.directive('timespan',function($filter){
var dateFilter = $filter('date');
function parser(data){
return dateFilter(data,'HH:mm:ss');
};
function formatter(data){
var converted = '2000-01-01T' + data + '-06:00';
return converted;
};
return {
require: 'ngModel',
link:function(scope,element,attrs,ctrl){
ctrl.$parsers.unshift(parser);
ctrl.$formatters.unshift(formatter);
}
};
});
As you cans when you open the fiddle the 12:00:00 isn't reflected by the timepicker. If you look at the console its complaining that ngModel is in the wrong format. If you change the time with the picker you'll see that $scope.myModel.time is in the correct format.
How do I get the ngModel to the correct format before it gets to timepicker?
timepicker in angular-bootstrap expects the $modelValue to be a date (it passes it to the Date() constructor and then parses hours and minutes from the object). Since it writes its own $render() function, based on $modelValue, your formatters aren't being used.
I'm afraid you're not going to get what you want without doing some surgery on the angular-bootstrap code. It would likely be easier to write a controller with two input fields (for hours and minutes).
A possible solution is to create directive for your Model Value input. This directive would
receive date in the same format as timepicker - timestamp or Date object and display it any way you want.
I think timepicker must use $viewValue in render method. Therefore the best solution is this surgery on the angular-bootstrap code.
Change $modelValue to $viewValue in render function.

Resources