How to create reusable custom directives in angular JS - angularjs

How can one create a reusable model window in AngularJS?
I'd like it to be reusable in the sense that we trigger the modal window with a hyperlink and set the popup content based on the parameter passed. For example:
<div ng-controller="SampleCtrl">
<a ng-click="toggleModal('FirstParam')">first</a>
<a ng-click="toggleModal('SecondParam')">second</a>
<a ng-click="toggleModal('ThirdParam')">Third</a>
</div>
When the first link is clicked it would display a modal containing content related to "FirstParam", etc.
I've tried custom directives and using $parent but I don't know how to pass the parameter from the toggleModal function call into the custom directive.
How can I achieve a re-usable modal window that is similar to this example?
In particular, I would like to know:
How can I pass variables from a controller function into a custom directive?
Are there any good tutorials on reusable custom directives and scopes?
Are there any other design patterns for reusable custom directives?

Checkout http://egghead.io for some basics on writing custom directives, also the angular docs on directive and compile have all the details https://docs.angularjs.org/guide/directive https://docs.angularjs.org/api/ng/service/$compile
Here's a plnkr I posted with an overridden template for the popover from ui-bootstrap. Depending on your exact use case you may be able to get away with just overriding a template or else making a directive that makes use of $modal or some other modal UI control http://plnkr.co/edit/eeiJ5re7mNdhHNDEeCQO?p=preview
// Code goes here
angular.module("myApp", ["ui.bootstrap", "ngSanitize"]).controller("TestCtrl", function($scope){
})
angular.module("template/popover/popover.html", []).run(["$templateCache", function ($templateCache) {
$templateCache.put("template/popover/popover.html",
"<div class='popover {{placement}}' ng-class='{ in: isOpen(), fade: animation() }'>" +
"<div class='arrow'></div><div class='popover-inner'>" +
"<h3 class='popover-title' ng-bind='title' ng-show='title'></h3>" +
"<div class='popover-content' ng-bind-html='content'></div>" +
"<button class='btn btn-cancel' ng-click='manualHide()'>Cancel</button>" +
"<button class='btn btn-apply' ng-click='manualHide()'>Apply</button></div></div>");
}]);

Related

Passing data dynamically to directive's transcluded controller

I have a directive for a single popup element. Depending on which of many hundreds of elements that are clicked on, I need to pass different data to the popup when it is displayed. Using a service to store/fetch the data seems like a side-band hack. Storing the data in my top-level controller and allowing the popup controller to inherit seems like poor encapsulation. I'd like to have an isolate scope in the directive and have the popup controller inherit from that. This doesn't seem to work. Any thoughts? Is $broadcast my only option?
I assume that you are working with AngularJS 1.x. I think directive Transclusion is what you are looking for. Transclusion provides a way to include markup that is bound to the parent controller inside the content area of a directive.
Check out this Plunker which is taken from the AngularJS documentation.
.directive('pane', function() {
return {
restrict: 'E',
transclude: {
'title': '?paneTitle',
'body': 'paneBody',
'footer': '?paneFooter'
},
template: '<div style="border: 1px solid black;">' +
'<div class="title" ng-transclude="title">Fallback Title</div>' +
'<div **ng-transclude="body"**></div>' +
'<div class="footer" **ng-transclude="footer"**>Fallback Footer</div>' +
'</div>'
};
<div ng-controller="ExampleController">
<input ng-model="title" aria-label="title"> <br/>
<textarea ng-model="text" aria-label="text"></textarea> <br/>
<pane>
<pane-title><a ng-href="{{link}}">{{title}}</a></pane-title>
<pane-body><p>{{text}}</p></pane-body>
</pane>
</div>
Pane is the directive. Note that the content contained in pane-title and pane-body are being compiled against the parent controller, but are displayed in the ng-transclude portions of the directive mark-up.
You should be able to use this to get your custom data into your popup without the need of a service or $broadcast.

ng-click is not working in kendo editor

I'm trying to insert an html tag with ng-click event in the kendo editor. When I get the inserted data and show in a div, ng-click is not working. The normal onclick in javascript is working fine, but ng-click is not.
The below given is the <a> tag inserted on the editor text area.
<a ng-click="testMsg()"><span>' + nodeId + '</span></a>
Any idea on how to resolve this ?
onclick is DOM native event handler but ng-click isn't. Only those functions that are registered in the scope will be available. Any built-in functions will NOT be available to ng-click without an explicit assignment to the scope as my example shows.
var app = angular.module('app1', []);
app.controller('ctrl1', ['$scope', function($scope) {
function testMsg() {
alert('Hello world');
}
$scope.goodTestMsg = testMsg;
}]);
<div ng-app="app1" ng-controller="ctrl1">
<p ng-click="goodTestMsg()">Click me will show a message</p>
<p ng-click="testMsg()">Click me should happen nothing</p>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
TL;DR: The method testMsg is not exposed to the controller scope. Try adding something like $scope.testMsg = testMsg; will solve your problem.
Instead of textarea, try using div. I am facing similar problem which was partially solved with div. However it will be inline editing as the toolbar will get hidden and it will be displayed only when you focus cursor on the div.

How to use Angular templating tags in Foundation for Apps?

I got this code from a Foundation for Apps page template here: Zurb
the code:
<div class="accordion-item" ng-class="{'is-active': active}">
<!-- {{ varialbe }} -->
<div class="accordion-title" ng-click="activate()">{{ title }}</div>
<div class="accordion-content" ng-transclude></div>
</div>
I realize it's easy enough to use an accordion but that's really not my question. After a little research I found that the above code is using Angular tag templating. However, I'm not sure how I can use this in my code Or why I would. This has to do with dynamically naming the title for what will be an active item in the accordion? In what scenario would I define the title variable being used above? Should the content simply be placed in the div?
using foundation template in your own app.
This is the code from foundation-apps.
$templateCache.put('components/accordion/accordion-item.html',
'<div class="accordion-item" ng-class="{\'is-active\': active}">\n' +
' <div class="accordion-title" ng-click="activate()">{{ title }}</div>\n' +
' <div class="accordion-content" ng-transclude></div>\n' +
'</div>\n' +
'');
$templateCache.put puts the template in its memory and you can get to it with $templateCache.get('components/accordion/accordion-item.html').
If you want to use it in a directive, simply point your templateUrl to "components/accordion/accordion-item.html"
angular
.module('app')
.directive('myDirective', function() {
return {
templateUrl: 'components/accordion/accordion-item.html'
};
})
override foundation's template
if you want to use your own template with foundation's javascript, simply use $templateCache.put().
$templateCache.put('components/accordion/accordion-item.html',
'my custom template');

AngularStrap - load template file in modal template as inner content

I'm new to Angular-strap and AngularJS. I'm working on a project that is gonna be using both of them.
Scenario
My idea is to have multiple Modal template files for Angular-strap (w/ header, w/ header and footer, etc.). In some of these templates I would like to load different HTML file, so the Modal template file can be like a wrapper. The idea is that these different HTML files will have their own controllers and scopes. I would like to use those scopes in Modal template files to display specific data like: title, buttons, text of buttons, etc.
Problem
Seems like I'm not able to use both data-template-url and data-content-template (or at least it's not working on my side). I can use one of them, for example, in this case below, only data-template-url is used.
<button type="button" class="btn btn-lg btn-danger" data-animation="am-fade-and-slide-top" data-template-url="views/mb-templates/popups/dialog-simple.html" bs-modal="modal" data-content-template="views/mb-templates/dialog-inner-content.html">Custom Modal
<br />
<small>(using data-template)</small>
</button>
Questions
Is it possible to create the scenario that I've described?
If it is, how this can be done?
It should be possible if you use templateUrl to return a function, and within the function you make your conditional template selection. The Angular Directive documentation's section on a "Template-expanding directive" has a good example. Here's a link to the Template-expanding directive example's Plunker. The example's directive code looks like this:
.directive('myCustomer', function() {
return {
templateUrl: function(elem, attr){
return 'customer-'+attr.type+'.html';
}
};
});

Avoiding too many Angularjs Directives

I have the following directive:
.directive('myDirective', function() {
restrict: 'A',
templateUrl: 'app/templates/someTemplate/html',
});
in my template (someTemplate.html) I have the following:
<div>
<div>Some div</div>
<input type="button" value="button" />
</div>
I want to add behavior to the button and div. I can go the route of adding directives like so:
<div>
<div div-click>Some div</div>
<input type="button" value="button" button-click />
</div>
and adding more directives and binding click events via element.bind(... but is there a best practice? Should I be adding behavior in the 'myDirective' containing those elements? via jQuery or jQlite . The clickable elements inside the template are not meant to be resuable..so should I just use jQuery to find those elements and bind event listeners to them? I can see how their can be a directives explosion by constantly using the directive route, what is the best practice?
The question for me would be on what exactly the directives should be for.
It sounds to me, as if you are trying to wrap functionality that you know from other frameworks like jQuery into directives. this leads to stuff like:
var app = angular.module("module.directives", []);
app.directive('myDirective', function() {
restrict: 'A',
templateUrl: 'app/templates/someTemplate/html',
link: function(scope, el) {
el.on("click", function() { console.log(42); });
}
});
While certainly possible, this is (at least for me) considered "bad" style.
The difference with Angular is, that it does not use the DOM as the "Model" part of the framework, like jQuery or Prototype do. Coming from these libraries this is something to wrap your head around, but actually, for starters, it boils down to this:
Work with the scope and let the changes to the scope be reflected in the DOM.
The reflection part is actually the short and easy one: Angular does this out of the box (i.e. "most of the time").
Reconsidering your example with the click - Angular provides excellent event handlers in the form of directives. ng-click is a very good example for this:
<div>
<div ng-click="method()">Some div</div>
<input type="button" value="button" ng-click="method2()" />
</div>
This directive takes an expression - it looks a bit like the old days, where you would bind javascript directly to elements, like this:
here
It's way different though - Angular will look for the names method and method2 on the current scope you are in. Which scope you are currently in depends on the circumstances (I heavily suggest the docs at this point)
For all of our intents and purposes, lets say, you configure a controller inside your directive from earlier:
var app = angular.module("module.directives", []);
app.directive('myDirective', function() {
restrict: 'A',
templateUrl: 'app/templates/someTemplate/html',
controller: ['$scope', function(scope) {
scope.active = false;
scope.method = function() { console.log(42); };
scope.method2 = function() { scope.active = !scope.active };
}]
});
You can define this in many places, even as late as during the link phase of a directive. You can also create an extra controller in a separate module. But let's just stick with this for a moment:
In the template - when you click on your div the scope's method will be called. Nothing fancy, just console output. method2 is a little bit more interesting - it changes the active variable on the scope. And you can use this to your advantage:
<div>
<div ng-click="method()">Some div</div>
<input type="button" value="button" ng-click="method2()" />
<span ng-show="active">Active</span>
</div>
When you click on your button, the span will be turned on and of - the ng-show directive handles this for you.
This has gotten a bit longer than expected - I hope though, that this sheds some light on the "best practises" (which are quite dependent on what you actually want to accomplish).

Resources