本文共 33040 字,大约阅读时间需要 110 分钟。
Laravel and AngularJS work great together, but it can be a little tricky to get going at first, especially if you are new to the frameworks. In a previous article, Chris showed you how to make a with Laravel and Angular. This tutorial will again bring the two frameworks together as we build out a simple time tracking application.
Laravel和AngularJS可以很好地协作,但是刚开始时可能会有些棘手,特别是如果您不熟悉框架。 在上一篇文章中,克里斯向您展示了如何使用Laravel和Angular制作 。 当我们构建一个简单的时间跟踪应用程序时,本教程将再次将这两个框架结合在一起。
We'll be going into a lot of detail in this tutorial, so to make things manageable it has been broken into two parts. The first part will focus on getting the front-end setup with AngularJS and the second part on getting the backend setup with Laravel 5.
在本教程中,我们将进行很多详细的介绍,因此为了使内容易于管理,它分为两个部分。 第一部分将重点介绍如何使用AngularJS进行前端设置,而第二部分将重点在于使用Laravel 5进行后端设置。
We'll be building a simple time tracking application that will give users the ability to track hours spent on tasks by clocking-in and clocking-out. We'll use the offered by UI Bootstrap to let users enter their start and end times, and will give them a field to enter comments. Users will be able to list all of their time entries, create new ones, and also edit or delete existing entries.
我们将构建一个简单的时间跟踪应用程序,该应用程序将使用户能够通过记入和记出来跟踪花费在任务上的时间。 我们将使用UI Bootstrap提供的让用户输入他们的开始时间和结束时间,并为他们提供一个用于输入评论的字段。 用户将能够列出其所有时间条目,创建新时间条目以及编辑或删除现有条目。
We will be using the by John Papa which sets out an opinionated set of conventions for Angular apps. If you haven't seen this style guide before, some of the ways we set things up might look a bit foreign. Don't worry though, it's easy to catch on, and you'll likely find our code easier to read and understand with the it applied.
我们将使用John Papa ,该为Angular应用设置了一套自以为是的约定。 如果您以前没有看过此样式指南,那么我们进行设置的某些方法可能看起来有些陌生。 不过,请不要担心,它很容易上手,并且您可能会发现我们的代码通过应用它更易于阅读和理解。
As we focus on the front-end for this part of the tutorial (the Angular side, we'll deal with the Laravel side in part 2), let's keep our folder structure simple. Create a new directory and give it the following structure:
当我们专注于本部分教程的前端时(在Angular方面,我们将在第2部分中讨论Laravel方面),让我们保持文件夹结构简单。 创建一个新目录并为其提供以下结构:
|--bower_components|--css|--data|--scripts |--controllers |--services
Next, let's install our dependencies with Bower. From the command line:
接下来,让我们使用Bower安装依赖项。 在命令行中:
bower install angular angular-bootstrap angular-resource bootstrap moment
That's a lot of dependencies! Let's go through them to see what we've got:
那有很多依赖! 让我们通过他们来看看我们所拥有的:
Let's create our JavaScript files and setup their basic structure. There are three files that we will need:
让我们创建JavaScript文件并设置其基本结构。 我们将需要三个文件:
Our app.js file is very simple---we just need to define the application module, which we will call timeTracker
, and put in our dependencies:
我们的app.js文件非常简单-我们只需要定义应用程序模块,我们将其称为timeTracker
,并放入我们的依赖项:
/* scripts/app.js */(function() { 'use strict'; angular .module('timeTracker', [ 'ngResource', 'ui.bootstrap' ]);})();
In our TimeEntry.js file we need to declare our controller which we will call TimeEntry
. Following the AngularJS style guide mentioned earlier, we will use a capture variable called vm
(ViewModel) for our controller:
在我们的TimeEntry.js文件中,我们需要声明我们将称为TimeEntry
控制器。 按照前面提到的AngularJS样式指南,我们将为控制器使用一个名为vm
(ViewModel)的捕获变量:
/* scripts/controllers/TimeEntry.js */(function() { 'use strict'; angular .module('timeTracker') .controller('TimeEntry', TimeEntry); function TimeEntry(time) { // vm is our capture variable var vm = this; vm.timeentries = []; }})();
Here you can see that we've declared an empty array called vm.timeentries
. This array will eventually hold the time entry data that we grab with ngResource. You'll also notice that we create a named function called TimeEntry
and pass it to the controller. The time
argument we pass into this function is actually a dependency that we are injecting, and it references the time
service that we will create next.
在这里您可以看到我们已经声明了一个名为vm.timeentries
的空数组。 该数组最终将保存我们使用ngResource捕获的时间输入数据。 您还将注意到,我们创建了一个名为TimeEntry
的命名函数,并将其传递给控制器。 我们传递给该函数的time
参数实际上是我们要注入的依赖项,它引用了接下来将要创建的time
服务。
Finally, our time.js file will be the service for abstracting common code, especially the ngResource pieces.
最后,我们的time.js文件将成为抽象通用代码(尤其是ngResource片段)的服务。
/* scripts/services/time.js */(function() { 'use strict'; angular .module('timeTracker') .factory('time', time); function time($resource) { // ngResource call to our static data var Time = $resource('data/time.json'); return {}; }})();
This service follows the same pattern that we saw in our controller, but this time uses a factory
called time
. Our named function time
takes ngResource as a dependency and currently returns an empty object.
该服务遵循在控制器中看到的相同模式,但是这次使用的factory
称为time
。 我们的命名函数time
将ngResource作为依赖项,当前返回一个空对象。
There's one other piece in there right now---a variable called Time
that uses ngResource to make a call to a static JSON file called time.json
. So what is happening here and what is this JSON file for? We will eventually wire things up with Laravel acting as a RESTful API for our app, and will have it return results as JSON. This time.json
file is a simple way to get started with some mocked out data without the need for a backend just yet. It allows us to use ngResource off the bat, and when we're ready, we can simply switch out the call to this static file for a call to the RESTful API that we build with Laravel.
现在还有另外一块-名为Time
的变量,该变量使用ngResource调用名为time.json
的静态JSON文件。 那么这里发生了什么,这个JSON文件是做什么用的呢? 最终,我们将使用Laravel作为我们应用程序的RESTful API进行连接,并将其返回结果作为JSON。 这个time.json
文件是一种简单的方法,可以开始使用一些time.json
数据,而无需后端。 它允许我们立即使用ngResource,并且当我们准备就绪时,我们可以简单地将对此静态文件的调用切换为对使用Laravel构建的RESTful API的调用。
Now that we know what it's for, let's put some data in the time.json
file. Feel free to change things up with your own details:
现在我们知道它的用途了,让我们将一些数据放入time.json
文件中。 随时使用您自己的详细信息进行更改:
/* data/time.json */[ { "id":1, "user_id":1, "user_firstname":"Ryan", "user_lastname":"Chenkie", "start_time":"2015-02-21T18:56:48Z", "end_time":"2015-02-21T20:33:10Z", "comment": "Initial project setup." }, { "id":2, "user_id":1, "user_firstname":"Ryan", "user_lastname":"Chenkie", "start_time":"2015-02-27T10:22:42Z", "end_time":"2015-02-27T14:08:10Z", "comment": "Review of project requirements and notes for getting started." }, { "id":3, "user_id":1, "user_firstname":"Ryan", "user_lastname":"Chenkie", "start_time":"2015-03-03T09:55:32Z", "end_time":"2015-03-03T12:07:09Z", "comment": "Front-end and backend setup." }]
Let's now setup our index.html
file that will provide the structure to our single-page app. For the time being, we'll put in the basic elements and link up our CSS and JavaScript files.
现在,让我们设置index.html
文件,该文件将为我们的单页应用程序提供结构。 目前,我们将放入基本元素并链接我们CSS和JavaScript文件。
Time Tracker
As you can see, we have bootstrapped our application on the body
tag by setting ng-app
to the timeTracker
module. We're also making use of the controller as
syntax in our controller
declaration. The controller as
syntax is a big part of the AngularJS Style Guide. It can make for cleaner code because we are able to bind methods and properties directly onto the controller by using the this
keyword, like you saw earlier when we setup the JavaScript files. This means that we don't have to rely on using $scope
.
如您所见,通过将ng-app
设置为timeTracker
模块,我们已在body
标签上引导了我们的应用程序。 我们还在controller
声明中将controller as
语法。 controller as
语法的controller as
是《 AngularJS样式指南》的重要组成部分。 它可以使代码更简洁,因为我们可以使用this
关键字将方法和属性直接绑定到控制器上,就像您在设置JavaScript文件时所看到的那样。 这意味着我们不必依赖使用$scope
。
We've now got the main structure for the application in place, but it isn't doing much just yet. Next up we will fetch our sample data and display it.
现在,我们已经有了应用程序的主要结构,但是目前还没有做很多事情。 接下来,我们将获取示例数据并显示它。
Here is some CSS that we'll use for adjusting the elements to display properly:
这是一些CSS,我们将使用它们来调整元素以使其正确显示:
/* css/style.css */.time-numbers > *, .time-entry > * { display: inline-block;}.timepicker { margin-right: 15px;}@media (min-width: 546px) { .time-entry-comment { position: relative; top: 60px; }}
We're going to let the time
service be responsible for fetching data. We've already seen that a reference exists for ngResource
to talk to the static time.json
file, but now we need to get a method in place to fetch the data. Let's update our time.js
file with a method to handle this request:
我们将让time
服务负责获取数据。 我们已经看到存在ngResource
与静态time.json
文件对话的引用,但是现在我们需要一个适当的方法来获取数据。 让我们使用一种处理该请求的方法来更新我们的time.js
文件:
/* scripts/services/time.js */(function() { 'use strict'; angular .module('timeTracker') .factory('time', time); function time($resource) { // ngResource call to our static data var Time = $resource('data/time.json'); function getTime() { // $promise.then allows us to intercept the results // which we will use later return Time.query().$promise.then(function(results) { return results; }, function(error) { // Check for errors console.log(error); }); } return { getTime: getTime, } }})();
Here we've added a method called getTime
. This method is responsible for using ngResource
to make a query
to the static data and then return the results. The $promise.then
syntax is necessary in our case because a little later we will be modifying the returned array on the fly. For now, it is simply returning the results of the query. Finally, we've modified the object that the time
service returns to include the getTime
method.
在这里,我们添加了一个名为getTime
的方法。 此方法负责使用ngResource
对静态数据进行query
,然后返回结果。 $promise.then
语法在我们的例子中是必需的,因为稍后我们将即时修改返回的数组。 目前,它只是返回查询结果。 最后,我们修改了time
服务返回的对象以包括getTime
方法。
Next, let's hook into this new method in our controller:
接下来,让我们在控制器中加入这个新方法:
/* scripts/controllers/TimeEntry.js */(function() { 'use strict'; angular .module('timeTracker') .controller('TimeEntry', TimeEntry); function TimeEntry(time) { // vm is our capture variable var vm = this; vm.timeentries = []; // Fetches the time entries from the static JSON file // and puts the results on the vm.timeentries array time.getTime().then(function(results) { vm.timeentries = results; console.log(vm.timeentries); }, function(error) { // Check for errors console.log(error); }); }})();
We use then
to grab the results and put them on our vm.timeentries
array. To make sure we're getting data back, we can log the array out to the console. If everything worked properly, you should see it show up in developer tools:
then
,我们使用它来获取结果并将其放在vm.timeentries
数组中。 为了确保我们能取回数据,我们可以将阵列注销到控制台。 如果一切正常,您应该在开发人员工具中看到它:
Finally, let's have the results show up in our application. We can add in some additional HTML and put the templating in place to display our results. At this time, we can also add in the controls for making new time entries:
最后,让结果显示在我们的应用程序中。 我们可以添加一些其他HTML并将模板放在适当的位置以显示我们的结果。 这时,我们还可以添加用于输入新时间的控件:
......{ {time.user_firstname}} { {time.user_lastname}}
{ {time.comment}}
{ {time.end_time | date:'mediumDate'}}
There's a lot going on here, so let's go through the changes step by step.
这里有很多事情,所以让我们一步一步地进行更改。
timepicker
directive that is offered by UI Bootstrap and set some default parameters for it. The timepicker
directive gives us a nice little widget that will allow users to clock-in and clock-out at specific times. 首先,我们对导航栏进行了一些更改。 我们已经添加了UI Bootstrap提供的timepicker
指令,并为其设置了一些默认参数。 timepicker
指令为我们提供了一个不错的小部件,它允许用户在特定时间进行timepicker
出。 ng-model
and that they are prefaced with vm
. This is because we are using the controller as
syntax and need an alias for our View Model (you can use anything as an alias, but we'll stick to vm
for now). 我们还为用户添加了一个字段,以便在其时间条目中包含评论。 您会看到,对于所有这些,我们都在ng-model
上为其指定了名称,并且以vm
开头。 这是因为我们使用controller as
语法,并且需要为我们的视图模型使用别名(您可以使用任何东西作为别名,但是现在我们将坚持使用vm
)。 logNewTime
is called when the button is clicked. We haven't defined this method yet, but we will in the next steps. 在“日志时间”按钮上,我们指定了单击按钮时将调用名为logNewTime
的方法。 我们尚未定义此方法,但我们将在后续步骤中进行定义。 Below the navbar, we are setting up an ng-repeat
to display the time entries. You'll see that for the date of the time entry we are taking the end_date
and formatting it with an Angular date filter.
在导航栏下方,我们正在设置ng-repeat
以显示时间条目。 您会看到,对于时间输入的日期,我们将使用end_date
并使用Angular日期过滤器对其进行格式化。
If everything is wired up correctly, you should see our entries displayed:
如果一切都正确连接,您应该看到显示的条目:
Things are looking good so far, but we aren't actually displaying the amount of time that is involved for each time entry. In the next section we're going to get the hour and minute calculations setup with Moment.js, after which we can update our app to display the amount of time logged.
到目前为止,情况看起来不错,但实际上并没有显示每次输入所涉及的时间。 在下一节中,我们将使用Moment.js进行小时和分钟的计算设置,此后,我们可以更新我们的应用程序以显示记录的时间量。
You might be wondering why we don't bother just storing a calculated value for the total amount of time in each time entry. This is a good question, and we could certainly set things up this way; however, when it comes to database schemas, it's a best practice to not store calculated values. If we simply store the start and end times, we have more flexibility when it comes to updating the database schema or when we make edits to our time entries. Say, for instance, that we wanted to update the start time for a given entry. If we store calculated values, we would have to write additional logic that updates the total time once our start time is updated. It is much simpler if we calculate things on the fly.
您可能想知道为什么我们不花时间在每个时间条目中存储总时间的计算值。 这是一个很好的问题,我们当然可以通过这种方式进行设置。 但是,对于数据库模式,最好的做法是不存储计算值。 如果仅存储开始时间和结束时间,则在更新数据库架构或对时间条目进行编辑时,我们将具有更大的灵活性。 举例来说,假设我们要更新给定条目的开始时间。 如果存储计算的值,则必须编写其他逻辑,以在开始时间更新后更新总时间。 如果我们即时进行计算,则要简单得多。
Moment.js gives us some great tools, and for this task we want the diff
and duration
methods. Let's setup two new methods in our time
service, one for getting the time difference and the other for getting the total time:
Moment.js为我们提供了一些出色的工具,为此,我们需要diff
和duration
方法。 让我们在time
服务中设置两种新方法,一种用于获取时差,另一种用于获取总时间:
/* scripts/services/time.js */(function() { 'use strict'; angular .module('timeTracker') .factory('time', time); function time($resource) { // ngResource call to our static data var Time = $resource('data/time.json'); // $promise.then allows us to intercept the results of the // query so we can add the loggedTime property function getTime() { return Time.query().$promise.then(function(results) { angular.forEach(results, function(result) { // Add the loggedTime property which calls // getTimeDiff to give us a duration object result.loggedTime = getTimeDiff(result.start_time, result.end_time); }); return results; }, function(error) { // Check for errors console.log(error); }); } // Use Moment.js to get the duration of the time entry function getTimeDiff(start, end) { var diff = moment(end).diff(moment(start)); var duration = moment.duration(diff); return { duration: duration } } // Add up the total time for all of our time entries function getTotalTime(timeentries) { var totalMilliseconds = 0; angular.forEach(timeentries, function(key) { totalMilliseconds += key.loggedTime.duration._milliseconds; }); // After 24 hours, the Moment.js duration object // reports the next unit up, which is days. // Using the asHours method and rounding down with // Math.floor instead gives us the total hours return { hours: Math.floor(moment.duration(totalMilliseconds).asHours()), minutes: moment.duration(totalMilliseconds).minutes() } } return { getTime: getTime, getTimeDiff: getTimeDiff, getTotalTime: getTotalTime } }})();
Our getTimeDiff
method has two parameters, a start time and an end time, which will be the times at which the user clocks in and clocks out. To find the difference between the start and end times, we use Moment's diff
method, which returns the total time in milliseconds. We could work with the total number of milliseconds for each time entry and do the math to get our total number of hours, but that could get cumbersome. Instead, let's use Moment's duration
method. It will return an object containing a nice breakdown of the total time spent on the task.
我们的getTimeDiff
方法有两个参数,一个开始时间和一个结束时间,这将是用户切入和切出的时间。 为了找到开始时间和结束时间之间的时差,我们使用Moment的diff
方法,该方法返回以毫秒为单位的总时间。 我们可以计算每次输入的总毫秒数,并进行数学运算以获得小时总数,但这可能会很麻烦。 相反,让我们使用Moment的duration
方法。 它将返回一个对象,其中包含花费在该任务上的总时间的详细细分。
Finally, we return an object with a duration key that equals the duration we just derived. When we exceed 24 hours worth of time entries, the Moment.js duration object will report the next unit up, which is days. We could use this unit, but I think it's a bit nicer to work with hours as the highest unit, so to get around this we use the asHours
method that Moment.js offers. We use Math.floor
to round it down and then keep using minutes
from the duration object to report the number of minutes.
最后,我们返回一个带有持续时间键的对象,该键等于我们刚刚得出的持续时间。 当我们输入的时间超过24小时时,Moment.js持续时间对象将报告下一个单位,即天。 我们可以使用这个单位,但是我认为将小时作为最高单位会更好一些,因此为了解决这个问题,我们使用Moment.js提供的asHours
方法。 我们使用Math.floor
舍下来,然后继续使用minutes
从时间对象报告的分钟数。
You'll also notice that we've amended the getTime
method with an angular.forEach
loop. This loop allows us to intercept the results that are returned from our call to the static JSON file and add in a new property called loggedTime
, which is equal to the result of a call to our new getTimeDiff
method. We pass in the start and end times found in the static data to the getTimeDiff
method.
您还将注意到,我们已经用angular.forEach
循环修改了getTime
方法。 此循环使我们可以拦截从对静态JSON文件的调用返回的结果,并添加一个名为loggedTime
的新属性,该属性等于对新的getTimeDiff
方法的调用的结果。 我们将在静态数据中找到的开始时间和结束时间传递给getTimeDiff
方法。
Finally, we want to have a way to calculate the total time that all of our time entries make up. To do this, we create a getTotalTime
method that accepts our timeentries
array, uses angular.forEach
to loop through them, and adds the number of milliseconds from each to a variable called totalMilliseconds
. Since we'll want access to both the number of hours and number of minutes, we return an object with keys for each. We once again use Moment's duration method here, but in this case we ask for access directly to hours
and minutes
.
最后,我们希望有一种方法来计算所有时间条目所占的总时间。 为此,我们创建了一个getTotalTime
方法,该方法接受我们的timeentries
数组,使用angular.forEach
遍历它们,并将每个毫秒数添加到名为totalMilliseconds
的变量中。 因为我们要访问小时数和分钟数,所以我们返回一个带有键的对象。 我们在这里再次使用Moment的duration方法,但是在这种情况下,我们要求直接访问hours
和minutes
。
Now that we have methods in place to calculate our time, let's put them to use in the view.
现在我们已经有了计算时间的方法,现在让我们在视图中使用它们。
......{ {time.user_firstname}} { {time.user_lastname}}
{ {time.comment}}
{ {time.end_time | date:'mediumDate'}}
{ {time.loggedTime.duration._data.hours}} hour s
{ {time.loggedTime.duration._data.minutes}} minutes
Total Time
{ {vm.totalTime.hours}} hours
{ {vm.totalTime.minutes}} minutes
We've made a few changes to the view to reflect our time calculations. Let's first take a look at the new h2
and h4
tags that have been added into the time-numbers
div. These are used to display the number of hours and minutes that each time entry makes up. For the number of hours, we're putting some checks in place to determine whether we need to display the hour span
, and then whether we need to pluralize the word "hour".
我们对视图进行了一些更改以反映我们的时间计算。 首先让我们看一下已添加到time-numbers
div中的新h2
和h4
标签。 这些用于显示每个时间条目组成的小时和分钟数。 对于小时数,我们将进行一些检查以确定是否需要显示小时span
,然后是否需要对单词“ hour”进行复数。
Next, we've added in another box to the right which gives us the total number of hours that all of the displayed time entries make up. However, if you run this right now you'll see that there are no values for the total time box, and that's because we still need to complete this functionality in the controller.
接下来,我们在右侧添加了另一个框,该框为我们提供了所有显示的时间条目组成的总小时数。 但是,如果立即运行此功能,您将看到“总时间”框没有任何值,这是因为我们仍然需要在控制器中完成此功能。
There are just a couple more pieces left to finish out in the controller before we have a working app. First, we need an object called totalTime
that is responsible for tallying up the time from each time entry. Second, we need to complete the logNewTime
method that we mentioned earlier. This method is called when we click the "Log Time" button up in the navbar.
在我们有一个可运行的应用程序之前,还需要完成几项工作才能在控制器中完成。 首先,我们需要一个名为totalTime
的对象,该对象负责计算每次输入的时间。 其次,我们需要完成前面提到的logNewTime
方法。 当我们单击导航栏中的“登录时间”按钮时,将调用此方法。
Let's handle the time aggregation by putting in a method called updateTotalTime
:
让我们通过放入一个名为updateTotalTime
的方法来处理时间聚合:
/* scripts/controllers/TimeEntry.js */(function() { 'use strict'; angular .module('timeTracker') .controller('TimeEntry', TimeEntry); function TimeEntry(time) { // vm is our capture variable var vm = this; vm.timeentries = []; vm.totalTime = {}; // Fetches the time entries from the static JSON file // and puts the results on the vm.timeentries array time.getTime().then(function(results) { vm.timeentries = results; updateTotalTime(vm.timeentries); }, function(error) { // Check for errors console.log(error); }); // Updates the values in the total time box by calling the // getTotalTime method on the time service function updateTotalTime(timeentries) { vm.totalTime = time.getTotalTime(timeentries); } }})();
You'll see that we've added an updateTotalTime
method at the bottom. This method takes the timeentries
array as an argument and updates the newly declared vm.totalTime
object to equal the result of a call to the getTotalTime
method from the time
service. Like we saw earlier, the getTotalTime
method from the time
service takes the array of time entries and loops through to count the total number of milliseconds from each. It then aggregates all that time and uses Moment.js to find and return the total number of hours and minutes.
您会看到我们在底部添加了updateTotalTime
方法。 此方法将timeentries
数组作为参数,并更新新声明的vm.totalTime
对象,使其等于从time
服务调用getTotalTime
方法的结果。 就像我们之前看到的, time
服务中的getTotalTime
方法获取时间条目数组,并循环遍历每个时间条目的毫秒总数。 然后,它将汇总所有时间,并使用Moment.js查找并返回小时数和分钟数的总数。
If we refresh the page we now see that the total number of hours and minutes are being displayed properly:
如果刷新页面,我们现在可以看到小时和分钟总数已正确显示:
Finally, let's add in the logNewTime
method that will handle new time entries. Because we are working with static JSON data for demo purposes here, all we need to do to update the list is push a new object onto the already existing timeentries
array. This is a temporary shim that will no longer be necessary once we get database persistence with Laravel working.
最后,让我们添加将处理新时间条目的logNewTime
方法。 因为我们在这里使用静态JSON数据进行演示,所以更新列表所需要做的就是将一个新对象推送到已经存在的timeentries
数组中。 这是一个临时的填充程序,一旦我们在Laravel工作的情况下实现了数据库持久性,就不再需要它了。
Here is the finished TimeEntry
controller:
这是完成的TimeEntry
控制器:
/* scripts/controllers/TimeEntry.js */(function() { 'use strict'; angular .module('timeTracker') .controller('TimeEntry', TimeEntry); function TimeEntry(time) { // vm is our capture variable var vm = this; vm.timeentries = []; vm.totalTime = {}; // Initialize the clockIn and clockOut times to the current time. vm.clockIn = new Date(); vm.clockOut = new Date(); // Fetches the time entries from the static JSON file // and puts the results on the vm.timeentries array time.getTime().then(function(results) { vm.timeentries = results; updateTotalTime(vm.timeentries); }, function(error) { // Check for errors console.log(error); }); // Updates the values in the total time box by calling the // getTotalTime method on the time service function updateTotalTime(timeentries) { vm.totalTime = time.getTotalTime(timeentries); } // Submits the time entry that will be called // when we click the "Log Time" button vm.logNewTime = function() { // Make sure that the clock-in time isn't after // the clock-out time! if(vm.clockOut < vm.clockIn) { alert("You can't clock out before you clock in!"); return; } // Make sure the time entry is greater than zero if(vm.clockOut - vm.clockIn === 0) { alert("Your time entry has to be greater than zero!"); return; } vm.timeentries.push({ "user_id":1, "user_firstname":"Ryan", "user_lastname":"Chenkie", "start_time":vm.clockIn, "end_time":vm.clockOut, "loggedTime": time.getTimeDiff(vm.clockIn, vm.clockOut), "comment":vm.comment }); updateTotalTime(vm.timeentries); vm.comment = ""; } }})();
There are few things going on in the logNewTime
method, so let's break it down.
logNewTime
方法中发生的事情很少,因此让我们对其进行分解。
Firstly, we want to make sure that new time entries make sense---the user shouldn't be able to enter a clock-in time that is after the clock-out time. For that, we put in a simple conditional that checks whether the clock-in time is after the clock-out time, and if it is, alerts the user of the error. We're also checking to make sure the user has actually logged some time and that they haven't just left the clock-in and clock-out times the same.
首先,我们要确保新的时间输入有意义-用户不应输入超出超时时间的进入时间。 为此,我们放置了一个简单的条件,该条件检查签入时间是否在签出时间之后,如果是,则向用户发出错误提示。 我们还在检查以确保用户实际上已经记录了一些时间,并且他们没有使输入和输出时间保持相同。
Next, we push a new object onto our time entries array. Notice here that we're specifying that start_time
and end_time
are equal to vm.clockIn
and vm.clockOut
respectively. These values come directly from the timepicker
directives we used in our view. Because of the way the timepicker directive works, if the user doesn't make a change to the clock-in and clock-out times, it will give null
values. This can cause problems, so to get around it we are initializing vm.clockIn
and vm.clockOut
to the current date.
接下来,我们将一个新对象推入我们的时间条目数组。 请注意,这里我们指定的是start_time
和end_time
分别等于vm.clockIn
和vm.clockOut
。 这些值直接来自我们在视图中使用的timepicker
指令。 由于timepicker指令的工作方式,如果用户不更改输入和输出时间,它将给出null
值。 这可能会导致问题,因此要解决此问题,我们将vm.clockIn
和vm.clockOut
初始化为当前日期。
If you look again at the data we started with in the static time.json
file, you'll notice that there is no loggedTime
property. This is because we have things setup in the time
service to add this property on as the data is read. Now that we're adding new time entries in after the initial data is loaded, we'll need to specify this property. We can reuse the getTimeDiff
method here, passing in the clock-in and clock-out times.
如果再次查看我们在static time.json
文件中开始使用的数据,您会注意到没有任何loggedTime
属性。 这是因为我们在time
服务中进行了一些设置,以便在读取数据时添加此属性。 现在,我们要在加载初始数据后添加新的时间条目,我们需要指定此属性。 我们可以在此处重用getTimeDiff
方法,传入传入和传出时间。
Finally, we make a call to our updateTotalTime
method so that the total time box reflects the right amount of time. We also want to clear the comment field so that it is ready for the next time entry.
最后,我们调用updateTotalTime
方法,以便总时间框反映正确的时间量。 我们还希望清除注释字段,以便下次输入时可以使用。
There we go---we've got a good start at a time tracking application! There are, however, some obvious limitations with the app as it is now:
我们走了-在一个时间跟踪应用程序上我们有了一个良好的开端! 但是,现在的应用程序存在一些明显的局限性:
We're going to fix these problems in the next part of this tutorial when we get a Laravel 5 backend running. We'll have Laravel expose a RESTful API and let our Angular front-end consume it. We'll also provide a way to log time as a specific user and edit or delete time entries. Stay tuned for more!
当我们运行Laravel 5后端时,我们将在本教程的下一部分中解决这些问题。 我们将让Laravel公开一个RESTful API,并让我们的Angular前端使用它。 我们还将提供一种以特定用户身份记录时间以及编辑或删除时间条目的方法。 敬请期待更多!
If you’d like to get more AngularJS and Laravel tutorials, feel free to head over to and . You should follow me on ---I'd love to hear about what you're working on!
如果您想获得更多AngularJS和Laravel教程,请随时访问并 。 您应该在关注我---我很想知道您在做什么!
翻译自:
转载地址:http://wnywd.baihongyu.com/