Directive to directive scope - angularjs

This is related to a question: Listen for broadcast in sub directive
I have two directives, one is a child, one is a parent. The issue is, I what the child to only catch an event of the parent directive. Here is what I need:
I have some check boxes and a select all button for a group of check boxes. When I click the "select all" button, I want it to select all the boxes. This part I have working. The catch is I have two instances of this on the page. Right now when I click "select all", all of the check boxes on the page are selected, not just the ones inside the directive instance. I'm sure this is a scope problem... but I'm not sure what. Here is my code:
HTML:
<div all-checkboxes-broadcast>
<div all-checkboxes-listener>
<input type="checkbox" />
</div>
<a ng-click="checkAll()" href="">Select All</a> <!-- this should ONLY check the boxes above, not the ones below. Currently clicking either select checks all the boxes on the page.-->
</div>
<div all-checkboxes-broadcast>
<div all-checkboxes-listener>
<input type="checkbox" />
</div>
<a ng-click="checkAll()" href="">Select All</a>
</div>
AngularJS:
app.directive('allCheckboxesBroadcast', [function () {
return {
restrict: 'A',
scope: true,
controller: ["$scope",function($scope) {
//select all checkboxes
$scope.checkAll = function () {
$scope.$broadcast('allCheckboxes',true);
};
}]
}
}]);
app.directive('allCheckboxesListener', [function () {
return {
restrict: 'A',
require: '^allCheckboxesBroadcast',
link: function(scope, element, attrs) {
scope.$on('allCheckboxes', function(event, shouldCheckAll) {
element.find('input').prop('checked',shouldCheckAll);
});
}
}
}]);
Edit: I found the answer myself. By adding "scope: true" to the parent directive, it creates a child scope that will prototypically inherit from its parent, which creates the functionality I was looking for. If anyone has a better way to do it, I'm all ears.

I found the answer myself. By adding "scope: true" to the parent directive (I changed my original question to include this edit), it creates a child scope that will prototypically inherit from its parent, which creates the functionality I was looking for. If anyone has a better way to do it, I'm all ears.

Perhaps you need to isolate the scope by adding a parameter in your return object in the allCheckboxesBroadcast directive, eg: scope: {}
Something you might wanna do also is label those directives:
<div all-checkboxes-broadcast="1">
<div all-checkboxes-listener="1">
<input type="checkbox" />
</div>
<a ng-click="checkAll()" href="">Select All</a> <!-- this should ONLY check the boxes above, not the ones below. Currently clicking either select checks all the boxes on the page.-->
</div>
<div all-checkboxes-broadcast="2">
<div all-checkboxes-listener="2">
<input type="checkbox" />
</div>
<a ng-click="checkAll()" href="">Select All</a>
</div>
Then:
app.directive('allCheckboxesBroadcast', [function () {
return {
restrict: 'A',
scope: { bcId: "&allCheckboxesBroadcast" },
controller: ["$scope",function($scope) {
//select all checkboxes
$scope.checkAll = function () {
$scope.$broadcast('allCheckboxes',true, $scope.bcId);
};
}]
}
}]);
app.directive('allCheckboxesListener', [function () {
return {
restrict: 'A',
scope: { bcId: "&allCheckboxesListener" },
require: '^allCheckboxesBroadcast',
link: function(scope, element, attrs) {
scope.$on('allCheckboxes', function(event, shouldCheckAll, id) {
if(id == scope.bcId){
element.find('input').prop('checked',shouldCheckAll);
}
});
}
}
}]);
This is a workaround I am using in one of my modules. If you find better options, let me know.
Update: I fixed your plnkr, took me longer than I thought, I am no angular expert and isolated scopes can mess with my mind still. The main issue was that you didn't use a template so your methods defined in your directives were not accessible in the html. I also replaced the & by = in the scope assignment. I thought & would work but I guess I should read the doc again. Anyway, there it is:
http://plnkr.co/edit/J2qBBj3R0rEkfxgPBE84?p=preview

Related

Angular Directive Pass back scope variable

I am trying to make a directive that is basically a text box with a max length counter on it. My directive is below. Basically a text box that will tell the user that they only have x number of characters left.
angular.module('InputApp', []);
angular.module('InputApp').directive('textAreaCounter', function () {
return {
templateUrl: '/Content/Directives/Inputs/TextAreaCounter.html',
restrict: 'AE',
multiElement: true,
scope: {
text: '='
},
link: function (scope, elem, attrs) {
if (scope.text == undefined || scope.text == '') {
scope.CharactersLeft = attrs.totalCharacters;
} else {
scope.CharactersLeft = attrs.totalCharacters - scope.text.length;
}
scope.TextValueChanged = function () {
scope.CharactersLeft = attrs.totalCharacters - scope.text.length;
}
}
}
});
The template html is:
<div class="row">
<div class="col-md-12">
<textarea ng-model="text" ng-change="TextValueChanged()" autogrow rows="5"></textarea>
</div>
</div>
<div class="row">
<div class="col-md-12 top-left smallText">
You have {{CharactersLeft}}.
</div>
</div>
And I use the directive like this.
<text-Area-Counter text="WholeDeletionText" total-Characters="250"></text-Area-Counter>
The issue that I am having is that the value 'WholeDeletionText' is not being updated by the directive.
My expectation is that the scope.WholeDeletionText in the parent would update with the text that was written in the textarea in the directive. At least my understanding of the isolated scope having the '=' sign is that they share it with the parent. What am I doing wrong or is there a better way to do it?
The problem occurs because you have not WholeDeletionText variable in the application scope.
To fix it you need:
1) create controller and initialize WholeDeletionText variable
.controller("MyController", function ($scope) {
$scope.WholeDeletionText = '123';
});
2) add ng-controller="MyController" directive in your main html file.
<body ng-app="InputApp" ng-controller="MyController">
<div>
<text-Area-Counter text="WholeDeletionText" total-Characters="250"></text-Area-Counter>
</div>
</body>
Full code see in Plunk.
Exactly, adding to the #Roman's point, when you use '=' in declaring the directive's scope variable it should be declared the the parent controller as well, otherwise you can use '#'.

Reusing a directive template for multiple forms with isolated scope

I'm working on a project where the user needs to be able to create many instances of the same form. As of now the user can click a button to create one or more forms. The problem I'm having is that by isolating the scope, as I think I should be doing given that I'm reusing the same directive, my ng-models can't communicate with the parent controller.
My directive for <rule-form></rule-form>..
(function(){
'use strict';
var ruleForm = function(){
return{
restrict: 'E',
replace: true,
scope: {},
templateUrl: 'edit/rule-create/ruleForm.html',
link: function(scope, element, attrs){
scope.length = document.forms.length;
}
}
}
angular.module('ganeshaApp')
.directive('ruleForm', ruleForm)
})();
And my template...
<form class="edit__div--rule-form" name="form_{{length}}">
<input type="text" placeholder="Rule Title" ng-model="rcCtrl.ruleTitle">
<div class="edit__div--rc-toolbar">
<select class="edit__btn--rc-select" ng-model="rcCtrl.select" apply-statement-type>
<option value="obligation statement">obligation statement</option>
<option value="prohibition statement">prohibition statement</option>
<option value="permission statement">restricted permission statement</option>
</select>
<div class="edit__btn--rc-noun">
Add noun/verb
</div>
<div class="edit__btn--rc-save" ng-click="rcCtrl.saveRule()">
<span class="glyphicon glyphicon-floppy-saved"></span>Save
</div>
<div class="edit__btn--rc-cancel">
<span class="glyphicon glyphicon-remove"></span>
Cancel
</div>
</div>
<div class="edit__select--statement-type"></div>
<div ng-show="rcCtrl.showTextEdit" class="edit__div--rule-form-text" contenteditable="true" ng-model="rcCtrl.ruleText"></div>
I tried using $parent , (e.g. $parent.rcCtrl.ruleText), but then I'm back to the problem of not having isolated scopes and each form updates the others. I'm a bit confused about this really. Does anyone know a solution to this problem, or is it just a problem with my code?
Add a controller to your directive.
angular.module('ganeshaApp').directive('ruleForm', function(){
return {
restrict: 'E',
replace: true,
scope: {},
templateUrl: 'edit/rule-create/ruleForm.html',
controller: "rulesFormController as rcCtrl",
link: function(scope, element, attrs){
scope.length = document.forms.length;
}
}
});
The AngularJS $compile service will then create an instance of the controller for each instance of the directive and attach it to each isolate scope.
For more information, see the AngularJS Comprehensive Directive API Reference.

How to set focus on textarea within a angular ui modal every time open the modal?

I have a textarea within an angular ui modal, and need to set focus to it when the modal is made visible, can not be in the document load because it works only the first time open the modal.
The modal have an animation to open, I need set focus after animation finished.
Based on other issues researched here, I created the following directive:
.directive('focusMe', ['$timeout', focusMe]);
function focusMe($timeout) {
return function (scope, element) {
scope.$watch(function () {
$timeout(function () {
element[0].focus();
});
});
};
};
But this directive always checks the focus of the textarea. As my modal has another input field, when I click on it, the focus is again changed to the textarea that is using the directive. How do I set focus only the first time that the modal is made visible?
UPDATE
More information:
The problem of always keep the focus on the textarea was resolved, in a way.
But as my modal has a fade in animation, in IE the focus is displayed above the text box, outside, I'm having to use timeout to IE to set correctly focus. That's not very nice. Some better way?
I use the following version of focus-me directive with angular ui-modal directive.
angular.directive('focusMe', function($timeout) {
return {
scope: { trigger: '#focusMe' },
link: function(scope, element) {
scope.$watch('trigger', function(value) {
if(value === "true") {
$timeout(function() {
element[0].focus();
});
}
});
}
};
});
Example of using focus-me directive in modal form:
<div class="modal-header">
some header
</div>
<div class="modal-body">
<form name="someForm" method="post" novalidate="">
<fieldset>
<textarea name="answerField" placeholder="Enter text..." focus-me="true" ng-model="model.text"></textarea>
</fieldset>
</form>
</div>
<div class="modal-footer">
some footer
</div>
Late for an answer but when using multiple directive it is best to use attr observe as u can run into issue of multiple directive creating isolate scope
ControlDirectives.directive('focusMe', function ($timeout) {
return {
link: function (scope, element, attr) {
attr.$observe('focusMe', function (value) {
if (value === "true") {
$timeout(function () {
element[0].focus();
});
}
});
}
};
});
<input name="baseCode" focus-me="true" type="text"/>
You'd have to test this, I'm not sure if it would work, but could you embed a script into your modal's HTML template with a simple:
...
<script>document.getElementById('fieldId').focus();</script>
...

AngularJS directive for accessing ng-repeat scope of child

Okay, so let me start off saying that I have a directive that creates a JQueryUI tab setup with a backing field that populate the tab name and the tab contents basically.
<div id="summaryTabPanel">
<ul>
<li ng-repeat="category in SummaryCategories">
{{category.Name}}
</li>
</ul>
<div ng-repeat="category in SummaryCategories" id="tabs-{{$index + 1}}">
{{category.Content}}
</div>
</div>
So as you can see, I have a <ul> with an ng-repeat in the <li>.
As I will be applying JQueryUI Tab function to the parent container id="summaryTabPanel", I need to wait until all the dom is rendered.
So I do some research, and find out I need to create a directive that kind of looks like the following:
Application.Directives.directive("repeatDone", function () {
return {
restrict: "A",
link: function (scope, element, attrs) {
scope.$watch("repeatDone", function () {
if (scope.$last) {
$("#summaryTabPanel").tabs({
event: "mouseover"
});
}
;
});
}
};
});
This watches to see if the last repeated item is found, and then applies the DOM changes via the statement $("#summaryTabPanel").tabs()
This is all fine and good, but it only works if I apply the directive to the last ng-repeated child item of the summaryTabPanel container.
<div id="summaryTabPanel">
<ul>
<li ng-repeat="category in SummaryCategories">
{{category.Name}}
</li>
</ul>
<div ng-repeat="category in SummaryCategories" id="tabs-{{$index + 1}}" repeat-done>
{{category.Content}}
</div>
</div>
If I move it to the previous ng-repeat item things work but not quite right. If I move it to the parent container, things don't work at all.
It seems wrong to me to apply this directive to the last child, and instead have a way to apply it to the root and somehow accomplish the same thing.
<div id="summaryTabPanel" repeat-done>
Is this possible somehow with Angular?
According to this link you should write your directive like this
Application.Directives.directive("repeatDone", function () {
return {
restrict: "A",
link: function (scope, element, attrs) {
$timeout( function () {
$("#summaryTabPanel").tabs({
event: "mouseover"
});
});
}
});
Please have a look at the jsfiddle I created to illustrate your case.

AngularJS: ngInclude with custom scope

I have an existing directive that creates a generic wizard that will be used across my current organization. This wizard will have steps, and each step has an HTML template to be loaded as the "body" of the wizard. Currently, I'm adding the template using ng-include. I don't know which attributes the wizard will have, as it will come from the wizard consumer.
Question: Is there a way to bind the template models (attributes) to a directive scope variable instead to the scope itself?
Currently I have:
Directive HTML
<div ng-include="/step1.html"></div>
Note: This step1.html is just an example. The actual HTML template will come as a configuration parameter from the wizard directive.
Step 1 HTML
<input type="text" ng-model="data.email"/>
This is "forcing" the wizard consumer to include "data" for each of his attributes.
As a result, I was looking for something like this:
Directive HTML
<div ng-include="/step1.html" ng-scope="scope.data"></div>
Step 1 HTML
<input type="text" ng-model="email"/>
I was able to solve the problem using ng-transclude. Now, the consumer does not need to send me the HTML code (step1.html). Right now, this goes directly into the wizard directive body, as follows:
Sample Controller
angular.module('sampleApp').controller('SampleCtrl', function ($scope) {
$scope.email;
});
Sample HTML
<div wizard>
<div step title="Step 1">
<input type="text" ng-model="email"/>
</div>
</div>
Directives with transclude option
angular.module('sampleApp').directive('wizard', function () {
return {
restrict: 'A',
transclude: true,
scope: {
},
template: '<div> Wizard <div class="stepsContainer" ng-transclude /></div>'
};
});
angular.module('sampleApp').directive('step', function () {
return {
require: "^wizard",
restrict: 'A',
transclude: true,
scope: {
title: '#'
},
template: '<div ng-show="current"> {{ title }} <div ng-transclude></div></div>'
};
});
I believe what you need is a directive, with which you can have isolate scope, just like taking parameter from outer scope.
Directive
yourModule.directive('step1', function() {
return {
scope: {
data: '=anyname'
},
templateUrl: "/step1.html"
};
})
Step1 HTML
<input type="text" ng-model="data.email"/>
Then when the consumer uses it:
<div step1 anyname="scope.consumerData"></div>
Then anyname can be named anything meaningful to the consumer, without knowing the template is actually using data.

Resources