Angular creating directive - using multiple templates

I am new to Angular JS and I am trying to create a datepicker directive. It won't work the same as the Angular UI Bootstrap datepicker as it won't use a textbox, but a fully functional calendar calendar that you can swipe and click a day (which will update the ngModel).

My plan was to have a single directive that has a renderMonth () function. This function will take the month as a parameter and generate all strings / days in the array, which will then be bound to the month template.

My problem is that I can specify the template for the datepicker in my directive declaration, but I don't know how to specify the templates for the strings / days, etc., and load them and link them. I could do it with jQuery and a lot of string concatenation, but that seems to be wrong.

I've read the source code for the Angular UI Datepicker but as a beginner it makes very little sense to me. They split everything into many sub-directives (month directive, year directive, etc.) and they have their own templates, but that's not what I want to do because my Angular skills won't stretch before creating directives who communicate with each other. Angular frontend code is too complex for me.

I really like the fact that they use existing directives like ng-repeat in their templates and link them to array or strings / days. To do this, I will need to load the template every time renderMonth () is called and compile the template, because there are existing directives in it.

So basically my question is, does anyone have examples of how I can write a template import into my render function, compile it, and ten bind it to the line / day data that is in my directive scope.

I'll be honest. I have no idea if I even say some meaning. I just type in words that sound vague.

ALI I need someone to point me in the right direction. Thank.

+3


source to share


1 answer


As discussed in the comments, I personally wouldn't bother too much about importing templates, functions, $compile

or anything that is difficult for relatively simple markup.

I've created a (very) basic barebones plunker here to demonstrate what my approach would be. I used moment.js to deal with dates because I'm terrible with vanilla javascript date handling and find it very clumsy.

This is the template:

<div class="controls">
  <button ng-click="prevMonth()"><-</button>
  <span>{{selected.format('MMMM')}}</span>
  <button ng-click="nextMonth()">-></button>
</div>
<div class="month">
  <span class="day" ng-repeat="day in selected.days">
    {{day.number}}
  </span>
</div>

      

The template is based on a region that has a property selected

that is a moment object. All it does is that some buttons change the month and display the name of the month at the top and then create a gap for each day that month.

A tiny bit of CSS puts the days on lines of 7:

span.day{
  float: left;
  width: 25px;
}
span.day:nth-child(7n+1){
  clear:left;
}

      

This is the link

function directive :

link: function(scope, element, attributes){
  // Set the selection to now initially.
  scope.selected = moment();

  generateDaysArray = function(){
    // -- REMOVED FOR BREVITY -- //
    return days;
  }
  // Watch the month for changes and update the days array
  scope.$watch(
    function(){
      return scope.selected.month();
    },
    function(newVal, oldVal){
      scope.selected.days = generateDaysArray();
    }
  )
  // Control button actions
  scope.nextMonth = function(){
    scope.selected.add('month', 1)
  }
  scope.prevMonth = function(){
    scope.selected.subtract('month', 1)
  }
}

      

Nothing special here, but note that the command you $watch

use takes advantage of the fact that you can pass a function instead of a string (although looking at it, you can now just use it 'selected.month()'

as a string).

Generating the days array is a little trickier if you want to align dates with the days of the week, which I started out but didn't finish. My plan was that for each object in the array had a property isPreviousMonth

that would conditionally apply a different style using ng-class

, and also look for the correct number, instead of just using ~

offsets for days



There is still a lot to be done to complete this, not least making choices and integrating dates with ngModel

, but hopefully this example has given you an idea of ​​one way to approach the templating problem. There are many other ways you could do this, but I find this to be the most intuitive.

Expansion

To solve my question regarding a few months, I extended plunker a bit .

I added an attribute to the directive:

<date-picker num-months="3"></date-picker>

      

And changed the template to ng-repeat

specific parts depending on the number of months given in the attribute:

<div class="controls">
  <button ng-click="prevMonth()"><-</button>
  <span ng-repeat="month in months" class="month-title">
    {{month.moment.format('MMMM')}}
  </span>
  <button ng-click="nextMonth()">-></button>
</div>
<div class="month" ng-repeat="month in months">
  <span class="day" ng-repeat="day in month.days">
    {{day.number}}
  </span>
</div>

      

Therefore, this requires an array of months, each with 2 properties: moment and an array of days. I added some quick and dirty javascript to generate this, but I won't embed it here as there are almost certainly many easier ways to do this!

Please note that we are currently still watching for scope.selected

changes in the month, which will lead to (possibly unwanted) behavior, which if you choose a date other than the first month, the calendar will be rearranged. I think instead of using it $watch

in this case, I would just add to the click events to redraw the calendar correctly.

PS . This refers to the cloning part, but not the scroll part. For scrolling, I would recommend adding one extra month at the end of the months array, followed by a condition ng-show

that means only the average month (s) is displayed. Then you can use angular inline animations ( this is a good link ) to scroll to / from with css. If you would like more information on this, please contact me via the chat I opened for this question.

+4


source







All Articles