angular set dirty form within directive controller - angularjs

I have an angular driven form using $dirty to spot for changes to enable/disable submit button.
Part of the form uses a Directive for uploading a logo but the form is noticing this as a changed element so upon setting a logo that validates in size I need to manually trigger that the form has had a change so should be a case of formName.$setDirty(); however console is saying that $setDirty() is not defined and I think this is because I am within a directive.
Within my directives controller upon file selection I call the function below and it is here when the file is valid that I would want to call the setdirty method.
function isFileValid(file) {
vm.fileValid = true;
vm.errorMessage = "";
if (file.size > 16777216) {
vm.errorMessage = "The File is too big!";
vm.fileValid = false;
} else if (file.size == 0) {
vm.errorMessage = "The File is empty!";
vm.fileValid = false;
}
if (vm.fileValid) {
// make form dirty
$setDirty();
}
return vm.fileValid;
}
Here is the directive JS
(function () {
'use strict';
.module("tpBusinessProfile")
.directive("tpLogoUploader", tpLogoUploader);
function tpLogoUploader() {
return {
templateUrl: "tpLogoUploader.directive.html",
bindToController: true,
scope: {
changedMethod: "&"
},
controller: "tpLogoUploaderCtrl",
controllerAs: 'logoCtrl',
restrict: "E"
};
}
})();
Any help is appreciated.

You need to use directive require option and require controller of form directive:
{
require: '^form'
and then in link function bind method that you need to your scope (dirty solution):
link(scope, elem, attrs, formController){
scope.makeFormDirty = formController.$setDirty
}
and now you can use it in your controller via makeFormDirty

Related

Angular directive to disable child elements

Is it feasible to create a directive (canUpdate) which will enable me to enable/disable elements of my angular/web api application depending on results from a service that will test users permissions for a given user group or comma separated list of groups maybe.
My thinking is:
<div><button can-update="customerMgmt">Edit customer detail</button></div>
and my directive can perform the call to check this user is part of customerMgmt group and enable/disable appropriately.
However I am struggling to visualize/understand what my directives' template would look like.
If you was to write a directive that would perform this type of operation what would your directives' html template look like, as I'd want this to be applicable to any element, text input, button, anchor, label etc... i'd basically be saying if the user isn't in the group(s) specified on the attribute then disable/don't allow text entry/clicking etc...
So I've wrote the following that "appears" to be performing as I expect for now, need to test more next week but it seems to be enabling/disabling dependent on what my permissionsService.permissionsForObject method returns(which goes off to webApi controller).... Does this make sense to you?
(function () {
'use strict';
angular.module('blocks.permissions').directive('canWrite', canWriteDirective);
function canWriteDirective() {
return {
//scope: {},
restict: "A",
controller: CanWriteController,
controllerAs: "vm",
bindToController: false,
link: function (scope, iElement, iAttrs, controller) {
controller.canWrite(iAttrs.canWrite);
}
}
};
CanWriteController.$inject = ["permissionsService"];
function CanWriteController(permissionService) {
var vm = this;
vm.canWrite = canWrite;
vm.canUpdate = false;
function canWrite(group) {
permissionService.permissionsForObject(group).then(function (result) {
vm.canUpdate = true;
}).catch(function (result) {
vm.canUpdate = false;
});
}
}
})();

Directive improvement with watch and conditional templating

Here is the directive:
.directive("unloggedWarning", function () {
return {
restrict: "EA",
link: function (scope) {
scope.$watch('currentUser', function() {
if(scope.currentUser === null) {
scope.notLogged = true;
} else {
scope.notLogged = false;
} });
}
};
})
currentUser is rootscope persistant user current status with Parse backend. So whenever user logs out, the watch will set notLogged to true. I guess I can then in the html file view use conditonal ng-if to display warning when user unlogs.
How could I improve this directive, so from INSIDE the directive, I can conditionally inject a template with some html in it ? I can't seem to pass the log status from the if statement, to a standard directive "template: " parameter inside the directive.
Here is a working example: http://plnkr.co/edit/S9fVZKXLx0Fc6QpkI8iH?p=preview
All I did was add the template:
template: '<div><div ng-show="notLogged">You are not logged in</div></div>',

angularjs to show and hide compiled directive

I am creating a tree view and trying to stick to angular. I have it close, where when the user clicks on a folder , i append a new directive which in turn via ajax gets the data to display.
The problem i am having is how to hide the dynamically created directive. When the user initially clicks on the folder, folderSelect is called and i insert a treechild directive. When the user clicks on the same folderSelect i need to hide the new treechild just created. I would think i can do it using ng-show on the directive i am creating. Just struggling wrapping my head around how to do it when creating this directive dynamically. Thanks in advance for any help
Some code:::
I have on the folder
data-ng-click="folderSelect($event,node)"
in the controller i have
$scope.folderSelect = function($event,node) {
node.expanded = node.expanded ? false : true;
if ( node.expanded ){
var treechild = angular.element(document.createElement('treechild'));
treechild.attr('parentid',node.data.rf_Breadcrumb);
treechild.attr('classlevel',node.data.class_Level);
treechild.attr('listtype',$routeParams.listType);
treechild.attr('ng-show','');
var el = $compile( treechild )( $scope );
angular.element($event.target).parent().append(treechild);
}
};
My question is how do i connect treechild.attr('ng-show','') to node.expanded, so when the parent
the directive
myApp.directive('treechild', function () {
return {
templateUrl: '/template/tree/treechild.html?v3',
controller: ['$scope', '$http', function($scope, $http) {
$scope.getChild = function(bread,listtype,classlevel) {
$http.get(URLsArray['therapeuticChildURL'] ,{params: {filter:listtype, breadcrumb:bread, classlevel: classlevel }} ).success(function(data) {
if ( data.therapeuticclass.length == 0 ){
alert("No records found");
return;
}
$scope.therapeuticchildlist = data.therapeuticclass[0].children;
$scope.druglist = data.therapeuticclass[0].drugs;
});
}
}],
restrict: 'E',
scope:true,
link: function (scope, element, attrs) {
scope.getChild(attrs.parentid,attrs.listtype, attrs.classlevel );
}
};
});
In case using an existing solution for your tree view is an option, I can really recommend Nick Perkins' angular-bootstrap-nav-tree. You can check out a demo here.
It might also help you with the implementation of your own tree view.

Angular ng-blur not working with ng-hide

Using a directive focus-me="inTextModeInput" in a text input
app.directive('focusMe', function($timeout) {
/*focuses on input
<input type="text" focus-me="focusInput">
*/
return {
scope: { trigger: '=focusMe' },
link: function(scope, element) {
scope.$watch('trigger', function(value) {
if(value === true) {
$timeout(function() {
element[0].focus();
scope.trigger = false;
});
}
});
}
};
});
Actually having 2 inputs, both uses focus-me
When i programatically set the value to focus on an input the ng-blur of other is not called.
NOTE : i am also using this in an ng-repeat.
Isolated scope
The blur is called, but you're not seeing that because you've created a directive with an isolated scope. The ng-blur is executed on the $parent scope. You should only use an isolated scope when the directive is implementing re-useable templates.
Two way binding on trigger
The line 'scope.trigger = false' is also setting a different boolean value because it's on a different scope. If you want to assign a value to a variable from a directive you should always wrap the value inside another object: var focus = { me: true } and set it like trigger=focus.me.
A better solution
But I wouldn't set the trigger to false at all. AngularJS is a MVC/MVVM based framework which has a model state for the user interface. This state should be idempotent; meaning that if you store the current state, reload the page and restore the state the user interface should be in the exact same situation as before.
So what you probably need is a directive that
Has no isolated scope (which allows all other directives to work: ng-blur, ng-focus, ...)
Keeps track of a boolean, which indicates the focus state
Sets this boolean to false when the element has lost focus
It's probably easier to see this thing in action: working plunker.
Maybe this (other) plunker will give you some more insight on scopes and directives.
Code
myApp.directive('myFocus', function($parse, $timeout) {
return {
restrict: 'A',
link: function myFocusLink($scope, $element, $attrs, ctrls) {
var e = $element[0];
// Grab a parser from the provided expression so we can
// read and assign a value to it.
var getModel = $parse($attrs.myFocus);
var setModel = getModel.assign;
// Watch the parser -- and focus if true or blur otherwise.
$scope.$watch(getModel, function(value) {
if(value) {
e.focus();
} else {
e.blur();
}
});
function onBlur() {
$timeout(function() {
setModel($scope, false);
});
}
function onFocus() {
$timeout(function() {
setModel($scope, true);
});
}
$element.on('focus', onFocus);
$element.on('blur', onBlur);
// Cleanup event registration if the scope is destroyed
$scope.$on('$destroy', function() {
$element.off('focus', onFocus);
$element.off('blur', onBlur);
});
}
};
});

Get value of scope variable in controller AngularJS

I have to fetch value of scope variable defined in directives. I have to get value of that scope variable in controller using AngularJS. How can i fetch value of scope variable?
Directive
app.directive('checkToggle', function() {
return {
scope: true,
link: function ($scope, element, attrs) {
$(element).on('click', function() {
$(element).find('i').toggleClass('icon-check icon-check-empty');
if ($(element).find('i').hasClass('icon-check')) {
$scope.isChecked = 'true';
} else {
$scope.isChecked = 'false';
}
});
}
}
});
I have to get $scope.isChecked value in controller.
If I understand your use-case correctly you would like to toggle an icon on click. If so you don't need to write any directive for this. And provided that you would like to write a directive your shouldn't go about it as you've started. Your code is very imperative, jQuery-like while AngularJS power is in driving declarative UI based on model changes.
Anyway, toggling an icon can be easily done with standard AngularJS directives:
<i ng-class="{'icon-star' : isChecked, 'icon-star-empty': !isChecked}" ng-click="isChecked = !isChecked"></i>
Here is a working plunk: http://plnkr.co/edit/nXXQA41w00Cpeo6tTibg?p=preview

Resources