angularjs to show and hide compiled directive - angularjs

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.

Related

angular set dirty form within directive controller

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

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).

How to dynamically insert and remove one nested Angular custom directives?

I'm relatively new to creating custom Angular directives, and I'm trying to replace a page in my application which is a series of JQuery components which get nested inside one another.
I'm wanting to create a set of custom Angular directives, which I can nest within each other allowing the user to build up a kind of form, whilst allowing them to delete and re-add any directives nested within one another if they need to.
I'm not sure how dynamically insert one directive into another, based on a user's choice, and how to set this within the directive so that a given directive's child can be deleted, re-added and then recompiled.
So far the only (unsophisticated) method I have come up with is to 2-way bind to an attribute on the directive's isolate scope, and have this determine the inner content of the directive like so, however upon changing this attribute object on the parent scope, the directive's DOM doesn't recompile, so I'm convinced there is a much better way to do this.
I'm assuming I might need to use transclusion or the link function in some way, so any guidance on this is much appreciated!
Current attempt:
app.directive("testCustomMapperThings", function($compile) {
var testTemplate1 = '<div>THIS IS FIRST THE DYNAMICALLY COMPILED TEST TEMPLATE 1</div>';
var testTemplate2 = '<div>THIS IS SECOND THE DYNAMICALLY COMPILED TEST TEMPLATE 2</div>';
var getTemplate = function(contentType) {
var template = '';
switch(contentType) {
case 'testTemplate1':
template = testTemplate1;
break;
case 'testTemplate2':
template = testTemplate2;
break;
}
return template;
};
var linker = function(scope, element, attrs) {
//reads the scope's content attribute (2 way bound to this directive's isolate scope) and sets as DOM
element.html(getTemplate(scope.content.testContent)).show();
//compiles the 2 way bound DOM, recompiles directive on changes to attributes. todo: CHECK DIRECTIVE RECOMPILES ON CHANGES TO ATTRIBUTES
$compile(element.contents())(scope);
};
return {
restrict: "E", //can only be an element
link: linker, //link function
scope: { //isolate scope, 2 way bind to a 'content' attribute
content:'='
}
};
});
Use of this directive in the DOM, where I attempted to alter the $scope.content object but the directive's inner content didn't recompile:
<test-custom-mapper-things content="testContent"></test-custom-mapper-things>
<button ng-click="changeContent()">Click to change the test content and see if DOM recompiles</button>
Controller for the parent scope of the directive:
$scope.testContent = {
testContent : "testTemplate1"
};
$scope.changeContent = function() {
if($scope.testContent.testContent == 'testTemplate1') {
$scope.testContent.testContent = 'testTemplate2';
} else {
$scope.testContent.testContent = 'testTemplate1';
}
};
Use the $compile service and scope.$watch method.
Example:
Javascript:
angular.module('dynamicTemplate',[])
.directive("testCustomMapperThings",['$compile',function($compile) {
return {
resctrict: 'AE',
scope: {
active: '='
},
link: function (scope, el, attrs, ctrl) {
var t1 = "<div>I'm 1st Template</div>",
t2 = "<div>I'm 2nd Template</div>",
newTemplate;
function loadTemplate() {
el.html('');
switch(scope.active) {
case 'template1':
newTemplate = t1;
break;
case 'template2':
newTemplate = t2;
break;
}
el.append($compile(newTemplate)(scope));
}
loadTemplate();
scope.$watch('active', function(newVal, oldVal){
if(newVal === oldVal) return;
loadTemplate();
});
}
}
}])
.controller('templateController', function() {
var vm = this;
vm.option = true;
vm.templateOption = vm.option? 'template1' : 'template2';
vm.change = function() {
vm.option = !vm.option;
vm.templateOption = vm.option? 'template1' : 'template2';
}
});
HTML
<div ng-app="dynamicTemplate">
<div ng-controller="templateController as t">
<button ng-click="t.change()"></button>
<test-custom-mapper-things active="t.templateOption"></test-custom-mapper-things>
</div>
</div>
Codepen: http://codepen.io/gpincheiraa/pen/vLOXGz

How do I use an Angular directive to show a dialog?

Using Angular, I'm trying to create a directive that will be placed on a button that will launch a search dialog. There are multiple instances of the search button, but obviously I only want a single instance of the dialog. The dialog should be built from a template URL and have it's own controller, but when the user selects an item, the directive will be used to set the value.
Any ideas on how to create the dialog with it's own controller from the directive?
Here's what I've go so far (basically just the directive)...
http://plnkr.co/edit/W9CHO7pfIo9d7KDe3wH6?p=preview
Here is the html from the above plkr...
Find
Here is the code from the above plkr...
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
var person = {};
person.name = 'World';
$scope.person = person;
$scope.setPerson = function(newPerson) {
person = newPerson;
$scope.person = person;
}
});
app.directive('myFind', function () {
var $dlg; // holds the reference to the dialog. Only 1 per app.
return {
restrict: 'A',
link: function (scope, el, attrs) {
if (!$dlg) {
//todo: create the dialog from a template.
$dlg = true;
}
el.bind('click', function () {
//todo: use the dialog box to search.
// This is just test data to show what I'm trying to accomplish.
alert('Find Person');
var foundPerson = {};
foundPerson.name = 'Brian';
scope.$apply(function () {
scope[attrs.myFind](foundPerson);
});
});
}
}
})
This is as far as I've gotten. I can't quite figure out how to create the dialog using a template inside the directive so it only occurs once and then assign it a controller. I think I can assign the controller inside the template, but first I need to figure out how to load the template and call our custom jQuery plugin to generate the dialog (we have our own look & feel for dialogs).
So I believe the question is, how do I load a template inside of a directive? However, if there is a different way of thinking about this problem, I would be interested in that as well.
I will show you how to do it using bootstrap-ui. (you can modify it easily, if it does not suit your needs).
Here is a skeleton of the template. You can normally bound to any properties and functions that are on directive's scope:
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
... // e.g. <div class="button" ng-click=cancel()></div>
</div>
<div class="modal-body">
...
</div>
<div class="modal-footer">
...
</div>
</div>
</div>
Here is how to create/declare directive in your module:
.directive("searchDialog", function ($modal) {
return {
controller: SearchDialogCtrl,
scope : {
searchDialog: '=' // here you will set two-way data bind with a property from the parent scope
},
link: function (scope, element, attrs) {
element.on("click", function (event) { // when button is clicked we show the dialog
scope.modalInstance = $modal.open({
templateUrl: 'views/search.dialog.tpl.html',
scope: scope // this will pass the isoleted scope of search-dialog to the angular-ui modal
});
scope.$apply();
});
}
}
});
Then controller may look something like that:
function SearchDialogCtrl(dep1, dep2) {
$scope.cancel = function() {
$scope.modalInstance.close(); // the same instance that was created in element.on('click',...)
}
// you can call it from the template: search.dialog.tpl.html
$scope.someFunction = function () { ... }
// it can bind to it in the search.dialog.tpl.html
$scope.someProperty;
...
// this will be two-way bound with some property from the parent field (look below)
// if you want to perform some action on it just use $scope.$watch
$scope.searchDialog;
}
Then it your mark-up you can just use it like that:
<div class="buttonClass" search-dialog="myFieldFromScope">search</div>
I recommend this plugin:
https://github.com/trees4/ng-modal
Demo here:
https://trees4.github.io/ngModal/demo.html
Create a dialog declaratively; and it works with existing controllers. The content of the dialog can be styled however you like.

Temporarily deactivate button until a certain service function resolves using a custom directive

I would like to disable a certain group of buttons that are available(I am using ngClick on those buttons) only for active users, and enable them again after the request that verifies that the account is indeed active resolves.
My current implementation is as follows:
directive('activeCompanyButton', function(authService, companyService) {
var defualtFunction = function(e){
e.preventDefault();
};
function bind(elem){
elem.addClass('disabled');
elem.bind('click', defualtFunction);
}
function unbind(elem){
elem.removeClass('disabled');
elem.unbind('click', defualtFunction);
}
return{
// scope: false,
link: function(scope, elem, attrs){
bind(elem);
},
controller: function($scope, $element, $attrs, authService, companyService){
function checkCompanyStatus(val){
var company = val;
var r = company && company.status == 'active';
return r;
}
$scope.$watch(function(){return companyService.getCompanyData(authService.getCompanyId())}, function(val){
console.log(val);
if(checkCompanyStatus(val)){
unbind($element);
$element.bind('click', $scope.$eval($attrs.ngClick));
}
else{
bind($element);
}
});
}
}
});
None of that is working, not even the $scope.$eval()(should i strip the '()' from the function name and leave the function to give the function reference rather than a function call?).
should I be using an isolate scope, I am not currently doing that because to the best of my understanding that would create multiple instances of dirty-checking(watchers) instead of just one.
Assuming this structure in your code:
<active-company-button>
<button style="custom" other="blah" />
</active-company-button>
One way to do this is add a templateUrl property to your directive to include a template that wraps a standard button & adds ng-disabled. You can then take advantage of ng-disabled's behavior for every button without having to add it to each button. Disadvantage is you have to put styling & other properties on the "outer" directive instead of on the button itself.
So, the above code would become this:
<active-company-button style="custom" other="blah" />

Resources