Angular Three Way Button with Directive - javascript

I'm looking for an answer to this question:
Multi State button like toggle in angularJS
...but using a directive. The main reason being that I'm trying to achieve isolate scope in order to create a reusable button. I have tried:
angular.module('myApp', [])
.directive('buttonToggle', function() {
return {
restrict: 'A',
scope: {
myBtnArr: "="
},
myBtnTxt: ["AND", "OR", "NOT"],
template: '<button>{{ myBtnTxt[myBtnArr] }} </button>'
}
});
With something like this in the HTML:
<div button-toggle my-btn-arr=0></div>
But Angular doesn't seem to like any flavor of this, either showing the button but not the text or throwing the cryptic a.match is not a function error. Thoughts?

You need to modify your directive to include a link function. Then place myBtnTxt on scope in there. Like so:
app.directive('buttonToggle', function() {
return {
restrict: 'A',
scope: {
myBtnArr: "="
},
template: '<button>{{myBtnTxt[myBtnArr]}}</button>',
link: function(scope){
scope.myBtnTxt = ["AND", "OR", "NOT"];
}
};
});

Related

Passing Function Arguments with Isolated and Share scopes with Angular Directives

I have 3 directive with isolate scope and share scope and I want pass a function beteween outermost a innermost directive. The outer and middle has isolate scopes and the middle with inner share the scope. Any suggest ?
Pass the functions of my controller as shown below .
<outer on-edit="helloWorld" ng-model="model" ng-repeat="items in items.objects" ></outer>
In my controller:
$scope.helloWorld = function(){
alert('Hello world');
}
My directive:
angular.module('myApp')
.directive('outer', function () {
return {
restrict: 'E',
replace: true,
scope: {
item: "=ngModel",
onEdit: '&'
},
template: '<div><middle on-edit='onEdit'></middle></div>',
controller : function($scope){
$scope.edit = function(){
$scope.onEdit()();
}
}
};
})
.directive('middle', function () {
return {
restrict: 'E',
replace: true,
scope: {
item : '=ngModel',
onEdit : '&'
},
templateUrl: '<div><inner on-edit='onEdit'></inner></div>'
};
})
.directive('inner', function () {
return {
restrict: 'E',
template: '<div><a ng-click='edit()'>Edit</a></div>'
};
})
And this not work, any ideas?
Thanks
This looks a bad design though, but in the middle directive's template you are using inner directive as follows:
<div><inner on-edit='onEdit'></inner></div>
If you look at it, inner directive has no scope, so the attribute on-edit doesn't make sense there.
If you want to use any method that is present in middle directive can be directly used in inner directive because of shared scope. Think of inner directive as a part of html written in some other html file which will be replaced at run time.
So anything you pass to middle directive is implicitly passed to inner.

Angular | Directive not passing correct value back

Hi i have created a directive and it does not pass back the correct message.
The directive is used to pass tool-tips back to the html page
this is what the html looks like
<info-text info-msg="Adding another applicant might help you to get approved."></info-text>
below is the directive
(function(){
angular.module('mainApp').directive('infoText', [function () {
return {
scope: { infoMessage: '&infoMsg' },
restrict: 'E',
replace: true,
template: '<p class="info-text"><i class="fa fa-info-circle"></i> {{infoText}}</p>',
link: function(scope, elem, attrs) {
$(elem).prev().hover(function(){
$(elem).addClass('info-hover');
}, function(){
$(elem).removeClass('info-hover');
});
}
};
}]);
}());
the message i get rendered on the page is as follows (it does send the glyphicon):
{{infoText}}
Any ideas,
thanks. Kieran.
You should not use & for this sort of binding, basically it is used for expression binding. I think one way binding (#) is efficient for what you are doing.
Also you should change directive template {{infoText}} to {{infoMessage}}
Markup
<info-text
info-msg="{{'Adding another applicant might help you to get approved.'}}"></info-text>
Directive
angular.module('mainApp').directive('infoText', [function () {
return {
scope: { infoMessage: '#infoMsg' },
restrict: 'E',
replace: true,
template: '<p class="info-text"><i class="fa fa-info-circle"></i> {{infoMessage}}</p>',
link: function(scope, elem, attrs) {
$(elem).prev().hover(function(){
$(elem).addClass('info-hover');
}, function(){
$(elem).removeClass('info-hover');
});
}
};
}]);
And making more cleaner and readable html you could place that string into some scope variable and pass that scope variable in info-msg attribute

ng-click in angular directive - pass function from root scope

Fixed the issue, here is the final fiddle that shows it working:
http://jsfiddle.net/mbaranski/tfLeexdc/
I have a directive:
var StepFormDirective = function ($timeout, $sce, dataFactory, $rootScope) {
return {
replace: false,
restrict: 'AE',
scope: {
context: "=",
title: "="
},
template: '<h3>{{title}}</h3><form id="actionForm" class="step-form"></form><button ng-click="alert()" type="button">Save</button>',
link: function (scope, elem, attrs) {
}
}
}
How do I make the alert() do something from the controller?
Here is a fiddle:
http://jsfiddle.net/mbaranski/tfLeexdc/
Angular can be twitchy, so I've built a whole new fiddle to demonstrate all of the "glue-up" pieces you need to make this work.
First, you weren't passing the properties through to the directive, so I've made that adjustment:
// You have to pass the function in as an attribute
<hello-directive list="osList" func="myFunc()"></hello-directive>
Second, you were using onclick instead of ng-click in your template, which was part of the problem, so I made that switch:
// You need to use "ng-click" instead of "onclick"
template: '<h3>{{list}}</h3><button ng-click="func()" type="button">Button</button>',
And lastly, you need to bind the function in the scope of the directive, and then call it by the bound name:
scope: {
list: "=",
// Bind the function as a function to the attribute from the directive
func: "&"
},
Here's a Working Fiddle
All of this glued up together looks like this:
HTML
<div ng-controller="MyCtrl">
Hello, {{name}}!
<hello-directive list="osList" func="myFunc()"></hello-directive>
</div>
Javascript
var myApp = angular.module('myApp', []);
function MyCtrl($scope) {
$scope.name = 'Angular Directive';
$scope.osList = "Original value";
$scope.stuffFromController = {};
$scope.myFunc = function(){ alert("Function in controller");};
};
var HelloDirective = function() {
return {
scope: {
list: "=",
func: "&"
}, // use a new isolated scope
restrict: 'AE',
replace: false,
template: '<h3>{{list}}</h3><button ng-click="func()" type="button">Button</button>',
link: function(scope, elem, attrs) {
}
};
};
myApp.directive("helloDirective", HelloDirective);
If you'd like to execute a function defined somewhere else, make sure you pass it in by the scope directive attribute.
Here you can do:
scope: {
context: '=',
title: '=',
alert='&' // '&' is for functions
}
In the place where you using the directive, you'll pass the "expression" of the function (meaning not just the function, but the actual invocation of the function you want to happen when the click occurs.
<step-form-directive alert="alert()" title=".." context=".."></step-form-directive>

Process scope object in Angular directive

I have an element directive that looks like this:
app.directive("ngArticle", [function(){
return {
restrict: "E",
replace: true,
templateUrl: "/partials/article.html",
scope: { article: "=article" }
}
}]);
And used like this:
<ng-article data-article="{{object goes here}}"></ng-article>
This works absolutely fine. But now, I want to access part of the object passed through the directive and process it before it renders out. For example, if the object inside data-article looks like this:
{
category: "Going Out",
id: "1234"
}
In the directive, I want to access the category value, replace whitespace with hyphens and change it all to lowercase. Is this possible within the directive?
I don't want to do this in a controller because the directive is used across multiple controllers.
You can access your object before it rendered using link function within directive:
app.directive("ngArticle", [function(){
return {
restrict: "E",
replace: true,
templateUrl: "/partials/article.html",
scope: { article: "=article" },
link(scope, element, attrs) {
...
}
}
}]);
Also, scope is not interpolated before the linking function so you can watch your article object:
scope.$watch('article', function() {
// do your transformations here
}
Remember to clean up your watchers before your directive destroy, this can prevent memory leaks
scope.$on('$destroy', function () {
// clean up watchers here
});

ng-repeat inside custom directive

So I'm struggling with passing the scope to my custom directives.
Here's my html
<li ng-repeat="post in posts | filter:search | orderBy:sortField:reverse" class="archives-listing" ng-class="{'last-border':$last}">
<archive-notes></archive-notes>
</li>
Here's my directives
app.directive('archiveNotes', function(){
return {
restrict: 'E',
transclude: true,
scope: {
notes: '#',
paths: '#'
},
controller: function($scope) {
},
templateUrl: '/wp-content/themes/twentythirteen/js/angular/templates/notes.html'
}
})
app.directive('archiveFolders', function(){
return {
require: '^archiveNotes',
restrict: 'E',
transclude: true,
scope: {
path: '#'
},
link: function(scope, element, attrs) {
},
templateUrl: '/wp-content/themes/twentythirteen/js/angular/templates/folders.html'
}
});
here are my templates.
notes.html
<div ng-class="{'found' : find(post.paths[$index], search)}" class="arch-notes">
<div ng-bind-html="is_NotesEmpty(post.notes)">{{post.notes}}</div>
<archive-folders></archive-folders>
</div>
folders.html
<div ng-repeat="path in post.paths | filter:search track by $index" ng-transclude>
<span class="arch-paths">{{path}}</span>
</div>
I left several things blank i.e controller and link because at this point i was just trying to figure how to make everything show up first before i start manipulating the DOM
I followed the example in the angularjs documentation, and it got me this far. I guess cant seem to figure out how to access the scope?
Any help is appreciated.
Based on your templates, it seems that your archiveNotes directive definition should actually look like this:
app.directive('archiveNotes', function(){
return {
...
scope: {
post: '='
},
...
}
})
To get the post variable passed in from ng-repeat's scope, you also need to set the post attribute on the directive's element:
<archive-notes post="post"></archive-notes>
Similarly, you need to set it on the child directive:
app.directive('archiveFolders', function(){
return {
...
scope: {
post: '='
},
...
}
});
... and change your notes.html template:
<archive-folders post="post"></archive-folders>
Isolate scope is sort of like a firewall, where you can set exceptions, in this case on specific scope variables. All we're doing here is setting those exceptions in the directive definitions, then passing them through using the attribute on the elements.
These videos by John Lindquist really shed some light on isolate scope for me:
Understanding Isolate Scope
Isolate Scope '#'
Isolate Scope '='
Isolate Scope '&'

Categories