sequelize 创建表
This is the second part of a three-part series on building an angularJS application with Sequelize broken down as follows.
这是一个由三部分组成的系列文章的第二部分,该系列文章使用Sequelize分解了以下内容,从而构建了angularJS应用程序。
- Part 1 - Creating out the server side of our sample application with Node.js and PostgreSQL with Sequelize. 第1部分 - 使用Node.js和PostgreSQL和Sequelize创建示例应用程序的服务器端。
- Part 2 - Creating a front end for the application in AngularJS and Angular material. 第2部分 -在AngularJS和Angular材质中为应用程序创建前端。
- Part 3 - Testing and Deploying to Heroku. 第3部分 -测试和部署到Heroku。
了解我们的前端结构 ( Understanding Our Frontend Structure )
Just like we did in the server directory , we will need to set up a few things for our frontend.
就像我们在服务器目录中所做的一样,我们需要为前端设置一些设置。
Our images, stylesheets, scripts and jade files will be in the app directory. We will then add gulp tasks to compile and minify the files to a public directory which will serve our application on the browser.
我们的图像,样式表,脚本和Jade文件将位于app目录中。 然后,我们将添加gulp任务以将文件编译并缩小到一个公共目录,该目录将在浏览器中为我们的应用程序提供服务。
├── index.js
├── gulpfile.js
├── package.json
├── bower.json
├── .bowerrc
├── .sequelizerc
├── app/
├── images/
├── js/
├── controllers/
├── directives/
├── filters/
├── services/
├── styles/
├── views/
├── app.js
├── index.jade
├── public
├── images/
├── js/
├── index.js
├── css/
├── app.css
├── views/
├── index.html
├── server
├── ...
将路由重构为路由文件 ( Refactor Routes To Routes File )
Currently, we have clogged up our /index.js with routes to our API. Let's refactor the routes into a routes file.
目前,我们已经使用到API的路由来阻塞/index.js 。 让我们将路由重构到路由文件中。
server/routes/index.js
服务器/路由/index.js
var authors = require('../controllers/authors'),
books = require('../controllers/books');
module.exports = function (router) {
router.get('/authors', authors.index);
router.get('/authors/:id', authors.show);
router.post('/authors', authors.create);
router.put('/authors', authors.update);
router.delete('/authors/:id', authors.delete);
router.get('/books', books.index);
router.get('/books/:id', books.show);
router.post('/books', books.create);
router.put('/books/:id', books.update);
router.delete('/books/:id', books.delete);
return router
};
Our index.js file is now much cleaner and does not handle any routes logic.
现在,我们的index.js文件更加整洁,并且不处理任何路由逻辑。
index.js
index.js
var express = require('express'),
routes = require('./server/routes')
bodyParser = require('body-parser');
var app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static(process.cwd() + '/public'));
//App routes
app.use(routes(express.Router()));
app.get('/*', function (req, res) {
res.sendFile('index.html', {
root: './public'
});
});
app.set('port', process.env.PORT || 8000);
app.listen(app.get('port'), function () {
console.log("Magic happens on port", app.get('port'));
});
设置前端 ( Setting Up The Front End )
In this section, we will accomplish the following.
在本节中,我们将完成以下任务。
- Add an entry index.jade and include head and footer layouts. 添加一个条目index.jade,包括头和页脚的布局。
- Add gulp tasks to automate the frontend workflow we discussed above. 添加gulp任务以自动化我们上面讨论的前端工作流。
- Update
.bowerrc
file to automatically inject installed packages to our html layout. 更新.bowerrc
文件,以自动将已安装的软件包注入我们的html布局。
添加index.jade (Add index.jade)
Being a Single Page Application (SPA), Bookmark will be served from a single entry file /public/index.html . Let's go ahead and setup the corresponding app/index.jade.
作为单页应用程序 (SPA),将从单个条目文件/public/index.html提供书签 。 让我们继续并设置相应的app / index.jade 。
/app/index.jade
/app/index.jade
doctype html
html(lang="en")
head
title bookmark app
include views/layouts/head
body(layout="row" ng-app="Bookmark")
include views/layouts/footer
/app/views/layouts/head.jade
/app/views/layouts/head.jade
meta(charset="utf-8")
meta(http-equiv="X-UA-Compatible" content="IE=edge")
meta(name="description" content="")
meta(name='HandheldFriendly' content='True')
meta(name='MobileOptimized' content='320')
meta(name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no')
base(href="/")
// CDN provided
link(rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:400,100,100italic,300,300italic,400italic,500,500italic,700,700italic,900,900italic" type="text/css")
link(rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" type="text/css")
// bower:css
// endbower
// inject:css
// endinject
/app/views/layouts/footer.jade
/app/views/layouts/footer.jade
// bower:js
// endbower
// inject:js
// endinject
We will be using wiredep
and gulp-inject
packages to inject the included files in the right order of dependencies automatically as they are installed as is demonstrated in this short video.
我们将使用wiredep
和wiredep
gulp-inject
软件包在安装时自动按照正确的依赖性顺序注入包含的文件,如本短片所示。
添加Gulp任务 (Adding The Gulp Tasks)
Adding gulp tasks is a very crucial point in our application development process. Without our gulpfile, all the code we write is never compiled into our public directory which serves our application on the user's browsers.
在我们的应用程序开发过程中,添加gulp任务是非常关键的一点。 没有gulpfile,我们编写的所有代码都不会编译到我们的公共目录中,该目录在用户的浏览器上为我们的应用程序提供服务。
Go ahead and install the packages below as dev dependencies and update your gulpfile.
继续并安装以下软件包作为dev依赖项并更新gulpfile。
npm install --save-dev gulp gulp-jade gulp-less wiredep gulp-inject gulp-concat gulp-uglify gulp-bower gulp-nodemon gulp-plumber gulp-cssmin
This maybe overwhelming if you are not familiar with gulp tasks, take some time to read through automating tasks with gulp.
如果您不熟悉gulp任务,这可能会令人不知所措,请花一些时间阅读使用gulp自动化任务 。
/gulpfile.js
/gulpfile.js
var gulp = require ( 'gulp' ) ,
jade = require ( 'gulp-jade' ) ,
less = require ( 'gulp-less' ) ,
wiredep = require ( 'wiredep' ) . stream ,
gulpinject = require ( 'gulp-inject' )
concat = require ( 'gulp-concat' ) ,
uglify = require ( 'gulp-uglify' ) ,
bower = require ( 'gulp-bower' ) ,
nodemon = require ( 'gulp-nodemon' ) ,
plumber = require ( 'gulp-plumber' ) ,
cssmin = require ( 'gulp-cssmin' ) ,
mocha = require ( 'gulp-mocha' ) ,
istanbul = require ( 'gulp-istanbul' ) ,
karma = require ( 'karma' ) . Server ,
path = require ( 'path' ) ;
//Paths to watch for changes using the watch task.
var paths = {
jade : 'app/**/*.jade' ,
index : 'public/index.html' ,
images : [ 'app/images/**/*' ] ,
scripts : {
js : './public/js/index.js' ,
css : './public/css/*.css'
} ,
compileScripts : {
js : [ 'app/app.js' , 'app/js/**/*.js' ] ,
css : 'app/styles/*.+(less|css)'
} ,
serverTests : [
'test/server/**/*.js'
] ,
serverScripts : [
'server/controllers/*.js'
]
} ;
//Compile Jade files to html and save them into the public directory.
gulp . task ( 'jade:compile' , function ( ) {
gulp . src ( paths . jade )
. pipe ( jade ( {
pretty : true
} ) )
. pipe ( gulp . dest ( './public' ) ) ;
} ) ;
/ / Concatinate js into index . js , minify and save in public / js .
gulp . task ( 'js:minify' , function ( ) {
gulp . src ( paths . compileScripts . js )
. pipe ( concat ( 'index.js' ) )
. pipe ( uglify ( ) )
. pipe ( gulp . dest ( './public/js/' ) ) ;
} ) ;
/ / Concatinate custom css into styles . css , minify and save in public / css .
gulp . task ( 'css:minify' , function ( ) {
gulp . src ( paths . compileScripts . css )
. pipe ( less ( {
paths : [ path . join ( __dirname , 'styles' ) ]
} ) )
. pipe ( concat ( 'styles.css' ) )
. pipe ( cssmin ( ) )
. pipe ( plumber ( ) )
. pipe ( gulp . dest ( './public/css' ) ) ;
} ) ;
/ / Copy the images folder from app to public recursively
gulp . task ( 'copy:images' , function ( ) {
gulp . src ( paths . images )
. pipe ( gulp . dest ( './public/images' ) ) ;
} ) ;
/ / Run bower install .
gulp . task ( 'bower:run' , function ( ) {
bower ( ) ;
} ) ;
/ / Inject bower scripts and custom scripts into / public / index . html .
gulp . task ( 'scripts:inject' , [ 'jade:compile' ] , function ( ) {
gulp . src ( paths . index )
. pipe ( wiredep ( ) )
. pipe ( gulpinject ( gulp . src ( paths . scripts . js ) , { relative : true } ) )
. pipe ( gulpinject ( gulp . src ( paths . scripts . css ) , { relative : true } ) )
. pipe ( gulp . dest ( './public/' ) ) ;
} ) ;
/ / Rn nodemon .
gulp . task ( 'nodemon:run' , function ( ) {
nodemon ( {
script : 'index.js' ,
ext : 'js html' ,
ignore : [ 'public/**' , 'app/**' , 'node_modules/**' ]
} ) ;
} ) ;
gulp . task ( 'test:client' , function ( done ) {
new karma ( {
configFile : __dirname + '/karma.conf.js'
} , done ) . start ( ) ;
} ) ;
//Run the server tests and generate coverage reports
gulp . task ( 'test:server' , [ 'test:server:coverage' ] , function ( done ) {
gulp . src ( paths . serverTests )
. pipe ( mocha ( ) )
. pipe ( istanbul . writeReports ( {
dir : './coverage/server' ,
reporters : [ 'lcov' , 'json' , 'text' , 'text-summary' ]
} ) ) ;
} ) ;
gulp . task ( 'test:server:coverage' , function ( ) {
gulp . src ( paths . serverScripts )
. pipe ( istanbul ( ) )
. pipe ( istanbul . hookRequire ( ) ) ;
} ) ;
/ / Watch for changes in files .
gulp . task ( 'watch' , function ( ) {
gulp . watch ( paths . jade , [ 'jade:compile' ] ) ;
gulp . watch ( paths . compileScripts . js , [ 'js:minify' ] ) ;
gulp . watch ( paths . compileScripts . css , [ 'css:minify' ] ) ;
gulp . watch ( paths . index , [ 'scripts:inject' ] ) ;
gulp . watch ( paths . images , [ 'copy:images' ] )
} ) ;
/ / Default task .
gulp . task ( 'default' , [ 'bower:run' , 'jade:compile' , 'js:minify' , 'css:minify' , 'scripts:inject' , 'copy:images' ] ) ;
/ / Dev environment task .
gulp . task ( 'dev' , [ 'nodemon:run' , 'bower:run' , 'jade:compile' , 'js:minify' , 'css:minify' , 'scripts:inject' , 'watch' , 'copy:images' ] ) ;
更新.bowerrc (Update .bowerrc)
Set the default directory where bower components will be installed and run the inject gulp task after bower components are installed.
设置将安装Bower组件的默认目录,并在安装Bower组件后运行Inject gulp任务。
{
"directory" : "public/lib" ,
"scripts" : {
"postinstall" : "node_modules/.bin/gulp scripts:inject"
}
}
初始化和主题化我们的书签应用程序 ( Initializing And Theming Our Bookmark Application )
We will use Angular material's mdThemingProvider
and mdIconProvider
services to theme our application and set a placeholder svg icon for our authors respectively.
我们将使用Angular材质的mdThemingProvider
和mdIconProvider
服务为应用程序设置主题,并分别为作者设置一个占位符svg图标。
/app/app.js
/app/app.js
angular . module ( 'Bookmark.controllers' , [ ] ) ;
angular . module ( 'Bookmark.services' , [ ] ) ;
//Inject dependencies to the Bookmark module.
var Bookmark = angular . module ( 'Bookmark' , [
'ngMaterial' ,
'ngMdIcons' ,
'Bookmark.controllers' ,
'Bookmark.services'
] ) ;
Bookmark
. config ( [ '$mdThemingProvider' , function ( $mdThemingProvider ) {
//Set default theme
$mdThemingProvider . theme ( 'default' )
. primaryPalette ( 'blue' )
. accentPalette ( 'red' ) ;
} ] )
. config ( [ '$mdIconProvider' , function ( $mdIconProvider ) {
//Placeholder icon for author profile.
$mdIconProvider
. icon ( 'author' , './images/svg/person.svg' ) ;
} ] ) ;
主控制器和烤面包服务 ( Main Controller And Toast Service )
The main controller handles all requests in our main page when the application is loaded. Besides fetching all authors from the server, it also triggers our dialogs to add, edit or delete books and authors.
加载应用程序后,主控制器处理我们主页中的所有请求。 除了从服务器获取所有作者外,它还触发我们的对话框来添加,编辑或删除书籍和作者。
/app/js/controllers/main.js
/app/js/controllers/main.js
angular . module ( 'Bookmark.controllers' )
. controller ( 'MainCtrl' , [ '$rootScope' , '$scope' , '$mdSidenav' , '$mdDialog' , 'Authors' , 'Books' , 'Toast' ,
function ( $rootScope , $scope , $mdSidenav , $mdDialog , Authors , Books , Toast ) {
//Fetch all authors
Authors . all ( )
. then ( function ( authors ) {
$rootScope . authors = authors ;
$rootScope . selectedAuthor = authors [ 0 ] ;
} ) ;
//set an author as selected
$scope . selectAuthor = function ( author ) {
$rootScope . selectedAuthor = author ;
} ;
//toggle the visibility of the Sidenav
$scope . toggleSidenav = function ( ) {
$mdSidenav ( 'left' ) . toggle ( ) ;
} ;
//Dialog to create new author
$scope . newAuthorDialog = function ( ev ) {
$mdDialog . show ( {
templateUrl : 'views/dialogs/new-author.html' ,
controller : 'AuthorDialogCtrl' ,
parent : angular . element ( document . body ) ,
targetEvent : ev ,
clickOutsideToClose : true ,
fullscreen : true
} ) ;
} ;
//Show An author's profile
$scope . showAuthorProfile = function ( ev ) {
$mdDialog . show ( {
templateUrl : 'views/dialogs/author-profile.html' ,
controller : 'AuthorDialogCtrl' ,
parent : angular . element ( document . body ) ,
targetEvent : ev ,
clickOutsideToClose : true ,
fullscreen : true
} ) ;
} ;
//Dialog to create new book
$scope . newBookDialog = function ( ev ) {
$mdDialog . show ( {
templateUrl : 'views/dialogs/new-book.html' ,
controller : 'BookDialogCtrl' ,
locals : {
bookDetails : null
} ,
parent : angular . element ( document . body ) ,
targetEvent : ev ,
clickOutsideToClose : true ,
fullscreen : true
} ) ;
} ;
//Delete an existing book
$scope . deleteBook = function ( ev , book ) {
var deleteBook = $mdDialog . confirm ( )
. title ( 'Delete ' + book . name + '?' )
. textContent ( book . name + ' will be wiped off the face of the earth.' )
. ariaLabel ( 'Delete book' )
. ok ( 'Yes! do it!' )
. cancel ( 'No' ) ;
$mdDialog . show ( deleteBook )
. then ( function ( ) {
Books . delete ( book . id )
. then ( function ( ) {
$scope . selectedAuthor . Books = $scope . selectedAuthor . Books . filter ( function ( i ) {
return i . id !== book . id ;
} ) ;
Toast . show ( book . name + ' has been deleted.' , 'top right' , 3000 ) ;
} )
. catch ( function ( error ) {
Toast . show ( 'Error deleting ' + book . name + '. Please try again.' , 'top right' , 3000 ) ;
} ) ;
} ) ;
} ;
//Show modal to edit an existing book's details
$scope . editBook = function ( ev , book ) {
$mdDialog . show ( {
templateUrl : 'views/dialogs/edit-book.html' ,
controller : 'BookDialogCtrl' ,
locals : {
bookDetails : book
} ,
parent : angular . element ( document . body ) ,
targetEvent : ev ,
clickOutsideToClose : true ,
fullscreen : true
} ) ;
} ;
} ] ) ;
The toast service simply displays an angular toast notification on actions such as adding, editing or deleting authors and books.
祝酒服务仅在添加,编辑或删除作者和书籍等操作上显示有角度的祝酒通知。
app/js/services/toast.js
app / js / services / toast.js
angular . module ( 'Bookmark.services' )
. factory ( 'Toast' , [ '$mdToast' , function ( $mdToast ) {
return {
show : function ( text , position , delay ) {
$mdToast . show (
$mdToast . simple ( )
. textContent ( text )
. position ( position )
. hideDelay ( delay )
) ;
}
} ;
} ] ) ;
作者控制器和服务 ( Author Controller And Service )
The Author dialog controller has one function that makes a request to the new
method in the Author service which creates a new author in our database.
Author对话框控制器具有一个功能,该功能向Author服务中的new
方法发出请求,该服务在我们的数据库中创建一个新作者。
We then use the toast service defined earlier to show a success or error message.
然后,我们使用先前定义的Toast服务显示成功或错误消息。
app/js/controllers/author-dialog.js
app / js / controllers / author-dialog.js
angular . module ( 'Bookmark.controllers' )
. controller ( 'AuthorDialogCtrl' , [ '$rootScope' , '$scope' , '$mdDialog' , 'Toast' , 'Authors' ,
function ( $rootScope , $scope , $mdDialog , Toast , Authors ) {
//Save a new author
$scope . saveAuthor = function ( author ) {
Authors
. new ( author )
. then ( function ( newAuthor ) {
newAuthor . Books = [ ] ;
$rootScope . authors . push ( newAuthor ) ;
Toast . show ( 'Author successfully created' , 'top right' , 3000 ) ;
$mdDialog . cancel ( ) ;
} )
. catch ( function ( ) {
Toast . show ( 'Error creating author' , 'top right' , 3000 ) ;
} ) ;
} ;
} ] ) ;
app/js/services/authors.js
app / js / services / authors.js
angular . module ( 'Bookmark.services' )
. factory ( 'Authors' , [ '$http' , '$q' , function ( $http , $q ) {
return {
all : function ( ) {
var deferred = $q . defer ( ) ;
$http
. get ( '/authors' )
. then ( function ( response ) {
deferred . resolve ( response . data ) ;
} )
. catch ( function ( error ) {
deferred . reject ( error ) ;
} ) ;
return deferred . promise ;
} ,
//add new author
new : function ( author ) {
var deferred = $q . defer ( ) ;
$http
. post ( '/authors' , author )
. then ( function ( response ) {
deferred . resolve ( response . data ) ;
} )
. catch ( function ( error ) {
deferred . reject ( error ) ;
} ) ;
return deferred . promise ;
}
} ;
} ] ) ;
图书管理员和服务 ( Books Controller And Service )
The books controller has two methods saveBook
which creates a new book and updateBook
which edits an existing book.
书籍控制器有两种方法saveBook
创建新书籍,而updateBook
编辑现有书籍。
app/js/controllers/book-dialog.js
app / js / controllers / book-dialog.js
angular . module ( 'Bookmark.controllers' )
. controller ( 'BookDialogCtrl' , [ '$rootScope' , '$scope' , '$mdDialog' , 'Toast' , 'Books' , 'bookDetails' ,
function ( $rootScope , $scope , $mdDialog , Toast , Books , bookDetails ) {
$scope . book = bookDetails ;
if ( $scope . book ) {
$scope . book . publication_date = new Date ( $scope . book . publication_date ) ;
}
//create a new book
$scope . saveBook = function ( book ) {
book [ 'author_id' ] = $rootScope . selectedAuthor . id ;
Books
. new ( book )
. then ( function ( newBook ) {
Toast . show ( 'Book successfully created' , 'top right' , 3000 ) ;
$rootScope . selectedAuthor . Books . push ( newBook ) ;
$mdDialog . cancel ( ) ;
} )
. catch ( function ( ) {
Toast . show ( 'Error creating book' , 'top right' , 3000 ) ;
} ) ;
} ;
//Update a book's details
$scope . updateBook = function ( book ) {
Books
. update ( book )
. then ( function ( ) {
Toast . show ( 'Book successfully updated' , 'top right' , 3000 ) ;
$mdDialog . cancel ( ) ;
} )
. catch ( function ( ) {
Toast . show ( 'Error updating book' , 'top right' , 300 ) ;
} ) ;
} ;
} ] ) ;
The Books service has 3 methods new
, delete
and update
which serves BookDialogCtrl
and mainCtrl
图书服务具有new
, delete
和update
3种方法,分别用于BookDialogCtrl
和mainCtrl
app/js/services/books.js
app / js / services / books.js
angular . module ( 'Bookmark.services' )
. factory ( 'Books' , [ '$http' , '$q' , function ( $http , $q ) {
return {
new : function ( book ) {
var deferred = $q . defer ( ) ;
$http
. post ( '/books' , book )
. then ( function ( response ) {
deferred . resolve ( response . data ) ;
} )
. catch ( function ( error ) {
deferred . reject ( error ) ;
} ) ;
return deferred . promise ;
} ,
delete : function ( bookId ) {
var deffered = $q . defer ( ) ;
$http
. delete ( '/books/' + bookId )
. then ( function ( response ) {
deffered . resolve ( response . data ) ;
} )
. catch ( function ( error ) {
deffered . reject ( error ) ;
} ) ;
return deffered . promise ;
} ,
update : function ( book ) {
var deferred = $q . defer ( ) ;
$http
. put ( '/books/' + book . id , book )
. then ( function ( response ) {
deferred . resolve ( response . data ) ;
} )
. catch ( function ( error ) {
deferred . reject ( error ) ;
} ) ;
return deferred . promise ;
}
} ;
} ] ) ;
设计应用程序布局 ( Designing The Application Layout )
Sidenav和导航布局 (Sidenav and navigation layouts)
The sidenav contains a clickable list of all the authors in the Bookmark application. /app/index.jade
sidenav包含“书签”应用程序中所有作者的可单击列表。 /app/index.jade
doctype html
html(lang="en")
head
title bookmark App
include views/layouts/head
body(layout="row" ng-app="Bookmark" ng-controller="MainCtrl")
//Sidenav
md-sidenav.md-whiteframe-z2(md-component-id="left" md-is-locked-open="$mdMedia('gt-sm')")
md-toolbar
h1 Authors
md-content
md-list
//Loop through the authors list
md-list-item(ng-repeat="author in authors")
md-button(ng-click="selectAuthor(author)")
md-icon.author_avatar(md-svg-icon="author")
span(ng-class="{selected: author === selectedAuthor}") {{author.name}}
div.new_author
md-button(aria-label="new_author" class="md-fab md-raised md-primary md-mini")
ng-md-icon(icon="add")
md-tooltip(direction="top") Add new author
//Main toolbar
div(layout="column" flex)
md-toolbar(layout="row")
div.md-toolbar-tools
md-button.md-button-icon(hide-gt-sm aria-label="Menu" ng-click="toggleSidenav()")
ng-md-icon.authors(icon="view_module")
h1 Bookmark
//Main content
include views/layouts/footer
I will be using Angular material icons so make sure you go ahead and bower install the icons package bower install --save angular-material-icons
.
我将使用Angular material图标,因此请确保您继续并bower安装图标包bower install --save angular-material-icons
。
If you run gulp dev
on your terminal now, we should now have this simple layout.
如果您现在在终端上运行gulp dev
,我们现在应该具有这种简单的布局。
![书签导航和工具栏](https://scotch-res.cloudinary.com/image/upload/w_auto,q_auto:good,f_auto/media/1376/6BI3hlz8Ttmt36k0PY3j_Screen%20Shot%202016-08-25%20at%2017.44.46.png)
主要内容布局 (Main content layout)
The main content section holds the bullk of our application.
主要内容部分包含了我们的应用程序。
/app/index.jade
/app/index.jade
//Main content
md-content.md-default-theme
div.md-padding
h2(ng-show="selectedAuthor.Books.length") Books by {{selectedAuthor.name}}
div.author_actions
md-button.author_profile(aria-label="new_book" class="md-fab md-raised md-primary md-mini")
ng-md-icon(icon="person")
md-tooltip(direction="bottom") View {{selectedAuthor.name}}'s profile
md-button.new_book(aria-label="new_book" class="md-fab md-raised md-primary md-mini")
ng-md-icon(icon="my_library_add")
md-tooltip(direction="bottom") Add new book by {{selectedAuthor.name}}
md-content.md-padding(layout="column")
md-card(ng-repeat="book in selectedAuthor.Books")
div(layout="row" layout-align="end")
div.books_menu
md-button(aria-label="edit" class="md-fab md-raised md-mini")
ng-md-icon(icon="edit")
md-tooltip(direction="bottom") edit book details
md-button(aria-label="delete" class="md-fab md-raised md-mini")
ng-md-icon(icon="delete")
md-tooltip(direction="bottom") delete book details
md-card-title
md-card-title-text
span.md-headline {{book.name}}
span.md-subhead Publication date: {{ book.publication_date | date: mediumDate}}
md-card-content
div.md-subhead {{book.description}}
md-chips
md-chip ISBN Number: {{ book.isbn}}
While we are yet to wire up the functions of most of the buttons, run gulp dev
to view the updated layout.
当我们尚未连接大多数按钮的功能时,请运行gulp dev
来查看更新的布局。
![书签应用程序的主要内容布局](https://scotch-res.cloudinary.com/image/upload/w_auto,q_auto:good,f_auto/media/1376/AgI6CMVbT8Sq95hnD9cx_Screen%20Shot%202016-08-29%20at%2022.34.00.png)
创建作者并添加新书 ( Creating An Author and Adding New Books )
With the layout looking arguably swanky, let's dive into the meat of our application. In this section, we will manage to create a new author from a form and by extension, adding a new book to an author's profile.
布局看起来可以说是时髦的,让我们深入研究应用程序的内容。 在本节中,我们将设法通过表单并通过扩展来创建新作者,并将新书添加到作者的个人资料中。
To get started, add a click event to the new author button.
首先,将click事件添加到新的author按钮 。
/app/index.jade
/app/index.jade
div.new_author
//Show dialog to create new author on click.
md-button(aria-label="new_author" class="md-fab md-raised md-primary md-mini" ng-click="newAuthorDialog($event)")
ng-md-icon(icon="add")
md-tooltip(direction="top") Add new author
The newAuthorDialog
method in our mainCtrl
pops up a dialog box that will contain our new author form. Read more about all the possible $mdDialog
options in the official angular material documentation.
newAuthorDialog
中的newAuthorDialog
方法mainCtrl
弹出一个对话框,其中将包含我们的新作者表单。 在官方的角度材料文档中阅读有关所有可能的$mdDialog
选项的更多信息。
To display the dialog that creates a new author, add the new-author template.
要显示创建新作者的对话框,请添加新作者模板。
/app/views/dialogs/new-author.jade
/app/views/dialogs/new-author.jade
md-dialog(aria-label="New Author" flex="50")
md-toolbar
.md-toolbar-tools
h2 Create New Author
form(name="authorForm" ng-submit="saveAuthor(author)" novalidate)
md-dialog-content.md-padding
md-input-container.md-block
label Name
input(ng-model="author.name" type="text" name="author.name" required)
md-input-container.md-block
label Biography
textarea(ng-model="author.bio" rows="5" name="author.bio" required)
md-dialog-actions
md-button(aria-label="Save Author" type="submit" ng-disabled="authorForm.$invalid") Save
To display the dialog to create a new book, add the new-book template.
要显示创建新书的对话框,请添加新书模板。
** app/views/dialogs/new-book.jade**
**应用程序/视图/对话框/新书.jade **
md-dialog(aria-label="New Book" flex="50")
md-toolbar
.md-toolbar-tools
h2 Add New Book
form(name="bookForm" ng-submit="saveBook(book)" novalidate)
md-dialog-content.md-padding
div(layout="row")
md-input-container(flex)
label Name
input(ng-model="book.name" type="text" name="book.name" required)
md-input-container(flex)
label Publication Date
md-datepicker(ng-model="book.publication_date" type="text" name="book.publication_date" required)
md-input-container.md-block
label ISBN number
input(ng-model="book.isbn" type="text" name="book.isbn" required)
md-input-container.md-block
label Description
textarea(ng-model="book.description" rows="5" name="book.description" required)
md-dialog-actions
md-button(aria-label="Save Book" type="submit" ng-disabled="bookForm.$invalid") Add Book
On clicking the submit, the saveAuthor
method in AuthorDialogCtrl
is called and the newly added details are passed to it which in turns calls the new
method in our Authors
service.
单击提交后,将saveAuthor
中的saveAuthor
方法, AuthorDialogCtrl
新添加的详细信息传递给它,这AuthorDialogCtrl
在Authors
服务中调用new
方法。
Thanks to Angular's two way data binding , the author is automatically visible to our sidenav when we push the author details to the authors variable in rootScope.
多亏了Angular的双向数据绑定,当我们将作者详细信息推送到rootScope中的authors变量时,作者对我们的sidenav自动可见。
查看作者的个人资料 ( Viewing An Author's Profile )
Just like we did while creating a new author, add a new event handler to pop the dialog containing the author's profile.
就像我们创建新作者时所做的一样,添加新的事件处理程序以弹出包含作者个人资料的对话框。
/app/index.jade
/app/index.jade
div.author_actions
md-button.author_profile(aria-label="view_profile" class="md-fab md-raised md-primary md-mini" ng-click="showAuthorProfile($event)")
ng-md-icon(icon="person")
md-tooltip(direction="bottom") View {{selectedAuthor.name}}'s profile
To display the dialog that shows an author's profile, add the author-profile template.
要显示显示作者个人资料的对话框,请添加作者个人资料模板。
** app/views/dialogs/author-profile.jade**
**应用程序/视图/对话框/作者个人资料.jade **
md-dialog(aria-label="Author Profile" flex="50")
md-toolbar
.md-toolbar-tools
h2 {{$root.selectedAuthor.name}}'s profile
md-dialog-content.md-padding
md-list
md-list-item
div.md-list-item-text
h3 Biography
p {{$root.selectedAuthor.bio}}
md-divider
md-list-item
div.md-list-item-text
h3 Books on Bookmark
p {{$root.selectedAuthor.Books.length}}
On clicking the view profile button, the User's profile will be displayed as shown below.
单击查看配置文件按钮后,将显示用户的配置文件,如下所示。
Run gulp dev
on your terminal to try this out.
在您的终端上运行gulp dev
进行尝试。
![查看作者资料](https://scotch-res.cloudinary.com/image/upload/w_auto,q_auto:good,f_auto/media/1376/I2m5MdEJQv2V4rKlFM2q_view%20author%20profile.png)
更新和删除现有书籍 ( Updating And Deleting An Existing Book )
To edit and delete an book, add click events to the edit and delete button for each book.
要编辑和删除一本书,请将点击事件添加到每本书的“编辑和删除”按钮中。
/app/index.jade
/app/index.jade
//Edit and Delete Books
div.books_menu
md-button(aria-label="edit" class="md-fab md-raised md-mini" ng-click="editBook($event, book)")
ng-md-icon(icon="edit")
md-tooltip(direction="bottom") edit book details
md-button(aria-label="delete" class="md-fab md-raised md-mini" ng-click="deleteBook($event, book)")
ng-md-icon(icon="delete")
md-tooltip(direction="bottom") delete book
We already have the deleteBook
method defined in MainCtrl which presents the user with a confirmation dialog to either accept or cancel the delete request.
我们已经在MainCtrl中定义了deleteBook
方法,该方法向用户显示一个确认对话框,以接受或取消删除请求。
To show the dialog with the edit book form, let's add the edit-book dialog template.
要显示带有编辑书形式的对话框,让我们添加编辑书对话框模板。
/app/views/dialogs/edit-book.jade
/app/views/dialogs/edit-book.jade
md-dialog(aria-label="Edit Book" flex="50")
md-toolbar
.md-toolbar-tools
h2 Edit {{book.name}} {{book.publication_date | date: 'm/d/yyyy'}}
form(name="bookForm" ng-submit="updateBook(book)" novalidate)
md-dialog-content.md-padding
div(layout="row")
md-input-container(flex)
label Name
input(ng-model="book.name" type="text" name="book.name" required)
md-input-container(flex)
label Publication Date
md-datepicker(ng-model="book.publication_date" type="text" name="book.publication_date" required)
md-input-container.md-block
label ISBN number
input(ng-model="book.isbn" type="text" name="book.isbn" required)
md-input-container.md-block
label Description
textarea(ng-model="book.description" rows="5" name="book.description" required)
md-dialog-actions
md-button(aria-label="Save Book" type="submit" ng-disabled="bookForm.$invalid") Update Book
At this point, our Bookmark application performs the basic CRUD operations for the authors and the books.
此时,我们的Bookmark应用程序将为作者和书籍执行基本的CRUD操作。
结论 ( Conclusion )
In this section, we managed to:
在本节中,我们设法:
- Setup the frontend for our Bookmark application. 为我们的书签应用程序设置前端。
- Theme and define our app layout 设置主题并定义我们的应用布局
- Create, view, update and delete book details. 创建,查看,更新和删除书籍详细信息。
Just like we did in the first part of this series, you may also challenge yourself to edit and delete an author's details.
就像我们在本系列第一部分中所做的一样,您可能还会挑战自己编辑和删除作者的详细信息。
In the last part of the series, we will right tests for our app and deploy it to Heroku.
在本系列的最后一部分,我们将对应用进行正确的测试并将其部署到Heroku。
翻译自: https://scotch.io/tutorials/creating-an-angularjs-application-with-sequelize-part-2
sequelize 创建表