How do I access angular scope values during directive link phase? - angularjs

I have an element that shows a datetimepicker. It is timezone sensitive. The time is stored in an item object in the scope as item.time, while the TZ is stored as item.timezone.
I created a directive to render it. In order to actually render correctly, it needs to know the timezone, but at link time (when it is all processed), the controller has not yet correctly loaded item... or so I would think, but it has correctly loaded item, because item.time is there. Nonetheless, when the formatter gets called, it has attrs.timezone as "", even though modelValue is loaded correctly.
HTML:
<span datetimepicker="true" ng-model="item.time" timezone="{{item.timezone}}"></span>
And the JS, (leaving out most of it...)
.directive('datetimepicker',function () {
return {
restrict: 'A',
require: 'ngModel',
template: '<span><input readonly="readonly"></input><span class="glyphicon glyphicon-calendar"></span></span>',
replace: true,
link: function ($scope,element,attrs,ngModel) {
var format = "YYYY-MM-DD H:mm",
formatter = function (modelValue) {
// HERE IS MY PROBLEM: at this point, modelValue does == item.time, but attrs.timezone is "", even though I know it loads correctly!
var ret = modelValue ? moment(modelValue) : "", mtz;
if (ret.tz) {
mtz = ret.tz(attrs.timezone);
}
if (mtz) {
ret = mtz.format(format);
} else if (ret.format) {
ret = ret.format(format);
}
return ret;
};
ngModel.$formatters.push(formatter);
};
})
EDIT:
angular is properly evaluating the attr "timezone" on the element; it is just not available inside the link function, or even the formatter. Here is the html after loading:
<span timezone="America/Winnipeg" ng-model="item.time" datetimepicker="true" class="ng-valid ng-valid-datetime ng-dirty">
<input readonly="readonly"/><span class="glyphicon glyphicon-calendar">
</span>
EDIT: add controller, highly simplified
.controller('ItemDetail',['$scope','Item',
function ($scope,Item) {
var itemId = "40"; // not really, but good enough for here
Item.get({item: itemId},function (item) {
$scope.item = item;
},function (httpResponse) {
// do error reporting
});
// click button to enter edit mode, which hides <span> showing time and shows input with datepicker
$scope.edit = function () {
$scope.editMode = true;
};
$scope.save = function () {
// save the updates
$scope.item.$save();
$scope.editMode = false;
};
$scope.cancel = function () {
// cancel any updates
$scope.item.$reset();
$scope.editMode = false;
};
}])

I think you should use an isolate scope, bind the scope var through attribute bindings (= binding), and use scope.$watchto set the timezone with a callback:
// directive scope binding
scope: {
timezone: '='
}
// watch local scope var in the link function since it's now binded
scope.$watch('timezone',function(newval, oldval){
// do what you need to do from here when the timezone var gets set
mtz = ret.tz(attrs.timezone);
});
More information on scope bindings

Related

Angularjs form $error is not getting updated when the model is updated inside a directive unless the model be cleared

I can't figure out what's happening in the following example. I just trying to create my own required validation in my own directive where I have an array and I want to make it required (it's a simplification of what I want to do but enough to show the point)
Fiddler: http://jsfiddle.net/gsubiran/p3zxkqwe/3/
angular.module('myApp', [])
.directive('myDirective', function($timeout) {
return {
restrict: 'EA',
require: 'ngModel',
controller: 'myDirectiveController',
controllerAs: 'D_MD',
link: function(scope, element, attrs, ngModel) {
ngModel.$validators.required = function(modelValue) {
var result = false;
if (modelValue && modelValue.length > 0)
result = true;
return result;
};
},
bindToController: {
ngModel: '='
},
template: '(<span>ArrayLength:{{D_MD.ngModel.length}}</span>)<br /><input type=button value="add (inside directive)" ng-click=D_MD.AddElem() /><br /><input value="clear (inside directive)" type=button ng-click=D_MD.Clear() />'
}; }) .controller('myDirectiveController', [function() {
var CTX = this;
//debugger;
//CTX.ngModel = "pipo";
CTX.clearModel = function() {
CTX.ngModel = [];
};
CTX.AddElem = function() {
CTX.ngModel.push({
Name: 'obj100',
Value: 100
});
};
CTX.Clear = function() {
CTX.ngModel = [];
}; }]) .controller('MainCtrl', function($scope) {
var CTX = this;
CTX.patito = 'donde esta el patito';
CTX.arrayElements = [];
CTX.setElements = function() {
CTX.arrayElements = [{
Name: 'obj0',
Value: 0
}, {
Name: 'obj1',
Value: 1
}, {
Name: 'obj2',
Value: 2
}];
};
CTX.clearElements = function() {
CTX.arrayElements = [];
}; })
When I hit the add (outside directive) button, the required works fine,
but when I hit the add (inside directive) button I still getting the required error in the form (the form is defined outside directive).
But the more confusing thing for me is the following:
When I hit the clear (inside directive) button after hitting add (outside directive) button to make the required error go out, in this case the form is updating and the validation error is showing.
Why $validations.required is not firing inside the directive when I add new element to array but yes when I clear it?
Any ideas?
******* UPDATE *******
It seems to be related with array.push if I change array.push with the assignation of new array with wanted elements inside it works ok.
Still the question why it is happening.
As workaround I changed in the directive the AddElem function in this way:
CTX.AddElem = function() {
CTX.ngModel = CTX.ngModel.concat({
Name: 'obj100',
Value: 100
});
};
The ngModel you use here is a JS object. Angular has a reference to that object in its $modelValue and $viewValue (because angular basically does $viewValue = $modelValue). The $modelValue is the actual value of the ngModel, which, if you change it, will change the $viewValue after having run $validators.
To know if your ngModel has Changed, angular compares the ngModel.$viewValue with the ngModel.$modelValue. Here, you are doing a push() to the $viewValue which is updating the $modelValue at the same time because they are just references of each other. Therefore, when comparing them, they have the same value ! Which is why angular does not run your $validator.
The docs explain it :
Since ng-model does not do a deep watch, $render() is only invoked if the values of $modelValue and $viewValue are actually different from their previous values. If $modelValue or $viewValue are objects (rather than a string or number) then $render() will not be invoked if you only change a property on the objects.
If I over-simplify angular code, this snippet explains it:
var myArray = [];
var ngModel = {
$viewValue: myArray,
$modelValue: myArray,
$validate: function () { console.log('validators updated'); }, // log when validators are updated
}
function $apply() { // the function that is called on the scope
if (ngModel.$viewValue !== ngModel.$modelValue) {
ngModel.$viewValue = ngModel.$modelValue;
ngModel.$validate(); // this will trigger your validator
} else {
console.log('value not changed'); // the new value is no different than before, do not call $validate
}
}
// your push is like doing :
ngModel.$viewValue.push(12);
$apply(); // will output 'value not changed', because we changed the view value as well as the model value
// whereas your should do:
var newArray = [];
// create a copy of the array (you can use angular.copy)
for (var i = 0; i < myArray.length; i++) {
newArray.push(myArray[i]);
}
ngModel.$viewValue.push(12);
ngModel.$viewValue = newArray; // here we clearly update the $viewValue without changing the model value
$apply(); // will output 'validators updated'
Of course you are not forced to do an array copy. Instead, you can force the update of your ngModel. This is done by calling ngModel.$validate();
One way of doing it would be to add a forceUpdate() function in your scope, and call it from the controller after you do a push();
Example: http://jsfiddle.net/L7Lxkq1f/

AngularJS directive with ng-show not toggling with change in variable

I'm trying to create a simple html5 video playlist app. I've got an overlay div on top of the html5 video that should appear/disappear when stopping and starting the video.
I've got ng-show and a variable to trigger it, but it's not changing when I look using ng-inspector.
My events might not be quite correct, either - but I can't seem to find much information on putting events on different elements within the same directive. Is this a clue that I should break this up into multiple directives?
(function() {
'use strict';
angular
.module('app')
.controller('campaignController', campaignController)
.directive('myVideo', myvideo);
function campaignController($log,Campaign) {
var vm = this;
vm.overlay = true;
Campaign.getCampaign().success(function(data) {
vm.campaign = data[0];
vm.item = vm.campaign.videos[0];
});
vm.select = function(item) {
vm.item = item;
};
vm.isActive = function(item) {
return vm.item === item;
};
};
function myvideo() {
return {
restrict: 'E',
template: ['<div class="video-overlay" ng-show="vm.overlay">',
'<p>{{ vm.campaign.name}}</p>',
'<img class="start" src="play.png">',
'</div>',
'<video class="video1" controls ng-src="{{ vm.item.video_mp4_url | trusted }}" type="video/mp4"></source>',
'</video>' ].join(''),
link: function(scope, element, attrs) {
scope.video = angular.element(document.getElementsByClassName("video1")[0]);
scope.startbutton = angular.element(document.getElementsByClassName("start")[0]);
scope.startbutton.on('click', function() {
scope.vm.overlay = false;
scope.video[0].play();
});
scope.video.on('click', function() {
scope.video[0].pause();
scope.vm.overlay = true;
});
}
};
}
})();
From my personal experience angular expression evaluation does not work as javascript. so try ng-show="vm.overlay==true".
Furthermore you bind click using native javascript.
Either don't do that and use ng-click or call scope.$apply() in the click event t callbackas last intruction (even though i'm not sure if it's really important).

scope.$watch(attrs. is not working with a custom directive

I am using scope.$watch to look for changes on an attribute-based directive. These changes are initiated by an ng-model binding on an input element in the view. The directive attribute on the view is being watched using scope.$watch in the directive. Yet the change event never seems to fire in the directive. What is causing my code to break?
The portions highlighted in the code below, where I log to the console (in the directive code, with stars), never fire. The change to the controller scope, via ng-model on the input, is not being propagated to the directive.
If I change the attribute value to a static string, rather than binding it via ng-model, it works.
This code is taken from a working example in the AngularJS documentation here. I cannot 'spot the difference' as the code in the documentation is very similar to mine.
angular.module('myApp.advancedDirectives', [
'myApp.advancedDirectives.advancedDirectives-directive'
])
.value('data', { name: 'John', surname: 'Smith' })
angular.module('myApp.advancedDirectives.advancedDirectives-directive', [])
.directive('advancedDirectives', ['$interval', 'dateFilter', 'data', function ($interval, dateFilter, data) {
console.log(data.length);
function link(scope, element, attrs) {
var format,
timeoutId;
scope.$watch(attrs.advancedDirectives, function (theFormat) {
format = theFormat;
**console.log(theFormat);
updateTime();**
});
element.on('$destroy', function () {
$interval.cancel(timeoutId);
});
// start the UI update process; save the timeoutId for canceling
timeoutId = $interval(function () {
updateTime(); // update DOM
}, 1000);
function updateTime() {
scope.directiveScope2 = dateFilter(new Date(), format);
}
}
var theScope = {
directiveScope1: '=info'
}
return {
templateUrl: 'components/advancedDirectives/advancedDirectivesTemplate.html',
scope: theScope,
link: link
}
}]);
<div ng-controller="viewAdvancedDirectivesCtrl">
<div>
<div><input ng-model="theViewFormat"/></div>
<div>Data from the directive scope: <span advanced-directives="theViewFormat" info='data'></span></div>
</div>
</div>
<span style='background:yellow'>Advanced Directive. Here is some data: {{directiveScope1.name}} {{directiveScope1.surname}}, alive at {{directiveScope2}}</span>
As you are using isolated scope for your directive, theViewFormat value wouldn't be available inside your directive context.
Either you need to use $parent.+ attrs.advancedDirectives while placing $watch
OR
More preferred way would be pass advancedDirectives via attribute to isolated scope like you did for passing info data
var theScope = {
directiveScope1: '=info',
advanced: '=advancedDirectives'
}
Then simply your watch would be on 'advanced' string.
scope.$watch('advanced', function (theFormat) {
format = theFormat;
console.log(theFormat);
updateTime();**
});
Here is my answer:
Below, please see the view code.
Note: I used curly-braces for {{theViewFormat}}
<div ng-controller="viewAdvancedDirectivesCtrl">
<div>
<div>Data from the directive scope: <span advanced-directives="{{theViewFormat}}" info='data'></span></div>
</div>
</div>
The link code looks like below.
Note: Use of $observe (as recommended by #Anik)
Note: I am observing on the attrs, rather than on the directive scope object, therefore avoiding recursive calls to updateTime, and I am now observing the attribute change, rather than the scope change, which matches my requirement.
function link(scope, element, attrs) {
var format,
timeoutId;
attrs.$observe('advancedDirectives', function (theFormat) {
format = theFormat;
console.log('format changed: ' + format);
updateTime();
});
element.on('$destroy', function () {
$interval.cancel(timeoutId);
});
// start the UI update process; save the timeoutId for canceling
timeoutId = $interval(function () {
updateTime(); // update DOM
}, 1000);
function updateTime() {
scope.directiveScope2 = dateFilter(new Date(), format);
console.log('updateTime: ' + format + dateFilter(new Date(), format));
}
}
The updateTime() code, which updates the directive scope is as follows:
function updateTime() {
scope.directiveScope2 = dateFilter(new Date(), format);
console.log('updateTime: ' + format + dateFilter(new Date(), format));
}
Thanks to #Pankaj and #Anik for getting me a long way ahead.

AngularJS : directive two way data binding not working

I have a controller with following code snippet,
...
$scope.selected_contents = [];
$scope.$watch('selected_contents', function (sel_contents) {
console.log(sel_contents, 'selected contents');
}, true);
...
a directive,
commonDirectives.directive('chkbox', function() {
return {
restrict: 'A',
require: '?ngModel',
scope : {
item : '=item',
selection_pool: '=selectionPool'
},
link: function(scope, elem, attrs, ngModel) {
console.log('selected contents are', scope.selection_pool);
// watch selection_pool
scope.$watch('selection_pool', function (pool) {
console.log(pool, scope.selection_pool, 'pool updated');
if (_.contains(pool, scope.item)) {
elem.prop('checked', true);
}
else {
elem.prop('checked', false);
}
});
// toggle the selection of this component
var toggle_selection = function () {
if(_.indexOf(scope.selection_pool, scope.item) != -1) {
scope.selection_pool = _.without(scope.selection_pool , scope.item);
}
else {
scope.selection_pool.push(scope.item);
}
};
elem.on('click', toggle_selection);
}
};
});
and a template which uses the directive,
<tr ng-repeat="content in contents">
<td><input type="checkbox" selection_pool="selected_contents" item="content" chkbox></td>
</tr>
The problem is, changes in selection_pool in the directive is not reflected to selected_contents in the controller. What am i missing?
Update 1:
Following the suggestion from #mohamedrias I wrapped the changes in scope with scope.$apply. Doing so updates selected_contents in controller only while adding the content but not while removing it.
...
// toggle the selection of this component
var toggle_selection = function () {
if(_.indexOf(scope.selection_pool, scope.item) != -1) {
scope.$apply(function () {
scope.selection_pool = _.without(scope.selection_pool , scope.item);
});
}
else {
scope.$apply(function () {
scope.selection_pool.push(scope.item);
});
}
};
...
Angular uses name-with-dashes for attribute names and camelCase for
the corresponding directive name
From here.
The variable should be changed from this selection_pool:
<input type="checkbox" selection_pool="selected_contents" item="content" chkbox>
to selection-pool:
<input type="checkbox" selection-pool="selected_contents" item="content" chkbox>
And this selectionPool into the directive:
scope : {
item : '=item',
selectionPool: '=selectionPool'
}
EDIT: Because the selectionPool is an array, you should use $watchCollection:
scope.$watchCollection('selectionPool', function (pool)
And when you add/remove values from the array in toggle_selection function, should be wrapped within the $timeout function:
$timeout(function () {
if (_.indexOf(scope.selectionPool, scope.item) != -1) {
scope.selectionPool = _.without(scope.selectionPool, scope.item);
} else {
scope.selectionPool.push(scope.item);
}
});
This is to assure that a digest cycle is going to be applied afterwards.
Here's the code working on a jsfiddle: http://jsfiddle.net/0rvcguz0/3/
After researching for entire day, I ended up here. If someone is having any trouble with Angularjs scope, I highly encourage to read it.
The proper solution in my case was to wrap selected_contents by an object. e.g.
$scope.selected = {};
$scope.selected.contents = [];
Then in the template replace selcted_contents with selected.contents.
But still what I don't understand is, [] or an Array is also an object. My earlier code should have worked according to the information I found in the wiki. If anyone could explain me why I would really appreciate it :).

Reset form to pristine state (AngularJS 1.0.x)

A function to reset form fields to pristine state (reset dirty state) is on the roadmap for AngularJS 1.1.x. Unfortunately such a function is missing from the current stable release.
What is the best way to reset all form fields to their initial pristine state for AngularJS 1.0.x.?
I would like to know if this is fixable with a directive or other simple workaround. I prefer a solution without having to touch the original AngularJS sources. To clarify and demonstrate the problem, a link to JSFiddle. http://jsfiddle.net/juurlink/FWGxG/7/
Desired feature is on the Roadmap - http://blog.angularjs.org/2012/07/angularjs-10-12-roadmap.html
Feature request - https://github.com/angular/angular.js/issues/856
Proposed solution Pull request - https://github.com/angular/angular.js/pull/1127
Updated with possible workaround
Good enough workaround?
I just figured out I can recompile the HTML part and put it back into the DOM. It works and it's fine for a temporarily solution, but also as #blesh mentioned in the comments:
Controllers should be used for business logic only, not for DOM!
<div id="myform">
<form class="form-horizontal" name="form">
</form>
</div>
And in my Controller on resetForm():
Save the original untouched HTML
Recompile the saved original HTML
Remove the current form from the DOM
Insert the new compiled template into the DOM
The JavaScript:
var pristineFormTemplate = $('#myform').html();
$scope.resetForm = function () {
$('#myform').empty().append($compile(pristineFormTemplate)($scope));
}
I think it's worth mentioning that in later versions of Angular (e.g. 1.1.5), you can call $setPristine on the form.
$scope.formName.$setPristine(true)
This will set all the form controls to pristine state as well.
FormController.$setPristine
Solution without a workaround
I came up with a solution which uses AngularJS without any workaround. The trick here is to use AngularJS ability to have more than one directive with the same name.
As others mentioned there is actually a pull request (https://github.com/angular/angular.js/pull/1127) which made it into the AngularJS 1.1.x branch which allows forms to be reset. The commit to this pull request alters the ngModel and form/ngForm directives (I would have liked to add a link but Stackoverflow doesn't want me to add more than two links).
We can now define our own ngModel and form/ngForm directives and extend them with the functionality provided in the pull request.
I have wrapped these directives in a AngularJS module named resettableForm. All you have to do is to include this module to your project and your AngularJS version 1.0.x behaves as if it was an Angular 1.1.x version in this regard.
''Once you update to 1.1.x you don't even have to update your code, just remove the module and you are done!''
This module also passes all tests added to the 1.1.x branch for the form reset functionality.
You can see the module working in an example in a jsFiddle (http://jsfiddle.net/jupiter/7jwZR/1/) I created.
Step 1: Include the resettableform module in your project
(function(angular) {
// Copied from AngluarJS
function indexOf(array, obj) {
if (array.indexOf) return array.indexOf(obj);
for ( var i = 0; i < array.length; i++) {
if (obj === array[i]) return i;
}
return -1;
}
// Copied from AngularJS
function arrayRemove(array, value) {
var index = indexOf(array, value);
if (index >=0)
array.splice(index, 1);
return value;
}
// Copied from AngularJS
var PRISTINE_CLASS = 'ng-pristine';
var DIRTY_CLASS = 'ng-dirty';
var formDirectiveFactory = function(isNgForm) {
return function() {
var formDirective = {
restrict: 'E',
require: ['form'],
compile: function() {
return {
pre: function(scope, element, attrs, ctrls) {
var form = ctrls[0];
var $addControl = form.$addControl;
var $removeControl = form.$removeControl;
var controls = [];
form.$addControl = function(control) {
controls.push(control);
$addControl.apply(this, arguments);
}
form.$removeControl = function(control) {
arrayRemove(controls, control);
$removeControl.apply(this, arguments);
}
form.$setPristine = function() {
element.removeClass(DIRTY_CLASS).addClass(PRISTINE_CLASS);
form.$dirty = false;
form.$pristine = true;
angular.forEach(controls, function(control) {
control.$setPristine();
});
}
},
};
},
};
return isNgForm ? angular.extend(angular.copy(formDirective), {restrict: 'EAC'}) : formDirective;
};
}
var ngFormDirective = formDirectiveFactory(true);
var formDirective = formDirectiveFactory();
angular.module('resettableForm', []).
directive('ngForm', ngFormDirective).
directive('form', formDirective).
directive('ngModel', function() {
return {
require: ['ngModel'],
link: function(scope, element, attrs, ctrls) {
var control = ctrls[0];
control.$setPristine = function() {
this.$dirty = false;
this.$pristine = true;
element.removeClass(DIRTY_CLASS).addClass(PRISTINE_CLASS);
}
},
};
});
})(angular);
Step 2: Provide a method on your controller which resets the model
Please be aware that you must reset the model when you reset the form. In your controller you can write:
var myApp = angular.module('myApp', ['resettableForm']);
function MyCtrl($scope) {
$scope.reset = function() {
$scope.form.$setPristine();
$scope.model = '';
};
}
Step 3: Include this method in your HTML template
<div ng-app="myApp">
<div ng-controller="MyCtrl">
<form name="form">
<input name="requiredField" ng-model="model.requiredField" required/> (Required, but no other validators)
<p ng-show="form.requiredField.$errror.required">Field is required</p>
<button ng-click="reset()">Reset form</button>
</form>
<p>Pristine: {{form.$pristine}}</p>
</div>
</dvi>
EDIT... I'm removing my old answer, as it was not adequate.
I actually just ran into this issue myself and here was my solution: I made an extension method for angular. I did so by following a bit of what $scope.form.$setValidity() was doing (in reverse)...
Here's a plnkr demo of it in action
Here's the helper method I made. It's a hack, but it works:
angular.resetForm = function (scope, formName, defaults) {
$('form[name=' + formName + '], form[name=' + formName + '] .ng-dirty').removeClass('ng-dirty').addClass('ng-pristine');
var form = scope[formName];
form.$dirty = false;
form.$pristine = true;
for(var field in form) {
if(form[field].$pristine === false) {
form[field].$pristine = true;
}
if(form[field].$dirty === true) {
form[field].$dirty = false;
}
}
for(var d in defaults) {
scope[d] = defaults[d];
}
};
Hopefully this is helpful to someone.
Your form fields should be linked to a variable within your $scope. You can reset the form by resetting the variables. It should probably be a single object like $scope.form.
Lets say you have a simple form for a user.
app.controller('Ctrl', function Ctrl($scope){
var defaultForm = {
first_name : "",
last_name : "",
address: "",
email: ""
};
$scope.resetForm = function(){
$scope.form = defaultForm;
};
});
This will work great as long as your html looks like:
<form>
<input ng-model="form.first_name"/>
<input ng-model="form.last_name"/>
<input ng-model="form.address"/>
<input ng-model="form.email"/>
<button ng-click="resetForm()">Reset Form</button>
</form>
Maybe I'm not understanding the issue here, so if this does not address your question, could you explain why exactly?
Here I have found a solution for putting the from to its pristine state.
var def = {
name: '',
password: '',
email: '',
mobile: ''
};
$scope.submited = false;
$scope.regd = function (user) {
if ($scope.user.$valid) {
$http.post('saveUser', user).success(function (d) {
angular.extend($scope.user, def);
$scope.user.$setPristine(true);
$scope.user.submited = false;
}).error(function (e) {});
} else {
$scope.user.submited = true;
}
};
Just write angular.extends(src,dst) ,so that your original object is just extends the blank object, which will appear as blank and rest all are default.
Using an external directive and a lot of jquery
app.controller('a', function($scope) {
$scope.caca = function() {
$scope.$emit('resetForm');
}
});
app.directive('form', function() {
return {
restrict: 'E',
link: function(scope, iElem) {
scope.$on('resetForm', function() {
iElem.find('[ng-model]').andSelf().add('[ng-form]').each(function(i, elem) {
var target = $(elem).addClass('ng-pristine').removeClass('ng-dirty');
var control = target.controller('ngModel') || target.controller('form');
control.$pristine = true;
control.$dirty = false;
});
});
}
};
});
http://jsfiddle.net/pPbzz/2/
The easy way: just pass the form into the controller function. Below the form "myForm" is referenced by this, which is equivalent to $scope.
<div ng-controller="MyController as mc">
<ng-form name="myform">
<input ng-model="mc.myFormValues.name" type="text" required>
<button ng-click="mc.doSometing(this.myform)" type="submit"
ng-disabled="myform.$invalid||myform.$pristine">Do It!</button>
</ng-form>
</div>
The Controller:
function MyController(MyService) {
var self = this;
self.myFormValues = {
name: 'Chris'
};
self.doSomething = function (form) {
var aform = form;
MyService.saveSomething(self.myFromValues)
.then(function (result) {
...
aform.$setPristine();
}).catch(function (e) {
...
aform.$setDirty();
})
}
}

Resources