How to control invoking of directive - angularjs

In this plnkr :
https://plnkr.co/edit/F0XsOPZKq5HArFo9vtFs?p=preview
I'm attempting to prevent a custom directive being invoked by the use of ng-show. But if check console output when the directive is invoked 4 times : console.log('invoked') But ng-show shows/hides html elements it does not control what is rendered within the custom directive itself.
Is there a mechanism to pass the ng-show to the custom directive and if it's false then do call the directive ? I think could pass a new variable to the directive which contains same value as ng-show and then wrap the body of the directive in a conditional ?
src :
goob.html :
goob
http-hello2.html:
2. http-hello2.html
index.html :
<!doctype html>
<html ng-app="app">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.9/angular.min.js"></script>
<script src="script.js"></script>
</head>
<body>
<div ng-controller="FetchCtrl">
<label>Filter: <input ng-model="search"></label>
<div ng-show="false">
<div ng-repeat="sourceUrl in sourceUrls | filter:search track by $index ">
<status-viewer url="sourceUrl"> </status-viewer>
</div>
</div>
</div>
</body>
</html>
mytemplate.html :
<!--<h1>{{url}}</h1>-->
<div>
<p>{{model}}</p>
</div>
script.js :
var myapp = angular.module('app', []).controller('FetchCtrl', FetchCtrl)
myapp.directive('statusViewer', function ($http , $interval) {
return {
restrict: 'E',
templateUrl: 'mytemplate.html',
scope: {
url: '='
},
link: function (scope, elem, attrs, ctrl) {
console.log('invoked')
scope.isFinishedLoading = false;
$http.get(scope.url).success(function (data) {
scope.model = data;
});
}
};
});
function FetchCtrl($scope, $http, $q , $parse) {
$scope.sourceUrls = [
'http-hello2.html'
,'http-hello2.html'
,'test.html'
,'goob.html'];
}
test.html :
test

Instead of ng-show you should use ng-if directive to avoid directive linking before show
Forked plunker example

Related

How to write directive to hide div clicking anywhere on page?

This is first time that I am writing directive. I am trying to write directive to hide my div.This is my html:
<div id="loggedIn" close-logged-in class="fade-show-hide" ng-show="loggedInOpened" ng-cloak>
#Html.Partial("~/Views/Shared/_LoggedInPartial.cshtml")
</div>
I find element but when I click anywhere on page I dont get anything.Can someone help me how to write directive so that when shown user click anywhere on page, it will hide that div.Any suggestion?
'use strict';
angular.module("accountModule").directive('closeLoggedIn', function () {
return {
scope: {},
restrict: 'A',
link: function (scope, element, attrs) {
var loggedIn = angular.element(document.getElementById("loggedIn"));
console.log(loggedIn);
var isClosed = false;
loggedIn.on('click', function (e) {
console.log("LOGGED IN ON CLICK ", loggedIn);
});
}
}
I dont get this message "LOGGED IN ON CLICK"
You don't need getElementById("loggedIn") The <div> is already there for you as the element argument in the link function. There should rarely be any need to reference elements by their ID's in Angular.
Is this what you are trying to achieve?
DEMO
html
<div hide-when-click-anywhere default-display='block'>Click anywhere to hide me</div>
js
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope, $window) {
$scope.isHidden = false;
});
app
.directive('hideWhenClickAnywhere', function ($window) {
return {
// bind a local scope (i.e. on link function scope) property
// to the value of default-display attribute in our target <div>.
scope: {
defaultDisplay: '#'
},
restrict: 'A',
link: function (scope, element, attrs) {
var el = element[0];
// set default css display value. Use 'block' if
// the default-display attribute is undefined
el.style.display = scope.defaultDisplay || 'block';
angular.element($window).bind('click', function(){
// Toggle display value.
// If you just want to hide the element and
// that's it then remove this if block
if(el.style.display === 'none'){
el.style.display = scope.defaultDisplay || 'block';
return;
}
el.style.display = 'none';
});
}
};
});
Update
Ok after reading you comments I think this might be more what you're trying to achieve;
Take note of this line in the <button> element defined in template.html:
ng-click="contentHidden = !contentHidden; $event.stopPropagation();"
$event.stopPropagation() stops the event from bubbling up and triggering the 'click' listener we have defined on $window in our directive.
DEMO2
app.js
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope, $window) {
$scope.hideOnStart = true;
});
app
.directive('panel', function ($window) {
return {
scope: {
// this creates a new 'isolate' scope
// '=' sets two-way binding between the directive
// scope and the parent scope
// read more here https://docs.angularjs.org/api/ng/service/$compile
hideOnStart: '=',
panelTitle: '#'
},
transclude: true,
templateUrl: 'template.html',
restrict: 'E',
link: function (scope, element, attrs) {
console.log(scope.panelTitle)
// div content is hidden on start
scope.contentHidden = scope.hideOnStart || false;
angular.element($window).bind('click', function(e){
// check if the content is already hidden
// if true then ignore
// if false hide the content
if(!scope.contentHidden){
scope.contentHidden = true;
// we have to manually update the scope
// because Angular does not know about this event
scope.$digest();
}
});
}
};
});
template.html
<div class="panel panel-default">
<div class="panel-heading">
<div class="panel-title">{{panelTitle}}
<button
type="button"
class="close"
ng-click="contentHidden = !contentHidden; $event.stopPropagation();"
aria-label="Close"
>
<span ng-hide="contentHidden">close</span>
<span ng-hide="!contentHidden">open</span>
</button>
</div>
</div>
<div class="panel-body" ng-hide="contentHidden" ng-transclude></div>
</div>
index.html
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<link rel="stylesheet" href="style.css" />
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" />
<script data-require="angular.js#1.4.x" src="https://code.angularjs.org/1.4.3/angular.js" data-semver="1.4.3"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<div class="container">
<h1>Demo</h1>
<div class="row">
<div class="col-sm-6">
<panel panel-title="Title" hide-on-start="hideOnStart">
<h4>Content...</h4>
<p>foo bar baz</p>
</panel>
</div>
</div>
</div>
</body>
</html>

Angularjs directive, call same directive from within itself

I am building generic angularjs directive to support editing of json object. I have json data and also admin data to have details about original data. Following code details which I am using to build my generic directive.
Please refer Plunker for running code.
http://plnkr.co/edit/x2lqHjYq48gwxW7oYyEQ?p=preview
Directive Code:
app.directive("objecteditor", [function () {
return {
restrict: "E",
templateUrl: "ObjectEditor.html",
replace: true,
scope: {
object: '=',
objectAdmin: '='
},
link: function (scope, element, attrs) {
//Method to initialize
scope.init = function () {
};
//Call init() to initialze the loading.
scope.init();
}
};
}]);
Directive Template:
<div>
<h4 data-ng-bind-template="{{objectAdmin.displayName}}"></h4>
<div data-ng-repeat="column in objectAdmin.objectDefinition">
<div data-ng-switch="column.type">
<div data-ng-switch-when="string">
<label class="label-plain" data-ng-bind-template="{{column.displayName}}"></label>
<input type="text" data-ng-model="object[objectAdmin.name][column.name]" placeholder="{{displayName}}" title="{{displayName}}" name="textBox{{name}}" required />
</div>
<!--Call same object for child type as object. But how??? If i call <object> directive here then goes into infinite cycle -->
<div data-ng-switch-when="object">
</div>
</div>
</div>
</div>
Controller Code:
var app = angular.module("myApp", []);
app.controller('ApplicationController', ['$scope',
function($scope) {
//Method to initialize
$scope.init = function() {
//Set json data strucutre for editing
$scope.objectAdmin ={"name":"bankinfo","displayName":"Bank Info","type":"object","objectDefinition":[{"name":"name","displayName":"Bank Name","type":"string"},{"name":"mainPhone","displayName":"Main Phone","type":"string"},{"name":"contact","displayName":"Contact","type":"object","objectDefinition":[{"name":"name","displayName":"Name","type":"string"},{"name":"title","displayName":"Title","type":"string"}]}]};
$scope.object={"bankinfo":{"name":"Chase Bank - Newburgh","mainPhone":"1 (845) 333-3333","contact":{"name":"Donna Shuler","title":"Commercial Accounts Mgr."}}};
};
//Call init() to initialze the loading.
$scope.init();
}
]);
Index.html
<!doctype html>
<html ng-app='myApp'>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
<script src="script.js"></script>
<script src="ObjectEditor.js"></script>
</head>
<body data-ng-controller="ApplicationController">
<h2>How to call same directive within itself?</h2>
<!--Use object editor directive to edit the object details -->
<objecteditor data-object="object" data-object-admin="objectAdmin"></objecteditor>
</body>
</html>
I want to make my directive generic so that same type of object editing can be supported by single directive.
When I tried to call same directive within itself then it fall into infinite loop (which is expected).
How can I achieve this functionality in directive?
Please help.
I was going through the recursive directive and came across following post which was helpful to implement my requirement.
https://www.packtpub.com/books/content/recursive-directives. Following is code to implement this.
Object Editor Directive Template:
<div>
<objecttree data-object="object" data-object-admin="objectAdmin" data-folder-guid="folderGuid" data-hide-header="'true'"></objecttree>
<script type="text/ng-template" id="objectTree">
<div class="clear-left">
<h4 data-ng-if="!hideHeader" data-ng-bind-template="{{objectAdmin.displayName}}"></h4>
<div data-ng-repeat="column in objectAdmin.objectDefinition">
<div data-ng-switch="column.type">
<imageeditor data-ng-switch-when="image" data-image="object[column.name]" data-folder-guid="folderGuid" data-column="column"></imageeditor>
<formcheckbox data-ng-switch-when="boolean" data-input-value="object[column.name]" data-editable="column.editable" data-name="column.name" data-display-name="column.displayName" data-formName="'resourceAddEditFormName'"></formcheckbox>
<addresseditor data-ng-switch-when="address" data-address="object[column.name]" data-column="column" data-collection-admin="objectAdmin"></addresseditor>
<formtextbox data-ng-switch-when="string" data-ng-if="!column.isDropdown" data-input-value="object[column.name]" data-editable="column.editable" data-name="column.name" data-display-name="column.displayName" data-formName="'resourceAddEditFormName'"></formtextbox>
<dropdownbox data-ng-switch-when="string" data-ng-if="column.isDropdown" class="admin-textbox" data-selected-id-list="object[column.name]" data-dropdown="column" data-multiple="column.dropdownTypeMultiple"></dropdownbox>
<div data-ng-switch-when="object">
<hr/>
<objecttree data-object="object[column.name]" data-object-admin="column" data-folder-guid="folderGuid" data-hide-header="'true'"></objecttree>
</div>
<div data-ng-switch-when="array">
<hr/>
<arrayobjecttree data-objects="object[column.name]" data-objects-admin="column" data-folder-guid="folderGuid" data-hide-header="'true'"></arrayobjecttree>
</div>
</div>
</div>
</div>
</script>
</div>
Object Tree:
app.directive("objecttree", ['$compile', '$templateCache', function ($compile, $templateCache) {
return {
restrict: "E",
scope: {
object: '=',
objectAdmin: '=',
folderGuid: '=',
hideHeader: '='
},
link: function (scope, element, attrs) {
element.replaceWith(
$compile(
$templateCache.get('objectTree')
)(scope)
);
}
};
}]);
This is Shankar. You can use the below solution for recursive directive.
Please confirm me whether this resolves your issue.If not, you can let me know.
As I'm not able to paste my entire code here, you can find the solution in my Github repository:(i.e. gmssankar/myRepository/Recursive Directive)
https://github.com/gmssankar/myRepository/blob/master/Recursive%20Directive
The code can be found in the following link too in plunker:
plnkr.co/edit/WrZekRS5AzfFJhQ2d3XK?p=preview
I'm not able to attach the complete code here as it restricts the number of lines. The logic is: Inside Directive template display bank name, main phone,contact name and contact title from the passed object. Then check if the passed object, has object definition (i.e child nodes). If yes, then extract each child using ng-repeat , and pass to same directive. Issue gets resolved here. From Github or plnkr , you can access the complete code.

Angular directive controller scope visibility

Question
Why isn't monkey and selected visible to the template?
Plunk
http://plnkr.co/edit/djS0KWyfJNKD0tfZ0IiV?p=preview
Code
<head>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.3/angular.js"></script>
<script type="text/javascript">
angular
.module('toruSelect', [])
.directive('toruSelect', function () {
return {
restrict: 'AE', // Allow usage as A - attribute, E - element
scope: { // Isolated scope
selected: '=' // Bi-directional binding to selected attribute,
},
controller: ['$scope', function ($scope) {
$scope.monkey = 'MONKEY';
console.log('toruSelect.controller.$scope', $scope);
}]
}
});
var app = angular.module('app', ['toruSelect']);
app.controller('AppCtrl', function($scope) {
$scope.val = 'initial';
$scope.appData = 'App data';
});
</script>
</head>
<body ng-controller="AppCtrl">
<h1>Directives and scopes..</h1>
<div toru-select selected="val">
<div style="color: red">RESULT: toruSelect.controller.monkey: {{monkey}}</div>
<div>EXPECTED: toruSelect.controller.monkey: MONKEY</div>
<div style="color: red">RESULT: toruSelect.controller.selected: {{selected}}</div>
<div>EXPECTED: toruSelect.controller.selected: initial</div>
</div>
</body>
Result
Directives and scopes..
RESULT: toruSelect.controller.monkey:
EXPECTED: toruSelect.controller.monkey: MONKEY
RESULT: toruSelect.controller.selected:
EXPECTED: toruSelect.controller.selected: initial
As you pointed it out on the comment of your directive, it has an isolated scope, so that value attached with monkey key is available on directive scope, not on controller one.
For selected, you have to display {{val}} and not {{selected}} as it's the variable concerned by the bi-directional binding on the directive scope.

How do I use getterSetter in an angular directive with an isolated scope?

I'm having trouble getting a getterSetter to work in a ng-model directive.
Please, look at this plunker: http://plnkr.co/edit/Tx0nyvvbuKqf1ZpsRTPu?p=preview
If you use the uncommented template, the example behaves as if it didn't understand that getSet() is a function it should call.
If you uncomment the other template (and comment out the first one), the input is connected to the {{ }} as expected.
Why is the getterSetter not working?
Thank you for help!
This is the code:
javascript:
var app = angular.module('app', [])
.controller('ctrl', ['$scope', function($scope) {
$scope.something = "hahaha";
}])
/*
* attributes:
* - value - a variable to store the input value in
*
*/
.directive('inputNumber', ['$log', function( $log ) {
var linker = function( scope, element, attrs) {
$log.info('inputNumber linker called! value = "' + scope.value + '".');
scope.getSet = function( newValue ) {
if( angular.isDefined( newValue ) ) {
scope.value = newValue;
}
return scope.value;
}
}
return {
restrict : 'E',
// template: '<p>Say something! {{ value }}</p><input ng-model="value" ng-model-options="{ getterSetter: false }"></input>',
template: '<p>Say something! {{ getSet() }}</p><input ng-model="getSet" ng-model-options="{ getterSetter: true }"></input>',
link: linker,
scope: {
value: '='
}
};
}])
html:
<!DOCTYPE html>
<html>
<head>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.15/angular.min.js"></script>
<link rel="stylesheet" href="style.css">
<script src="script.js"></script>
</head>
<body>
<h1>Hello Plunker!</h1>
<div ng-app="app" ng-controller="ctrl">
<input-number value="something" ></input-number>
</div>
</body>
</html>
The problem here is ngModelOptions is only available in angular 1.3, but you're using angular 1.2.x. Try to modify your script tag to use 1.3.0-rc.0/angular.min.js and it should work.
Your directive's template should be set to the model (in this case 'value').
template: '<p>Say something! {{ getSet() }}</p><input ng-model="value"
ng-model-options="{ getterSetter: true }"></input>'
Demo Plunker

Using ng-switch in directive with transclude

I am trying to create a template that shows some transcluded content. When I use ng-show everything works fine, but using ng-if or ng-switch gives me problems. I get this error message: Illegal use of ngTransclude directive in the template! No parent directive that requires a transclusion found
I understand that ng-switch creates a new scope. But the transclude should still go up to the parent chain. Is this a defect in angularjs? See http://jsfiddle.net/HgvP7/
Here is my html, modified from the documentation example:
<div ng-app="docsTransclusionExample">
<div ng-controller="Ctrl">
<my-dialog>Check out the contents, {{name}}!</my-dialog>
</div>
<!-- my-dialog.html -->
<script type="text/ng-template" id="my-dialog.html">
<div ng-switch="1+1">
<div ng-switch-when="2">
<div ng-transclude></div>
</div>
</div>
</script>
</div>
And the code:
angular.module('docsTransclusionExample', [])
.controller('Ctrl', function($scope) {
$scope.name = 'Tobias';
})
.directive('myDialog', function() {
return {
restrict: 'E',
transclude: true,
scope: {},
templateUrl: 'my-dialog.html',
link: function (scope, element) {
scope.name = 'Jeff';
}
};
});

Resources