Showing posts with label angularjs-directive. Show all posts
Showing posts with label angularjs-directive. Show all posts

Thursday, May 3, 2018

how to populate the end time in timepicker dropdown based on selected start time

Leave a Comment

I'm trying to set operation hours for a business. End time should be 1hr after the start time and the endtime dropdown should not show values on/ before start time. Here is my fiddle

(function() {   'use strict';   angular.module('agencyApp', [])     .controller('HoursController', HoursController)     .directive('timePicker', timePicker);    function HoursController() { var vm = this;  vm.print = function() {   console.log(vm); }; vm.setClosed = setClosed; vm.setAllWeekdayClosed = setAllWeekdayClosed; vm.weekdays = [{   Day: 'Monday',   fromTime: "Select",   toTime: "Select" }, {   Day: 'Tuesday',   fromTime: "Select",   toTime: "Select" }, {   Day: 'Wednesday',   fromTime: "Select",   toTime: "Select" }, {   Day: 'Thursday',   fromTime: "Select",   toTime: "Select" }, {   Day: 'Friday',   fromTime: "Select",   toTime: "Select" }, {   Day: 'Saturday',   fromTime: "closed",   toTime: "closed" }, {   Day: 'Sunday',   fromTime: "closed",   toTime: "closed" }];  vm.closed = [   false, false, false, false, false, true, true ];  function setClosed(index) {   if (vm.closed[index]) {     vm.weekdays[index].toTime = vm.weekdays[index].fromTime = 'closed';   }   if (!vm.closed[index]) {     vm.weekdays[index].fromTime = 'Select';     vm.weekdays[index].toTime = 'Select';   } };  function setAllWeekdayClosed(index) {   if (vm.closed[index]) {     vm.weekdays[index].toTime = vm.weekdays[index].fromTime = 'closed';   }   if (!vm.closed[index]) {     vm.weekdays[index].fromTime = '7:00 am';     vm.weekdays[index].toTime = '3:00 pm';   } }; 

}

function timePicker() { var ddo = { template: '', restrict: 'E', require: 'ngModel', replace: true, compile: compileFn }; return ddo;

function compileFn(tElement, tAttrs) {   tElement.attr('ng-model', tAttrs.ngModel);   //tElement.attr('ng-disabled', tAttrs.ngModel + ' === "closed"');   return linkFn; };  function linkFn(scope, element, attrs) {   scope.timings = ['Select', '12:15 am', '12:30 am', '12:45 am',     '1:00 am', '1:15 am', '1:30 am', '1:45 am',     '2:00 am', '2:15 am', '2:30 am', '2:45 am',     '3:00 am', '3:15 am', '3:30 am', '3:45 am',     '4:00 am', '4:15 am', '4:30 am', '4:45 am',     '5:00 am', '5:15 am', '5:30 am', '5:45 am',     '6:00 am', '6:15 am', '6:30 am', '6:45 am',     '7:00 am', '7:15 am', '7:30 am', '7:45 am',     '8:00 am', '8:15 am', '8:30 am', '8:45 am',     '9:00 am', '9:15 am', '9:30 am', '9:45 am',     '10:00 am', '10:15 am', '10:30 am', '10:45 am',     '11:00 am', '11:15 am', '11:30 am', '11:45 am',     '12:00 pm', '12:15 pm', '12:30 pm', '12:45 pm',     '1:00 pm', '1:15 pm', '1:30 pm', '1:45 pm',     '2:00 pm', '2:15 pm', '2:30 pm', '2:45 pm',     '3:00 pm', '3:15 pm', '3:30 pm', '3:45 pm',     '4:00 pm', '4:15 pm', '4:30 pm', '4:45 pm',     '5:00 pm', '5:15 pm', '5:30 pm', '5:45 pm',     '6:00 pm', '6:16 pm', '6:30 pm', '6:45 pm',     '7:00 pm', '7:15 pm', '7:30 pm', '7:45 pm',     'closed'   ]; };   } })(); 

1 Answers

Answers 1

You will have to write a custom filter to filter out time options which are before selected start time.

I have done it for you. Please refer jsFiddle

angular.module('agencyApp', [])   .controller('HoursController', HoursController)   .directive('timePicker', timePicker)   .filter('timeFilter', function () {     return function (input, filterCriteria) {       if (filterCriteria && filterCriteria !== "Select") {         input = input || [];         if (filterCriteria === 'closed') {           return ['closed'];         }         var out = [];         input.forEach(function (time) {           if (moment(time, 'h:mm a').isAfter(moment(filterCriteria, 'h:mm a'))) {             out.push(time);           }         });         return out;       } else {         return input;       }     };   }) 

I have added momentjs for time comparison.

Directive template will look like this.

<select class="input-small" ng-options="time as time for time in timings | timeFilter:min"></select> 

And now directive will use min attribute to filter.

scope:{  min: '=' } 

Usage

<time-picker ng-model="vm.weekdays[$index].toTime" id="aid-to-{{$index}}" name="to" min="vm.weekdays[$index].fromTime"></time-picker> 

See the min attribute accepting fromTime

Read More

Saturday, July 8, 2017

Combining angularJS and d3.js: Refreshing a plot after submitting new input parameters

Leave a Comment

I would like that after I click on the submit button with new values inside input fields, my network d3.js plot would get updated based on the new graph generated by new input values. Here in the following, you can find my example code:

GenerateGraph.js This file contains a bunch of functions which generate a graph (randomGraph) based on the submitted input values. Then the graph is needed to get refreshed in the browser.

function degree(node,list){   var deg=new Array();   for (var i=0; i<node.length; i++){     var count=0;     for (var j=0; j<list.length; j++){         if (node[i]==list[j][0] || node[i]==list[j][1]){           count++;         }     }     deg.push(count);   }   return deg; } function randomGraph (n, m) { //creates a random graph on n nodes and m links   var graph={};   var nodes = d3.range(n).map(Object),       list  = randomChoose(unorderedPairs(d3.range(n)), m),       links = list.map(function (a) { return {source: a[0], target: a[1]} });   graph={   Node:nodes,   ListEdges:list,   Links:links   }   return graph; }  function randomChoose (s, k) { // returns a random k element subset of s   var a = [], i = -1, j;   while (++i < k) {     j = Math.floor(Math.random() * s.length);     a.push(s.splice(j, 1)[0]);   };   return a; }  function unorderedPairs (s) { // returns the list of all unordered pairs from s   var i = -1, a = [], j;   while (++i < s.length) {     j = i;     while (++j < s.length) a.push([s[i],s[j]])   };   return a; } 

network.html

!DOCTYPE html> <html> <head>      <meta charset="utf-8">           <meta name="viewport" content="width=device-width, initial-scale=1">      <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Tangerine">      <link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">      <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>      <title>graph</title>       <script src='http://d3js.org/d3.v3.min.js'></script>           <link rel="stylesheet" type="text/css" href="style.css">  </head> <body ng-app="myApp">      <script src="GenerateGraph.js" type="text/javascript"></script>      <script src="svgGraph.js" type="text/javascript"></script>      <h1 class="title">Simulating a network</h1>      <div id="outer" ng-controller="MainCtrl" class="col-md-6">         <network-inputs inputs="networkInputs" submit="submit(inputs)"></network-inputs>      </div>      <!--test -->      <script type="text/javascript">          //get the input parameters for plotting          angular.module("myApp", [])           .directive('networkInputs', function() {           return {                 restrict: 'E',                                 scope: {                    inputs: '<',                    submit: '&'                 },                 link : link,                                template:                '<h3 >Initialise new parameters to generate a network </h3>'+                     '<form ng-submit="submit({inputs: inputs})" class="form-inline">'+                       '<div class="form-group">'+                          '<label>Number of nodes</label>'+                          '<input type="number" min="10" class="form-control" ng-model="inputs.N" ng-required="true">'+                       '</div>'+                       '<div class="form-group">'+                          '<label>Number of links</label>'+                           '<input type="number" min="0.1" class="form-control" ng-model="inputs.m" ng-required="true">'+                       '</div>'+                       '<button style="color:black; margin: 1rem 4rem;" type="submit">Generate</button>' +                    '</form>'};          })          .factory("initialiseNetwork",function(){             var data = {                             N: 20,                             m: 50,                         };              return {                 networkInputs:data             };                 })           .controller("MainCtrl",  ['$scope','initialiseNetwork' ,function($scope,initialiseNetwork) {                 $scope.networkInputs={};                $scope.mySVG=function(){                          var graph=randomGraph($scope.networkInputs.N, $scope.networkInputs.m);                  };                 function init(){                   $scope.networkInputs=initialiseNetwork.networkInputs;                    //Run the function which generates the graph and plot it                  }                init();                 $scope.submit = function(inputs) {                     var dataObject = {                        N: inputs.N,                        m: inputs.m                    };                    //lets simply log them but you can plot or smth other                     console.log($scope.networkInputs);                  }           }]);      </script> </body>  </html> 

svgGraph.js

function link(scope,element, attrs){     //SVG size     var width = 1800,     height = 1100;      // We only need to specify the dimensions for this container.      var vis = d3.select(element[0]).append('svg')               .attr('width', width)               .attr('height', height);     var force = d3.layout.force()                       .gravity(.05)                       .distance(100)                       .charge(-100)                       .size([width, height]);         // Extract the nodes and links from the data.      scope.$watch('val',function(newVal,oldVal){                    vis.selectAll('*').remove();                    if (!newVal){                        return;                    }          var Glinks = newVal.links;         var W=degree(newVal.nodes,newVal.list);          var Gnodes = [];         var obj=newVal.nodes;         Object.keys(obj).forEach(function(key) {            Gnodes.push({"name":key, "count":W[key]});         });          //Creates the graph data structure          force.nodes(Gnodes)              .links(Glinks)              .linkDistance(function(d) {                return(0.1*Glinks.length);               })//link length              .start();          //Create all the line svgs but without locations yet         var link = vis.selectAll(".link")            .data(Glinks)            .enter().append("line")            .attr("class", "link")            .style("stroke-width","0.3px");          //Do the same with the circles for the nodes - no          var node = vis.selectAll(".node")             .data(Gnodes)             .enter().append("g")             .attr("class", "node")             .call(force.drag);         node.append("circle")              .attr("r", function(d){               return d.count*0.5;                })             .style("opacity", .3)             .style("fill", "red");         //add degree of node as text          node.append("text")             .attr("text-anchor", "middle")             .text(function(d) { return d.count })             .attr("font-family",'Raleway',"Tangerine");         //Now we are giving the SVGs co-ordinates - the force layout is generating the co-ordinates which this code is using to update the attributes of the SVG elements         force.on("tick", function () {              link.attr("x1", function (d) {                  return d.source.x;                  })                  .attr("y1", function (d) {                  return d.source.y;                  })                  .attr("x2", function (d) {                  return d.target.x;                  })                  .attr("y2", function (d) {                  return d.target.y;                  });                node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });         });           }); } 

The link in the plunker is here.

1 Answers

Answers 1

1 - In the directive, you watch val, which in controller is $scope.data, so I guess you need it for every submitted form? then just assign the data to $scope.data every submit:

$scope.submit = function(inputs) {     var dataObject = {       N: inputs.N,       m: inputs.m     };     $scope.data = randomGraph(dataObject.N, dataObject.m); } 

2 - Then, in sgvGraph.js, in the scope.watch, you use var newVal.nodes and newVal.list anf newVal.link which are all undefined because you build your object with {Node:.., Links:..., ListEdges:...}

3 - Should add novalidate in form and manage the error manually, because I cant submit with the min="0.1" with chrome

This is the working plunkr: http://embed.plnkr.co/PbynuNCPM4Jv4lPmK8eW/

Read More

Monday, May 8, 2017

Weird ngIf + $compile behavior

Leave a Comment

I'm trying to build a directive that would do the following:

  • add another directive to the element (ngSwipeRight for example)
  • add some custom behavior to the new directive.

An example would be: mySwipeBack that would add ngSwipeRight and when the user swipes over the element I would do history.back().

I tried like this:

.directive('swipe', function($compile){   return {     restrict: 'A',     compile: function(el){         // I removed all the actual logic for demo purposes         // here I would add ng-swipe-right plus a handler         el.removeAttr('swipe');         var fn = $compile(el);         return function (scope) {           fn(scope);         };    }  } }); 

But I ran into an issue with the following markup:

<div ng-if='true'>   <h1 swipe>OUTSIDE     <div ng-if="true">INSIDE</div>   </h1> </div> 

The "INSIDE" text doesn't get rendered. You can see the behavior in this jsbin: http://jsbin.com/tokofevuga/edit?html,js,output

If I remove the first ng-if, it works as intended.

Does anyone know what the reason is behind this - and if I can make it work?

Or if someone has another idea of how to achieve what I described above?

3 Answers

Answers 1

    return function (scope) {       $compile(el)(scope);     }; 

Work for me ... And the point is that in your code - you compile element immediately in directive compile, while here function is returned from compile, that will be executed somewhen later.

Answers 2

Since ng-if create its own scope, I think you just compile the element too early.

In the below code snippet, I moved compile part to link, and bind the element with the current scope of the directive itself.

var app = angular.module('jsbin', []);    app.controller('DemoCtrl', function() {    });    app.directive('swipe', function($compile) {    return {      restrict: 'A',      link: function($scope, el) {        el.removeAttr('swipe');        $compile(el)($scope);      }    }  });
<!DOCTYPE html>  <html>    <head>    <meta charset="utf-8">    <title>Angular JS</title>  </head>    <body ng-app="jsbin">    <div ng-controller="DemoCtrl">      <div ng-if='true'>        <h1 swipe>OUTSIDE          <div ng-if="true">INSIDE</div>        </h1>      </div>    </div>    <script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.0/angular.js"></script>  </body>    </html>

Answers 3

Consider this. ngIf removes the whole element and replaces it with the comment. The expression is then watched for. The subsequent changes trigger the element from between DOM to REMOVED.

Now the problem is, you have placed the block inside the compile phase. The code for whole ng-if gets replaced by a single comment.

Regarding solution, there can be many. It all depends upon what goes well with the structure and needs if your app.

I propose two:

1) Move the logic from 'compile' to 'link' phase.

link: function(el){     el.removeAttr('swipe');     var fn = $compile(el);     return function (scope) {       fn(scope);     };  }, 

2) You will need to use transclusion.

app.directive('swipe', function($compile){  return {   restrict: 'A',   compile: function(el){     el.removeAttr('swipe');     var fn = $compile(el);     return function (scope) {       fn(scope);         };    },     transclude: true,     template: "<div ng-transclude></div>"  } }); 

The following forks of your sample might guide you:

http://jsbin.com/zecujonoqi/1/edit?html,js,output

http://jsbin.com/mubodevofa/1/edit?html,js,output

Hope it helps.

Read More

Wednesday, January 4, 2017

AngularJS bootstrap popover custom directive

Leave a Comment

I have created a directive using bootstrap custom popover. That works taking an input from a user for a group name and it has two buttons for apply that value to model and show that value on tooltip and a button to close the popover. I am using popover java script events , Problem is that single popover works perfectly but when I open another one this popover not closing itself. Need help in closing other popovers while one is open. Here is the plnk showing the directive.

Here is the code

var app = angular.module('myApp',[]); app.directive('customEditPopover', function () {         return {             restrict: 'A',             template: '<span><i class="fa fa-tags" aria-hidden="true"></i></span>',             scope: {                 'myModel': '=',             },             link: function (scope, el, attrs) {                 scope.label = attrs.popoverLabel;                 var btnsave = '#' + attrs.popoverSave;                 var btncancel = '#' + attrs.popoverCancel;                 var index = attrs.index;                 $(el).tooltip({                     title: '' + (scope.myModel == undefined) ? "" : scope.myModel,                     container: 'body',                     placement: 'top'                 });                 $(el).popover({                     trigger: 'click',                     html: true,                     content: attrs.popoverHtml,                     placement: attrs.popoverPlacement,                     container: 'body'                 })                 .on('inserted.bs.popover', function (e) {                     bindevents();                     $('#popovertext' + index).val(scope.myModel);                 }).on('hidden.bs.popover', function () {                     Unbindevents();                 });                 function bindevents() {                     $(btnsave).bind('click', function () {                         var text = $('#popovertext' + index).val();                         scope.myModel = text;                         $(el).tooltip('hide')                        .attr('data-original-title', text)                        .tooltip('fixTitle')                         toggle();                     });                     $(btncancel).bind('click', function () {                         toggle();                     });                 }                 function Unbindevents() {                     $(btnsave).unbind('click');                     $(btncancel).unbind('click');                 }                 function toggle() {                     $(el).popover('hide');                     $(el).click();                 }              }         };     }); app.controller('MyController',['$scope',function($scope){   $scope.list=[     {       name:'ABC'     },      {       name:'DEF'     },      {       name:'GHI'     },      {       name:'KLM'     }     ];  }]); 

Need help closing other popover while opening next one.

2 Answers

Answers 1

You can close the other popovers when show.bs.popover is triggered as shown below : Updated Plunkr

$(el).popover({                 trigger: 'click',                 html: true,                 content: attrs.popoverHtml,                 placement: attrs.popoverPlacement,                 container: 'body'             })              .on('show.bs.popover', function () {               var siblings = $(el).parent().parent().siblings();               siblings.each(function (each){                 $(siblings[each]).find('span').popover('hide');               });             }); 

Answers 2

IMO, the most modular way would be to use a service to track all opened PopUps.

This gives multiple benefits.

  1. You can extend the service to track different types of popups, allowing you to close all or open all or do whatever you need

  2. It removes the logic from the controls and directives on keeping track of popups. Thus they can focus on 1 specific thing. You can also run multiple controllers and popups on a page allowing each controller/popup to be a discrete unit of work and not needing to worry about other controllers/popups stomping on their items.

The cons of doing this

  1. As I commented earlier you can use the angular bootstrap directives for this. Making your code "cleaner", because all this is done for you. What is not done for you is closing all active popups. You can take the same service approach I have used and just plug it into the ng-bootstrap directives.
  2. Binding/Unbinding the JQUERY events seems to become unresponsive when you click 8+ times. I do not have an answer for why that is, but the overall idea of using a service to keep track is still a valid one.

You shoudl be able to cut and paste this code into the plnkr or your dev box and it will work.

// Code goes here var app = angular.module('myApp',[]);  app.factory('popUpService', function() {   var activePopups = [];   // factory function body that constructs shinyNewServiceInstance    return {   AddPopup : function(el)   {   activePopups.push(el);   },   CloseAllPopups : function(closeFunction){   for (var i = 0; i < activePopups.length; i++)   {             closeFunction(activePopups[i])    }   }   }  }); app.directive('customEditPopover',['popUpService', function (popUpService) {         return {             restrict: 'A',             template: '<span><i class="fa fa-tags" aria-hidden="true"></i></span>',             scope: {                 'myModel': '=',             },             link: function (scope, el, attrs) {                 scope.label = attrs.popoverLabel;                 var btnsave = '#' + attrs.popoverSave;                 var btncancel = '#' + attrs.popoverCancel;                 var index = attrs.index;                 $(el).tooltip({                     title: '' + (scope.myModel == undefined) ? "" : scope.myModel,                     container: 'body',                     placement: 'top'                 });                 $(el).popover({                     trigger: 'click',                     html: true,                     content: attrs.popoverHtml,                     placement: attrs.popoverPlacement,                     container: 'body'                 })                 .on('inserted.bs.popover', function (e) {                     bindevents();                     $('#popovertext' + index).val(scope.myModel);                 }).on('hidden.bs.popover', function () {                     Unbindevents();                 });                 function bindevents() {                  CloseAll();                  popUpService.AddPopup($(el));                      $(btnsave).bind('click', function () {                         var text = $('#popovertext' + index).val();                         scope.myModel = text;                         $(el).tooltip('hide')                        .attr('data-original-title', text)                        .tooltip('fixTitle')                     closeCurrent();                     });                     $(btncancel).bind('click', function () {                     closeCurrent();                     });                 }                 function Unbindevents() {                     $(btnsave).unbind('click');                     $(btncancel).unbind('click');                 }                 function closeCurrent(){                    $(el).popover('hide');                  //   $(el).click();                 }                 function CloseAll() {                  popUpService.CloseAllPopups(function(popup){                      popup.popover('hide');                 //    popup.click();               });              }         }         }     }]); app.controller('MyController',['$scope',function($scope){   $scope.list=[     {       name:'ABC'     },      {       name:'DEF'     },      {       name:'GHI'     },      {       name:'KLM'     }     ];  }]); 
Read More

Wednesday, April 27, 2016

Using bootstrap-daterangepicker in an Angular directive

Leave a Comment

I'm having troubles using bootstrap-daterangepicker in an Angular directive. It works fine when initially loading the page, but when switching pages/states (I'm using ui-router) and going back to a page with a daterangepicker the plugin doesn't work anymore.

It throws this error:

TypeError: $(...).daterangepicker is not a function

It seems like the plugin has removed itself when changing states. Anyone an idea how to fix this?

app.directive("daterangepicker", function() {     return {       restrict: "A",       scope: false,       link: function($scope, $element, $attr){          $($element).daterangepicker({           format: "DD.MM.YYYY",           startDate: moment().subtract('days', 1),           endDate: new Date(),                     buttonClasses: ['btn-primary'],         }, function(from, to) {           $scope.date = {from: from, to: to};           $scope.$apply(); // I need apply() here to use the two-way-databinding         });       }     }   }); 

3 Answers

Answers 1

I would avoid reinventing the wheel, there is already a directive provided by several projects, use the https://angular-ui.github.io/bootstrap/

Import the module:

angular.module('myModule', ['ui.bootstrap']);

and the use it like that:

<!doctype html> <html ng-app="ui.bootstrap.demo">  <head>   <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.3/angular.js"></script>   <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.3/angular-animate.js"></script>   <script src="//angular-ui.github.io/bootstrap/ui-bootstrap-tpls-1.3.2.js"></script>   <script src="example.js"></script>   <link href="//netdna.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet"> </head>  <body>    <style>     .full button span {       background-color: limegreen;       border-radius: 32px;       color: black;     }      .partially button span {       background-color: orange;       border-radius: 32px;       color: black;     }   </style>   <div ng-controller="DatepickerPopupDemoCtrl">     <pre>Selected date is: <em>{{dt | date:'fullDate' }}</em></pre>      <h4>Popup</h4>     <div class="row">       <div class="col-md-6">         <p class="input-group">           <input type="text" class="form-control" uib-datepicker-popup="{{format}}" ng-model="dt" is-open="popup1.opened" datepicker-options="dateOptions" ng-required="true" close-text="Close" alt-input-formats="altInputFormats" />           <span class="input-group-btn">              <button type="button" class="btn btn-default" ng-click="open1()"><i class="glyphicon glyphicon-calendar"></i></button>            </span>         </p>       </div>        <div class="col-md-6">         <p class="input-group">           <input type="text" class="form-control" uib-datepicker-popup ng-model="dt" is-open="popup2.opened" datepicker-options="dateOptions" ng-required="true" close-text="Close" />           <span class="input-group-btn">              <button type="button" class="btn btn-default" ng-click="open2()"><i class="glyphicon glyphicon-calendar"></i></button>            </span>         </p>       </div>     </div>     <div class="row">       <div class="col-md-6">         <label>Format: <span class="muted-text">(manual alternate <em>{{altInputFormats[0]}}</em>)</span></label>         <select class="form-control" ng-model="format" ng-options="f for f in formats">           <option></option>         </select>       </div>     </div>      <hr />     <button type="button" class="btn btn-sm btn-info" ng-click="today()">Today</button>     <button type="button" class="btn btn-sm btn-default" ng-click="setDate(2009, 7, 24)">2009-08-24</button>     <button type="button" class="btn btn-sm btn-danger" ng-click="clear()">Clear</button>     <button type="button" class="btn btn-sm btn-default" ng-click="toggleMin()" uib-tooltip="After today restriction">Min date</button>   </div> </body>  </html> 

The controller:

angular.module('ui.bootstrap.demo', ['ngAnimate', 'ui.bootstrap']); angular.module('ui.bootstrap.demo').controller('DatepickerPopupDemoCtrl', function($scope) { $scope.today = function() { $scope.dt = new Date(); }; $scope.today();

  $scope.clear = function() {     $scope.dt = null;   };    $scope.inlineOptions = {     customClass: getDayClass,     minDate: new Date(),     showWeeks: true   };    $scope.dateOptions = {     dateDisabled: disabled,     formatYear: 'yy',     maxDate: new Date(2020, 5, 22),     minDate: new Date(),     startingDay: 1   };    // Disable weekend selection   function disabled(data) {     var date = data.date,       mode = data.mode;     return mode === 'day' && (date.getDay() === 0 || date.getDay() === 6);   }    $scope.toggleMin = function() {     $scope.inlineOptions.minDate = $scope.inlineOptions.minDate ? null : new Date();     $scope.dateOptions.minDate = $scope.inlineOptions.minDate;   };    $scope.toggleMin();    $scope.open1 = function() {     $scope.popup1.opened = true;   };    $scope.open2 = function() {     $scope.popup2.opened = true;   };    $scope.setDate = function(year, month, day) {     $scope.dt = new Date(year, month, day);   };    $scope.formats = ['dd-MMMM-yyyy', 'yyyy/MM/dd', 'dd.MM.yyyy', 'shortDate'];   $scope.format = $scope.formats[0];   $scope.altInputFormats = ['M!/d!/yyyy'];    $scope.popup1 = {     opened: false   };    $scope.popup2 = {     opened: false   };    var tomorrow = new Date();   tomorrow.setDate(tomorrow.getDate() + 1);   var afterTomorrow = new Date();   afterTomorrow.setDate(tomorrow.getDate() + 1);   $scope.events = [{     date: tomorrow,     status: 'full'   }, {     date: afterTomorrow,     status: 'partially'   }];    function getDayClass(data) {     var date = data.date,       mode = data.mode;     if (mode === 'day') {       var dayToCheck = new Date(date).setHours(0, 0, 0, 0);        for (var i = 0; i < $scope.events.length; i++) {         var currentDay = new Date($scope.events[i].date).setHours(0, 0, 0, 0);          if (dayToCheck === currentDay) {           return $scope.events[i].status;         }       }     }      return '';   } }); 

plunker: http://plnkr.co/edit/LZgUAZQkNKgDrUZAVWSa?p=preview

Answers 2

I finally found the solution to my problem!

To be clear: the same bug was happening with the angular-daterangepicker plugin.

It was actually caused because some other dependency loaded an extra jQuery. Because of this multiple jQuerys were loaded and this caused the bug.

Answers 3

Try adding this before initializing the daterangepicker in the directive.

if (!jQuery().daterangepicker) {      return; }  $($element).daterangepicker({...}); 
Read More

Thursday, March 10, 2016

Link function after transclude in Angular js

Leave a Comment

I have two directives in Angular. One has to be trascluded in the other. My problem is that I can't access the DOM with simple JQuery selector after the transclude function has run. In particular I want to compile the first directive (topic) and then inject it in the other one (about-us) so I can access the DOM elements in the about-us link function. Here is the code for what I want to achieve:

<section>     <topic active = "true" title = "Lorem ipsum" icon = "ti-layers">A) Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</topic>     <topic title = "Lorem ipsum" icon = "ti-package">B) Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</topic>     <topic title = "Lorem ipsum" icon = "ti-stats-up">C) Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</topic>     <topic title = "Lorem ipsum" icon = "ti-layout-media-center-alt">D) Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</topic> </section> 

Both about-us and topic directives use transclude: true inside their configurations to compile their respective templates.

angular.module('playground', []).directive('topic', function() {   return {     restrict: 'E',     replace: true,     templateUrl: 'path/to/topic.html',     transclude: true,     scope: true,     link: function(scope, element, attributes, ctrl, transclude)     {         // Playing around with scope          // Transcluding         element.find('.tab-content p').append(transclude());     }   }; }).directive('section', ['$timeout', function($timeout) {   return {     restrict: 'E',     replace: true,     templateUrl: 'path/to/about-us.html',     transclude: true,     link: function(scope, element, attributes, ctrl, transclude)     {       element.find('.tabs').append(transclude());        // Now I want to retrieve some DOM contents after the transclude has taken place        $timeout(function()       {         // Playing with DOM and JQuery but sometimes this function run prior of the child transclude function        });     }   }; }]); 

For the sake of completeness here is the code for the two templates:

<!-- topic.html --> <li class="ng-class:active">   <div class="tab-title">       <i class="icon ng-class:icon;"></i>       <span>{{title}}</span>   </div>   <div class="tab-content">     <!-- ng-transclude directive not needed -->     <p ng-transclude></p>   </div> </li>  <!-- about-us.html --> <section class="bg-secondary pb0" id="about-us">   <div class="container">     <div class="row">       <div class="col-sm-12 text-center">         <h2 class="mb64 mb-xs-24">About us.</h2>       </div>       <div class="col-md-8 col-md-offset-2 col-sm-12 text-center">         <div class="tabbed-content icon-tabs">           <ul class="tabs"></ul>         </div>       </div>     </div>   </div> </section> 

The problem is that the $timeout function called as a work around has a completely random behaviour and sometimes it works, other times it doesn't. How can I fix this? Am I missing something about transclude, link and compile functions? Thank you for every reply!

2 Answers

Answers 1

I've solved my problem setting up a simple watcher on the main directive and using the transcludeFn injected by Angular in the link function.

Here's the code. Hope it could help if you have a similar problem.

angular.module('playground', []).directive('topic', function() {   return {     restrict: 'AE',     replace: true,     templateUrl: 'path/to/topic.html',     transclude: true,     scope: true,     link: function(scope, element, attributes, ctrl, transclude)     {        // Playing with scope...        transclude(scope.$parent, function(clone, parent_scope)       {         // Transcluding         element.find('.tab-content p').append(clone);          // It's time to render!         parent_scope.should_render = true;       });     }   }; }).directive('Section', function() {   return {     restrict: 'E',     replace: true,     templateUrl: 'path/to/section.html',     transclude: true,     scope: {},     link: function(scope, element, attributes, ctrl, transclude)     {       scope.rendered = false;       scope.should_render = false;        transclude(scope, function(clone)       {         // Transcluding         element.find('.tabs').append(clone);       });        // Setting up a watcher       var remove_watcher = scope.$watch('should_render', function(should_render)       {         if(should_render)         {           if(scope.rendered)           {             remove_watcher();             return;           }           // Now I can play with the DOM and JQuery.            scope.rendered = true;         }       });     }   }; }) 

Now I can use my directive like this:

<section>     <topic active = "true" title = "Lorem ipsum" icon = "ti-layers">A) Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</topic>     <topic title = "Lorem ipsum" icon = "ti-package">B) Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</topic>     <topic title = "Lorem ipsum" icon = "ti-stats-up">C) Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</topic>     <topic title = "Lorem ipsum" icon = "ti-layout-media-center-alt">D) Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</topic> </section> 

Answers 2

have you tried the compile in stead of link?

compile: function compile(tElement, tAttrs, transclude) {       return {         pre: function preLink(scope, iElement, iAttrs, controller) { ... },         post: function postLink(scope, iElement, iAttrs, controller) { ... }       } } 

Here you can find the documentation

Read More