Applying angularjs ng-repeat to owl-carousel - javascript

<div class="owl-carousel">
<div ng-repeat="items in itemlist">
<img ng-src="{{items.imageUrl}}" />
</div>
<div>
<img src="http://placehold.it/350x150" />
</div>
</div>
View carousel here: Owl-carousel2
I'm running into an issue where whenever the ng-repeat directive is applied to carousel the items are stacked vertically instead of being layout horizontally.
If I leave out ng-repeat and use static items then it works as it should.
Is there a directive I can write and apply to owl-carousel to maintain the layout?
Also what is about ng-repeat that is causing the carousel to break?
Is angular somehow stripping the owl-carousel classes applied to the carousel?
Note* If build the list manually then iterate through and append the elements using :
var div = document.createElement('div');
var anchor = document.createElement('a');
var img = document.createElement('img');
.....
carousel.appendChild(div);
then call the owl.owlCarousel({..}) It works, not sure if this is the best work around because ng-repeat makes everything bit easier.
I discovered a hack , if I wrap the owl init in a timeout then ng-repat works.
setTimeout(function(){
...call owl init now
},1000);
<link rel="stylesheet" href="css/owl.carousel.css"/>
<link rel="stylesheet" href="css/owl.theme.default.min.css"/>
.....
<script src="/js/lib/owl.carousel.min.js"></script>
<script>
$(document).ready(function() {
var owl = $('.owl-carousel');
owl.owlCarousel({
.....
});
owl.on('mousewheel', '.owl-stage', function(e) {
if (e.deltaY > 0) {
owl.trigger('next.owl');
} else {
owl.trigger('prev.owl');
}
e.preventDefault();
});
})
</script>

Was able to modify a directive from DTing on another post to get it working with multiple carousels on the same page. Here is a working plnkr
-- Edit --
Have another plnkr to give an example on how to add an item. Doing a reinit() did not work cause any time the owl carousel is destroyed it loses the data- elements and can never initialize again.
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.items1 = [1,2,3,4,5];
$scope.items2 = [1,2,3,4,5,6,7,8,9,10];
}).directive("owlCarousel", function() {
return {
restrict: 'E',
transclude: false,
link: function (scope) {
scope.initCarousel = function(element) {
// provide any default options you want
var defaultOptions = {
};
var customOptions = scope.$eval($(element).attr('data-options'));
// combine the two options objects
for(var key in customOptions) {
defaultOptions[key] = customOptions[key];
}
// init carousel
$(element).owlCarousel(defaultOptions);
};
}
};
})
.directive('owlCarouselItem', [function() {
return {
restrict: 'A',
transclude: false,
link: function(scope, element) {
// wait for the last item in the ng-repeat then call init
if(scope.$last) {
scope.initCarousel(element.parent());
}
}
};
}]);
Here is the HTML
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/owl-carousel/1.3.3/owl.carousel.min.css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/owl-carousel/1.3.3/owl.theme.min.css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/owl-carousel/1.3.3/owl.transitions.min.css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/owl-carousel/1.3.3/owl.carousel.min.js" />
<script data-require="angular.js#1.3.x" src="https://code.angularjs.org/1.3.15/angular.js" data-semver="1.3.15"></script>
<script data-require="jquery#2.1.3" data-semver="2.1.3" src="http://code.jquery.com/jquery-2.1.3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/owl-carousel/1.3.3/owl.carousel.min.js"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<data-owl-carousel class="owl-carousel" data-options="{navigation: true, pagination: false, rewindNav : false}">
<div owl-carousel-item="" ng-repeat="item in ::items1" class="item">
<p>{{::item}}</p>
</div>
</data-owl-carousel>
<data-owl-carousel class="owl-carousel" data-options="{navigation: false, pagination: true, rewindNav : false}">
<div owl-carousel-item="" ng-repeat="item in ::items2" class="item">
<p>{{::item}}</p>
</div>
</data-owl-carousel>
</body>
</html>

I tried fiddling around with different directives but had no luck until I discovered this, it might a bit of a hack fix, but it works nonetheless.
Here is my div setup:
<div ng-repeat="item in mediaitems">
<img ng-src="item.imageurl" />
</div>
$scope.mediaitems is generated using ajax call. I found that if I delayed the owl initialization until my list was populated then it would render it properly. Also if you decide you want update you list dynamically just call the setupcarousel function (look below) after the list has been populated and it will re-init the carousel.
Note that carousel init is in an external file within an anonymous function. That's just how I choosed to set it up, you can have yours in-line or however you please.
In my controller I had something like this :
$scope.populate = function(){
$timeout(function(){
$scope.mediaitems = returnedlist; //list of items retrun from server
utils.setupcarousel(); //call owl initialization
});
};
var utils = (function(){
var setupcarousel = function(){
console.log('setting up carousel..');
var owl = $('.owl-carousel');
if(typeof owl.data('owlCarousel') != 'undefined'){
owl.data('owlCarousel').destroy();
owl.removeClass('owl-carousel');
}
owl.owlCarousel({
loop: false,
nav: true,
margin: 10,
responsive: {
0: {items: 3 },
600: {items: 5},
960: { items: 8},
1200:{items: 10},
1600:{items: 12}
}
});
};
return{
....
}
})();

The Angular UI Team has put together a set of bootstrap components implemented as angular directives. They are super sleek and fast to implement, and because they are directives, you don't run into issues with using jquery in an angular project. One of the directives is a carousel. You can find it here and here. I messed around with carousels for a long time with angular. I got the owl to work after some annoying tinkering, but AngularUI's implementation is much easier.

This is the same answer as mentioned by JKOlaf. However I've added responsive behaviour to it which provides better UX.
2 major improvements:
1. Fully responsive code resulting better UX in different devices.
2. The "autoHeight" property is handled now resulting smaller thumbnail of the images.
Code goes below:
Was able to modify a directive from DTing on another post to get it working with multiple carousels on the same page. Here is a working plnkr
-- Edit -- Have another plnkr to give an example on how to add an item. Doing a reinit() did not work cause any time the owl carousel is destroyed it loses the data- elements and can never initialize again.
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.items1 = [1,2,3,4,5];
$scope.items2 = [1,2,3,4,5,6,7,8,9,10];
}).directive("owlCarousel", function() {
return {
restrict: 'E',
transclude: false,
link: function (scope) {
scope.initCarousel = function(element) {
// provide any default options you want
var defaultOptions = {
};
var customOptions = scope.$eval($(element).attr('data-options'));
// combine the two options objects
for(var key in customOptions) {
defaultOptions[key] = customOptions[key];
}
defaultOptions['responsive'] = {
0: {
items: 1
},
600: {
items: 3
},
1000: {
items: 6
}
};
// init carousel
$(element).owlCarousel(defaultOptions);
};
}
};
})
.directive('owlCarouselItem', [function() {
return {
restrict: 'A',
transclude: false,
link: function(scope, element) {
// wait for the last item in the ng-repeat then call init
if(scope.$last) {
scope.initCarousel(element.parent());
}
}
};
}]);
You can change the responsive item counts as per your requirement. Set it to a smaller value for larger thumbnail of images.
Hope this will help somebody and Thanks to the original answer provider.

Related

Using carousel and an updated Knockout foreach

I have this small project I'm working at, and it's my first time ever using KnockoutJS (and a long while since I used javascript).
Any javascript carousel works with foreach, until the array is updated. I have already tried using Glider, Slick and Owl plugins and they all end up doing the same thing:
Document starts, foreach initiates, populates the carousel with cards fetched from URL. OK
Using the <select>, I change the option which will return a different API URL based on it. OK
The foreach is restarted, the new content is thrown in the data-bind, but the last carousel remains in HTML, working (?)
The new content doesn't work well with the carousel, as the items show stacked on top of each other.
The fourth step actually happened long before I understood what happened with carousels and lifecycle from KO; I had to use a handleBinding to start the plugin function after the foreach was made. Problem is, when the foreach is updated, KO won't restart the whole view, just what's inside of it, so the handleBinding is ignored.
Also, I can't explain why, in the 3rd step, the last carousel keeps there.
Code:
Select
<div id="select-regiao" data-bind="with: localizacoes" class="form-group mb-0">
<select id="selected-option" data-bind="options: planosPorLocalizacao, value: planosSelecionados" class="custom-select select-oi mr-2">
</select>
</div>
Carousel, foreach, card...
<div class="owl-carousel owl-theme" data-bind="foreach: planos, carousel">
<!-- #region card do plano -->
<div class="mt-4 m-2">
<div class="card card-plano">
[... card content... ]
</div>
</div>
<!-- #endregion -->
</div>
Before </body>
<script src="assets/js/jquery-3.3.1.slim.min.js" type="text/javascript"></script>
<script src="assets/js/popper.min.js" type="text/javascript"></script>
<script src="assets/js/bootstrap.min.js" type="text/javascript"></script>
<script src='assets/bower_components/knockout/dist/knockout.js' type='text/javascript'></script>
<script src="assets/js/ko-models.js" type="text/javascript"></script>
<script src="assets/js/owl-2-2.3.4/dist/owl.carousel.min.js"></script>
My ko-models.js:
$(document).ready(function() {
apiUrl = "link";
localizacoes = {
planosPorLocalizacao: ["Todos", "Rio de Janeiro, RJ", "São Paulo, SP"],
planosSelecionados: ["Todos"]
};
planos = [];
var vm = null;
var select = document.getElementById("selected-option");
select.addEventListener("change", function() {
if (select.value == "Rio de Janeiro, RJ") {
apiUrl = "other link";
} else if (select.value == "São Paulo, SP") {
apiUrl = "other link";
} else if (select.value == "Todos") {
apiUrl = "link";
}
console.log(select.value);
MostraPlanos();
});
function MostraPlanos() {
fetch(apiUrl).then(function(next) {
next.json().then(function(res) {
// ko.cleanNode({planos: res});
res.forEach(el => {
el.dependentePreco = el["dependente-preco"];
el.precoReal = el["preco"].split(",")[0];
el.precoCentavo = el["preco"].split(",")[1];
});
planos = res;
if(vm == null) {
vm = {
planos: ko.observable(planos),
localizacoes
};
ko.applyBindings(vm);
}
else{
vm.planos(planos);
vm.gliderCarousel();
}
});
});
}
MostraPlanos();
ko.bindingHandlers.carousel = {
update: function() {
$(".owl-carousel").owlCarousel({
loop:true,
margin:10,
nav:true,
responsive:{
0:{
items:1
},
600:{
items:3
},
1000:{
items:5
}
}
});
}
}
});
I found out the problem was the way the plugins used to track the carousel. Glider, for instance, was the last one I tried and spent most time at.
From the possible options in Glider.js:
addTrack
Type: Boolean
Default: true
Whether or not Glider.js should wrap it's children with a
'glider-track' .
NOTE: If false, Glider.js will assume that the
'glider-track' element has been added manually. All slides must be
children of the track element.
Adding .glider-track to the outer div (parent of cards) and setting addTrack: false in JS solved the issue!

Managing Multiple Counters in Angularjs

Within my application I need to use of multiple counters on the same page. Each of these counters will count from 0 to 100%, back to 0, and again to 100%.
I am using interval to accomplish this using the below simplified block of code
$interval(function() {
if (data[counter] < 100) {
data[counter] = data[counter] + interval;
} else {
data[counter] = 0;
}
}, 1000);
The requirements I am attempting to solve for are:
The amount of counters on the page may vary depending on results from a DB
Based on events, any particular counter may be started or stopped
The counters must be independently defined to facilitate unique counting intervals
I feel the best approach would be to create an independent block of code that could be executed when I expect counting to begin and a block where I can execute a stop command.
My first attempt at this was to create a service within Angular. It worked great for the first counter and solved for the last 2 requirements, however because of Angular treating services as singletons it did not allow for multiple independent counters on a page.
My questions is looking for direction on the best way to approach this. I've seen recommendations of creating services as APIs, but I also see the potential of using directives. Does anyone have recommendations?
Here is an answer based on directives. I split up the actual counter from the GUI. So, you could use any GUI you like.
The counter and GUI together look like this:
<counter count="counter1Count" api="counter1Api"></counter>
<counter-gui count="{{counter1Count}}" api="counter1Api"></counter-gui>
Notice how using the same variable links them together. Check out the code snippet for the full example:
var app = angular.module('myApp',[]);
app.controller('MyCtrl', ['$scope', function($scope) {
$scope.counter1Api={};
}]);
app.directive('counterGui',function(){
return {
restrict: 'E',
template : '<h1>{{count}}</h1>' +
'<button ng-click="api.start()" class="btn btn-default" type="button">Start</button>' +
'<button ng-click="api.stop()" class="btn btn-default" type="button">Stop</button>' +
'<button ng-click="api.reset()" class="btn btn-default" type="button">Reset</button>',
scope: {
count : "#",
api : "="
}
};
});
app.directive('counter',function(){
return {
restrict: 'E',
controller: ['$scope','$interval', function myCounterController($scope,$interval) {
var intervalPromise= null;
reset();
function reset() {
$scope.count= 0;
console.log("reset",$scope.count);
}
function start() {
// Make sure the timer isn't already running.
if (!intervalPromise){
intervalPromise= $interval(function() {
if ($scope.count < 100) {
$scope.count++;
} else {
$scope.count = 0;
}
}, 1000);
}
}
function stop() {
if (intervalPromise) {
$interval.cancel(intervalPromise);
intervalPromise = null;
}
}
$scope.api={
reset : reset,
start : start,
stop : stop
};
}],
scope: {
count : "=",
api : "="
}
};
});
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>AngularJS Counter Example</title>
<!-- AngularJS -->
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.js"></script>
</head>
<body ng-app="myApp">
<div ng-controller="MyCtrl">
<h1>Counter 1</h1>
<counter count="counter1Count" api="counter1Api"></counter>
<counter-gui count="{{counter1Count}}" api="counter1Api"></counter-gui>
<h1>Counter 2</h1>
<counter count="counter2Count" api="counter2Api"></counter>
<counter-gui count="{{counter2Count}}" api="counter2Api"></counter-gui>
<h1>Counter 3</h1>
<p>Two GUIs displaying the same counter.</p>
<counter count="counter3Count" api="counter3Api"></counter>
<counter-gui count="{{counter3Count}}" api="counter3Api"></counter-gui>
<counter-gui count="{{counter3Count}}" api="counter3Api"></counter-gui>
</div>
</body>
</html>

Unable to get sparklines working with dynamic data in AngularJS version of SmartAdmin template

I am working on a project using the AngularJS version of the SmartAdmin Bootstrap template available here.
As part of the project, I need to add sparklines to several of the pages. I have it working just fine with static data such as:
<div id="revenue-chart" class="sparkline no-padding bg-dark-orange"
data-sparkline-type="line"
data-fill-color="#ffc65d"
data-sparkline-line-color="#ffc65d"
data-sparkline-spotradius="0"
data-sparkline-width="100%"
data-sparkline-height="180px"> 90,60,50,75,30,42,19,100,99,76,89,65 </div>
The SmartAdmin template provides a way to implement the jQuery sparklines without direct manipulation of the javascript. That is the reason for the data-* directives.
However, when I switch the code to the following, it no longer displays a graph.
<div id="revenue-chart" class="sparkline no-padding bg-dark-orange"
data-sparkline-type="line"
data-fill-color="#ffc65d"
data-sparkline-line-color="#ffc65d"
data-sparkline-spotradius="0"
data-sparkline-width="100%"
data-sparkline-height="180px"> {{revenue.points}} </div>
I added a <div> {{revenue.points}} </div> to the page, and it prints the values correctly, so I know the values are being passed to the page.
My guess is that the graph is being rendered before the Angular code is processed thereby showing no data for the graph to display. Is there some code I can add to trigger the graph to be redrawn after the Angular substitution occurs? Or is it possible to delay drawing the graph until after the substitution occurs?
I am new to both Angular and Bootstrap, so I am certain I am missing something, I just am not sure where else to look on this.
The template uses Bootstrap 3.3 and Angular 1.4.2.
I ended up creating a directive. The completed directive is:
"use strict";
angular.module('app.tvidash').directive('inlineRevenueChart', function(){
return {
restrict: 'A',
require: 'ngModel',
scope: {
data: '='
},
link: function(scope, element, attrs, ngModel){
var opts = {};
opts.type = attrs.type || 'line';
scope.$watch(attrs.ngModel, function() {
render();
});
scope.$watch(attrs.opts, function(){
render();
});
var render = function() {
var model;
if(attrs.opts) angular.extend(opts, angular.fromJson(attrs.opts));
console.log(opts);
// Trim trailing comma if we are a string
angular.isString(ngModel.$viewValue) ? model = ngModel.$viewValue.replace(/(^,)|(,$)/g, "") : model = ngModel.$viewValue;
var data;
// Make sure we have an array of numbers
angular.isArray(model) ? data = model : data = model.split(',');
$(element).sparkline(data, opts);
};
}
}
});
And the accompanying div tag:
<div id="revenue-chart" class="db-chart sparkline no-padding bg-dark-orange"
inline-revenue-chart
ng-model="revenue.points"
opts="{{ {type: 'line', width: '100%', height: '180px', lineColor: '#ffc65d', fillColor: '#ffc65d', spotRadius: 0.1, drawNormalOnTop: false} }}"></div>
I am sure there is a better way to do the options, but this is working as I need it for now. Thanks to those that took a look.

Memory leak with angularJS and jQuery / angular bootstrap

I have a ng-repeat list that updates every minute. Its a list of cards that contains stuff like title, description, dates and so on.
In those cards there's also a angular-ui-bootstrap popover which i use to display comments on those cards.
When the list updates, the popover will keep some reference which creates a lot of detached dom elements.
Heres some of the code.
The directive i use.
.directive('mypopover', function ($compile, $templateCache) {
var getTemplate = function (contentType) {
var template = '';
switch (contentType) {
case 'user':
template = $templateCache.get("templateId.html");
break;
}
return template;
}
return {
restrict: "A",
link: function ($scope, element, attrs) {
var popOverContent;
popOverContent = getTemplate("user");
popOverContent = $compile("<span>" + popOverContent + "</span>")($scope);
var options = {
content: popOverContent,
placement: "bottom",
html: true,
trigger: "manual",
selector: '.fa-comment',
date: $scope.date,
animation: true
};
$(element).popover(options).on("mouseenter", function () {
var _this = this;
$(this).popover("show");
$('.popover').linkify();
$(".popover").on("mouseleave", function () {
$(this).popover('destroy');
$('.popover').remove();
});
}).on("mouseleave", function () {
var _this = this;
setTimeout(function () {
if (!$(".popover:hover").length) {
$(this).popover('destroy');
$('.popover').remove();
}
}, 350);
});
var destroy = function () {
$(element).popover('destroy');
}
$scope.$on("$destroy", function () {
destroy();
});
}
}
})
from the html..
The bo-something is the just a one way bind i use instead of the normal double bind from angular
<a bo-href="c.ShortUrl" target="_blank" bindonce ng-repeat="c in cards | filter:searchText | limitTo: limitValue[$index] track by c.Id">
<div class="panel detachable-card">
<div class="panel-body" bo-class="{redLabel: c.RedLabel, orangeLabel: c.OrangeLabel}">
<!-- Comments if any -->
<script type="text/ng-template" id="templateId.html">
<div ng-repeat="comment in c.Comment track by $index">
<strong style="margin-bottom: 20px; color:#bbbbbb; white-space: pre-wrap;">{{c.CommentMember[$index]}}</strong>
<br />
<span style="white-space: pre-wrap;">{{comment}}</span>
<hr />
</div>
</script>
<span bo-if="c.Comment" data-container="body" mypopover style="float:right"><i class="fa fa-comment fa-lg"></i></span>
<!-- Card info -->
<strong style="font-size:12px; color:#999999"><span bo-if="!c.BoardNameOverride" bo-text="c.NameBoard"></span> <span bo-if="c.BoardNameOverride" bo-text="c.BoardNameOverride"></span></strong>
<br />
<strong bo-text="c.Name"></strong>
<br />
<span bo-if="c.Desc" bo-text="c.Desc"><br /></span>
</div>
</div>
</a>
Heres a heap-snapshot of the site after one update.
http://i.stack.imgur.com/V4U1O.png
So Im fairly bad at javascript in general, and i have my doubts about the directive. I would have thought that the .popover('destroy') would remove the reference, but it doesnt seem to..
Any help is greatly appreciated..
Why are you constantly destroying the popup over and over? There is no need to destroy the popup every time the mouse is moved. Just show and hide the popup. It's much nicer on memory than constantly destroying and recreating the popup.
But, what you may not realize is that bootstrap components don't play well with AngularJS. Bootstrap components weren't architected in ways that allow the content within them to be updated easily which poses problems when you use them with AngularJS because the update model is built into the framework. And that's why the AngularUI project rewrote their Javascript components from the ground up in AngularJS so they behave as you would expect. I think you'll find those much easier to use.
http://angular-ui.github.io/bootstrap/
If you are using bootstrap 2.3 AngularUI v0.8 was the last version supporting bootstrap v2.3.

How to manipulate styles of directive in AngularJS?

I'm writing a component using AngularJS and AngularJS directives.
I'm doing something like this:
var MyApp = angular.module('MyApp', []);
MyApp.directive('myTag', function() {
return { /* Some logic here*/ }
});
I want to be able to change style of my component (using CSS), something like this:
<my-tag class="MyClass"></my-tag>
Besides this I want to be able to manipulate all elements style inside my
component (HTML markup inside of my-tag).
Do you have any advice or useful examples how to manipulate the style properties of custom tags using AngularJS?
This should do the trick.
var MyApp = angular.module('MyApp', []);
MyApp.directive('myTag', function() {
return {
link: function(scope, element, attributes){
element.addClass('MyClass');
}
}
});
This is how AngularJS adds core CSS styles:
angular.element(document).find('head').prepend('<style type="text/css">#charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide{display:none !important;}ng\\:form{display:block;}</style>');
You can find this code in angular.js v1.2.0-rc.2.
EDIT
In a custom directive, I use this solution to bundle CSS stylesheets in the directive:
var outputColorCSS = {
selector: 'span.ouput-color',
rules: [
'display: inline-block',
'height: 1em',
'width: 5em',
'background: transparent',
'border: 3px solid black',
'text-align: center',
'font-weight: bold',
'font-size: 0.8em'
]
};
var outputColorStyleSheet = outputColorCSS.selector + outputColorCSS.rules.join(';');
angular.element(document).find('head').prepend('<style type="text/css">' + outputColorStyleSheet + '</style>');
Then you can use class="ouput-color" in your directive templates.
I found it very clean and useful.
I'm a little late to the party, but why aren't you all using the built in .css() method?
just use:
link: function(scope, elem, attr, ctrl)
{
elem.css({'display': 'block', 'height': '100%', 'width': '100%'});
}
or whatever css you desire.
You can put custom styles in a directive's declaration with a parameter, just like you exemplified.
In order to declare a style like that, you have to define a variable to hold the custom styles:
scope: {
myClass: '#myClass'
},
And then set that parameter in the directive's template, like this:
<my-tag my-class="CustomClass"></my-tag>
Finally, in the template of the directive itself, reference that class:
<h1 class="{{myClass}}">{{myContent}}</h1>
I made a plunker that shows how you can customize styles in a directive, check it out here .
Plunker
To manipulate the css style through an attribute directive, you could do something like this:
var app = angular.module('colorSwap', []);
app.directive('styleChanger', function() {
return {
'scope': false,
'link': function(scope, element, attrs) {
var someFunc = function(data)
{
/* does some logic */
return 'background-color:' + data;
}
var newStyle = attrs.styleChanger;
scope.$watch(newStyle, function (style) {
if (!style) {
return ;
}
attrs.$set('style', someFunc(style));
});
}
};
});
Some html:
<div ng-app="colorSwap">
<input type="txt" ng-init="colorName= 'yellow'" ng-model="colorName" />
<div style-changer="colorName">this is the div content</div>
</div>
To make an element directive, change it's own style, something like this:
app.directive('elementWithStyle', function() {
return {
'restrict' : 'E',
'scope': {},
'controller': function($scope) {
$scope.someStyle = 'Cyan';
$scope.someFunc = function() { $scope.someStyle = 'purple' };
},
'template': '<div style="background: {{someStyle}}" ng-click="someFunc()"> click me to change colors </div>'
}
});
And the html:
<div ng-app="colorSwap">
<element-with-style>123</element-with-style>
</div>
I hope this helps. The rest of the answers cover class manipulation more or less.
For css manipulation inside of the childs of your directive try this:
var MyApp = angular.module('MyApp', []);
MyApp.directive('myTag', function() {
return {
link: function(scope, element, attr){
// For your tag
element.addClass('MyClass');
// For elements inside your directive tag
var tag_childs = element[0].childNodes;
for(var i = 0; i < element[0].childElementCount; i++){
tag_childs[i].style.height = '70px';
}
}
}
});
Here is an example, please note that this is probably not the best use of AngularJS, being declarative, you would likely want to just put the classes on the markup. However, just so you understand what's going on, let me demonstrate a simple directive to do what you first asked.
var MyApp = angular.module('MyApp', []);
MyApp.directive('myTag', function($compile) {
return {
restrict: 'E', // this means it will be an element
link: function(scope, element, attrs, ctrl) {
// First, I included the $compile service because it will be needed
// to compile any markup you want to return to the element.
// 1. Add the class, as you wanted
element.addClass('MyClass');
// 2. Add markup
var html = '<div>Hello World</div>';
//Compile it and add it back
$compile(html)(scope);
element.html(html);
}
};
});
Finally, on your markup, you just put this in:
<my-tag />
app.directive('bookslist', function() {
return {
scope: true,
templateUrl: 'templates/bookslist.html',
restrict: "E",
controller: function($scope){
},
link: function(scope, element, attributes){
element.addClass('customClass');
}
}
});
.customClass table{
background: tan;
}
.customClass td{
border: 1px solid #ddd;
}
<!DOCTYPE html>
<html>
<head>
<link href="app.css" rel="stylesheet">
<script type="text/javascript" src="angular.min.js"></script>
<script type="text/javascript" src="app.js"></script>
<title>Task</title>
</head>
<body ng-app="app">
<div ng-controller="myCtrl">
<bookslist></bookslist>
</div>
</body>
</html>
Angular
app.directive("time",function(){
var directive={};
directive.restrict="A";
directive.link=function(scope,element,attr,ctrl){
element.css({
backgroundColor:'#ead333'
});
}
var time=new Date().toTimeString();
directive.template=time;
return directive;
});
HTML
The times is <span time></span>
I didn't found the perfect solution just yet, but I'm following John Papa's styling of controllers even with directives:
the directive is a folder (directiveName.directive)
3 files inside: directiveName.directive.js, directiveName.template.html, directiveName.styles.css
use templateUrl when declaring the directive. The template has the link to the css file, as usual
I found it to be very clean and follows a pattern. The bad side of it is that you create several <link> tags near the directives in the rendered HTML (not seem to be a issue still, though). Check out this comment too.
That being said, take a look at Angular 1.5 component's. It's relatively new and has a much better approach. Now I use directives only for DOM manipulation (not reusability as components).

Categories