Reusing a directive template for multiple forms with isolated scope - angularjs

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.

Related

AngularJS Directive Transclude parent scope

I'm working on a directive and I'm using transclude so the inner element of the directive use is used inside it.
Let's say that this is my directive's view:
<div>
<div ng-repeat="opt in options">
<ng-transclude></ng-transclude>
</div>
</div>
Directive:
app.directive("myDirective", function(){
return {
restrict: "E",
transclude: true,
templateUrl: 'my-directive.html',
scope: {
options: '='
}
};
});
And a simple use of it:
<my-directive options="someOptions">
<p>{{someObject[$parent.opt]}}</p>
</my-directive>
This works just fine. My problem with this solution is that that $parent.opt is not very readable and clear...
Is there any other option?
Thanks
Your directive seems to be a very specific one.
How about passing the parent to the directive too?
<my-directive options="someOptions" object="someObject"></my-directive>
In the directive:
<div>
<div ng-repeat="opt in options">
<p>{{object[opt]}}</p>
</div>
</div>
And then add object: '<' in your isolated scope declaration.

Angular modal directive - issue when having more than 1 in a page

I made a modal directive for my angular app.
modal-directive.js
'use strict';
backyApp.directive('appModal', function() {
return {
restrict: 'A',
transclude: true,
link: function($scope, elem, attr){
$scope.modalClass = attr.appModal;
},
scope: '#',
templateUrl: './components/modal/modal.html'
};
});
and the template looks like this: (modal.html)
<!-- Modal -->
<div class="app-modal" ng-class="modalClass">
<div ng-transclude></div>
</div>
Now, let's pretend we have 2 modals in a page:
<div app-modal="firstModal">
<div class="form-group">
<input type="text" />
<input type="submit" value="Submit" />
</div>
</div>
</div>
<div app-modal="secondModal">
<div class="form-group">
<input type="text" />
<input type="submit" value="Submit" />
</div>
</div>
</div>
Problem: I end up with 2 modals having the same class (in my example above, secondModal will be attached to 2 of my modals)
Why does this happen? I need the value of my directive to be attached to each modal because thats the only way I can open the one I want.
I know this is horrible explanation Let me know if you have any question
Edit:
I want to have 2 app-modal divs, each one having its directive value as a class attached to it. Hope it's more clear now.
Use an isolated scope in the directive
backyApp.directive('appModal', function() {
return {
restrict: 'A',
transclude: true,
link: function($scope, elem, attr){
$scope.modalClass = attr.appModal;
},
scope: {},
templateUrl: './components/modal/modal.html'
};
});
Here is a plunker I did for this
https://embed.plnkr.co/UwjBIqTh5fNlAcbIs6TS/

Directive to directive scope

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

How to write an angularjs directive that makes use of both scope and attributes and refer it thru compiled partial?

I want to write a directive which takes advantage of custom attributes, as follows:
<plant-stages
title="Exploration<br/>du cycle de<br/>développement<br/>de la plante"
></plant-stages>
The controller is currently as follows:
app.directive('plantStages', function () {
return {
restrict: 'AE',
templateUrl: 'corn.figure.plant.stages.html',
link: function (scope, element, attrs) {
scope.title = attrs.title;
}
};
});
The partial is as follows:
<figure class="cornStages">
<div>
<p>{{title}}</p>
</div>
<div ng-repeat="stage in stages">
<div class="stage{{stage.stage}}"></div>
<div>
BBCH : {{stage.bbch}}<br/>
{{stage.displayName}}
</div>
</div>
</figure>
The partial makes use of some scope model variables.
And {{title}} should support plain HTML injection out of the view which embeds it, hence should be compiled. I tried to support this but without success.
What modification should I make to have the HTML compiled?
A bonus question: when I pass the attribute in, I create a dummy title variable in the scope that persists where it should only be local. How would one make changes to handle this?
If you want to wrap HTML in your custom directive take a look at the transclude option (see docs):
module.directive('myDirective', function() {
return {
restrict: 'E',
transclude: true,
template: '<div ng-transclude></div>'
};
});
This enables you to place HTML within the directive tag which can be used in the template:
<div ng-controller="Controller">
<my-directive>
<h1>Test</h1>
</my-directive>
</div>
In case you really want to pass HTML via an attribute use ng-bind-html. This requires the ngSanitize module:
module.directive('myDirective', function () {
return {
restrict: 'E',
template: '<div ng-bind-html="title"></div>',
scope: {
title:'#'
}
};
});
I added this to your fiddle.

Bootstrap switch not working for nested directives inside ng-repeat

Please read the code below:
<div ng-repeat="item in data.items">
<list-item></list-item>
</div>
Html(list_item.html) of the above directive(listItem) is as below(have shown only a small part of html):
<div class="active-icon-list">
<switch-check status="{{ item.status }}"></switch-check>
</div>
js of the above directive is as below:
directives.directive('cusListItem', function(){
return {
replace: true,
restrict: 'E',
templateUrl: "list_item.html",
link: function($scope, iElm, iAttrs, controller) {
// -----
}
};
});
Html(switch_check.html) of the above directive(switchCheck) is as below:
<div class="make-switch switch-small switch-mini active-list-switch">
<input type="checkbox" checked ng-if="status == 'active'">
<input type="checkbox" ng-if="status == 'inactive'">
</div>
js of the above directive is as below:
directives.directive('switchCheck', function($timeout) {
var switches = [];
return {
replace: true,
restrict: 'E',
scope: { status: '#'},
templateUrl: "switch_check.html",
link: function postLink($scope, iElm, iAttrs) {
iElm['bootstrapSwitch']();
}
}
});
I have made directives for the purpose of reusability.
Since I am using bootstrapSwitch.js instead of normal checkbox, I have created a directive.
If a remove ng-if and write a traditional input tag, this works properly.
<input type="checkbox" checked>
But if I use ng-if, iElm['bootstrapSwitch'](); fails and checkbox does not appear at all.
Is there a way(an efficient one) to figure out a solution for this one?
Please tell me if my question is not clear.

Resources