简体   繁体   English

角度指令创建 - 使用多个模板

[英]Angular directive creation - using multiple templates

I'm fairly new to Angular JS and I'm trying to create a datepicker directive. 我是Angular JS的新手,我正在尝试创建一个datepicker指令。 It will not work like the Angular UI Bootstrap datepicker as it won't use a textbox but a full page calendar which you will be able to swipe through and click a day (which will update ngModel). 它不会像Angular UI Bootstrap datepicker那样工作,因为它不会使用文本框,而是使用整页日历,您可以滑动并点击一天(这将更新ngModel)。

My plan was to have a single directive which has a renderMonth() function. 我的计划是有一个具有renderMonth()函数的指令。 This function would accept a month as a parameter and generate all the rows/days in an array which would then be bound to a template for the month. 此函数将接受一个月作为参数,并生成数组中的所有行/天,然后绑定到该月的模板。

My problem is that I can specify a template for the datepicker in my directive declaration but I don't know how to specify templates for the rows/days etc and load them in and bind them. 我的问题是我可以在我的指令声明中为datepicker指定一个模板,但我不知道如何为行/天等指定模板并加载它们并绑定它们。 I could do it using jQuery and lots of string concetenation, but that seems all wrong. 我可以使用jQuery和大量的字符串集合来做到这一点,但这似乎都是错的。

I have been reading the source code for the Angular UI Datepicker but as a newbie it makes very little sense to me. 我一直在阅读Angular UI Datepicker的源代码,但作为一个新手,它对我来说没什么意义。 They have decoupled 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 aren't going to stretch to creating directives which communicate with each other. 他们已将所有内容分解为许多子指令(月指令,年份指令等),并且他们有自己的模板,但这不是我想要做的,因为我的Angular技能不会延伸到创建与每个通信的指令其他。 The Angular UI code is just way too complicated for me. Angular UI代码对我来说太复杂了。

One thing I do like is that they use existing directives like ng-repeat in their templates and bind them to the array or rows/days. 我喜欢的一件事是他们在模板中使用现有的指令,如ng-repeat,并将它们绑定到数组或行/天。 For me to do that I'd need to load the template in each time the renderMonth() is called and compile the template because it has existing directives in it. 对我来说,我需要在每次调用renderMonth()时加载模板并编译模板,因为它中有现有的指令。

So, basically my question is, does anybody have any examples of how I could write a import a template into my render function, compile it and ten bind it to the row/day data which is in my directives scope. 所以,基本上我的问题是,是否有人有任何关于如何将模板导入我的渲染函数,编译它并将其绑定到我的指令范围内的行/日数据的示例。

I'll be honest. 我会说实话。 I have no idea if I'm even speaking any sense. 我不知道我是否说出任何意义。 I'm just typing words which sound vaguely right. 我只是输入听起来模糊的话语。

ALl I need is for somebody to point me in the right direction. 我需要的是有人指出我正确的方向。 Thanks. 谢谢。

As discussed in the comments, I personally wouldn't worry too much about importing templates, the $compile function, or anything that complex for what is relatively simple markup. 正如评论中所讨论的,我个人不会过分担心导入模板, $compile函数或任何复杂的相对简单的标记。

I've created a (very) basic barebones plunker here , to demonstrate what my approach would be. 我在这里创建了一个(非常)基本的准系统插件 ,以展示我的方法。 I've used moment.js to deal with dates, because I am terrible with vanilla javascript date manipulation and find it very clumsy. 我用了moment.js来处理日期,因为我对vanilla javascript日期操作非常糟糕,并且发现它非常笨拙。

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 relies on the scope having a selected property which is a moment object. 模板依赖于具有selected属性的范围,该属性是时刻对象。 All it does is creates some buttons change the month and displays the month name at the top, then creates a span for each day in that month. 它所做的就是创建一些按钮来更改月份并在顶部显示月份名称,然后为该月的每一天创建一个跨度。

A tiny bit of CSS puts the days in rows of 7: 一小部分CSS将日期排成7行:

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

This is the directive's link function: 这是指令的link功能:

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)
  }
}

There is really nothing special there, but do note that the $watch command used takes advantage of the fact that you can pass a function instead of a string (though looking at it now you could just use 'selected.month()' as a string). 那里真的没有什么特别之处,但请注意所使用的$watch命令利用了你可以传递一个函数而不是一个字符串的事实(尽管现在你可以使用'selected.month()'作为一个串)。

Generating the days array is slightly more complex if you want to align the dates with days of the week, which I started to do, but didn't finish. 如果你想将日期与一周中的几天对齐,那么生成days数组会稍微复杂一些,这是我开始做的,但没有完成。 My plan was for each object in the array to have a isPreviousMonth property which would conditionally apply a different style using ng-class , and would look up the correct number as well, rather than just using a ~ for the offset days! 我的计划是让数组中的每个对象都有一个isPreviousMonth属性,该属性将使用ng-class有条件地应用不同的样式,并且也会查找正确的数字,而不是仅使用~作为偏移天数!

There is a lot more to do to finish this off - not least making the dates selectable and integrating with ngModel , but hopefully this example has given you an idea of one way to approach the problem of templating. 还有很多工作要做 - 关键是使日期可以选择并与ngModel集成,但希望这个例子让你了解一种解决模板问题的方法。 There are many other ways you could do this, but I find this the most intuitive. 还有很多其他方法可以做到这一点,但我发现这是最直观的。

Extension 延期

To address your question regarding having multiple months, I've expanded the plunker a bit. 为了满足您对多个月的问题,我已经扩大了plunker一点。

I added an attribute to the directive: 我在指令中添加了一个属性:

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

And modified the template to ng-repeat certain parts depending on the number of months you specify in the attribute: 并根据您在属性中指定的月数修改模板以ng-repeat某些部分:

<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>

So this instead requires an array of months, each with 2 properties: a moment, and an array of days. 所以这需要一个月的数组,每个月都有2个属性:片刻和一系列天。 I added some quick and dirty javascript to generate that, but I won't paste it here as there are almost certainly much neater ways of doing it! 我添加了一些快速而又脏的javascript来生成它,但是我不会在这里粘贴它,因为几乎可以肯定有更简洁的方法!

Note that at present we're still watching scope.selected for changes in the month, which would lead to the (probably unwanted) behaviour that if you selected a date not in the first month, the calendar would rearrange itself. 请注意,目前我们仍在观察scope.selected 。选择月份中的更改,这将导致(可能不需要的)行为,如果您选择的日期不是在第一个月,日历将自行重新排列。 I think instead of using a $watch in this case, I'd just add to the click events to handle redrawing the calendar properly. 我认为在这种情况下不使用$watch ,我只是添加click事件来正确处理重绘日历。

PS This addresses the cloning part but not the scrolling part. PS这解决了克隆部分而不是滚动部分。 For scrolling, I would recommend adding one extra month on either end of the months array, then having an ng-show condition that means only the middle month(s) is shown. 对于滚动,我建议在月份数组的任一端添加一个月,然后使用ng-show条件,这意味着只显示中间月份。 You can then take advantage of angular's inbuilt animations ( this is a good reference ) to scroll them in/out using css. 然后,您可以利用angular的内置动画( 这是一个很好的参考 )来使用css滚动/滚出它们。 If you want more info about that contact me through the chat I opened for this question. 如果您想了解更多信息,请通过我为此问题打开的聊天与我联系。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM