Integración con MySQL, errores solucionados

This commit is contained in:
Juan Ferrer Toribio 2017-04-28 15:04:29 +02:00
parent 1eb0fb9c8d
commit ce2d7936af
37 changed files with 269 additions and 175 deletions

1
.gitignore vendored
View File

@ -3,3 +3,4 @@ spliting.js
build
npm-debug.log
debug.log
datasources.development.json

View File

@ -23,7 +23,7 @@
<vn-textfield vn-one label="Email" field="$ctrl.client.email"></vn-textfield>
<vn-autocomplete vn-one
field="$ctrl.client.salesPerson"
url="/client/api/SalesPeople"
url="/client/api/Employees"
show-field="name"
value-field="id"
label="Comercial">

View File

@ -43,7 +43,7 @@
<vn-textfield vn-two label="IBAN" field="fiscal.client.iban"></vn-textfield>
<vn-autocomplete vn-two
field="fiscal.client.payMethod"
url="/client/api/PaymentMethods"
url="/client/api/PayMethods"
show-field="name"
value-field="id"
label="Forma de pago">

View File

@ -3,8 +3,7 @@ import Component from '../lib/component';
import './style.scss';
/**
* Combobox like component with search and partial data loading
* features.
* Combobox like component with search and partial data loading features.
*/
export default class Autocomplete extends Component {
constructor($element, $scope, $http, vnPopover) {

View File

@ -1,7 +1,7 @@
import {module as _module} from '../module';
import * as resolveFactory from '../lib/resolveDefaultComponents';
import * as util from '../lib/util';
require ('./style.css');
require('./style.css');
const _NAME = 'card';
export const NAME = util.getName(_NAME);

View File

@ -1,6 +1,7 @@
import './mdl-override.css';
import './styles/fonts/mdi-override.css';
import './textfield/index';
import './watcher/index';
import './paging/index';
import './icon/index';
@ -20,8 +21,6 @@ export {NAME as RADIO, directive as RadioDirective} from './radio/radio';
export {NAME as RADIO_MDL, factory as radionMdl} from './radio/radio.mdl';
export {NAME as TEXTAREA, directive as TextareaDirective} from './textarea/textarea';
export {NAME as TEXTAREA_MDL, factory as textareaMdl} from './textarea/textarea.mdl';
export {NAME as TEXTFIELD, directive as TextfieldDirective} from './textfield/textfield';
export {NAME as TEXTFIELD_MDL, factory as textfieldMdl} from './textfield/textfield.mdl';
export {NAME as LABEL, directive as LabelDirective} from './label/label';
export {NAME as LABEL_MDL, factory as labelMdl} from './label/label.mdl';
export {NAME as ICON_BUTTON, directive as IconButtonDirective} from './icon-button/icon-button';

View File

@ -33,20 +33,19 @@ export function directive(interpolate, compile, $window) {
if (!validations || validations.length == 0)
return;
let errorMsg = angular.element('<span class="mdl-textfield__error"></span>');
element.after(errorMsg);
let input = ctrl[0];
let form = ctrl[1];
let parent = element.parent();
let errorSpan = angular.element('<span class="mdl-textfield__error"></span>');
let errorMsg;
let errorShown = false;
input.$validators.entity = function(value) {
try {
validateAll(value, validations);
return true;
} catch (e) {
errorMsg.text(e.message);
parent.attr('title', e.message);
errorMsg = e.message;
if (errorShown) changeError();
return false;
}
};
@ -54,14 +53,27 @@ export function directive(interpolate, compile, $window) {
scope.$watch(function() {
return (form.$submitted || input.$dirty) && input.$invalid;
}, function(value) {
let parent = element.parent();
if (value) {
changeError();
parent.addClass('invalid');
errorMsg[0].style.display = 'block';
} else {
element.after(errorSpan);
} else if (errorShown) {
parent.removeClass('invalid');
errorMsg[0].style.display = 'none';
parent.removeAttr('title');
errorSpan.remove();
errorSpan.empty();
}
errorShown = value;
});
function changeError() {
let parent = element.parent();
errorSpan.text(errorMsg);
parent.attr('title', errorMsg);
}
}
}
module.directive('vnValidation', directive);

View File

@ -2,15 +2,17 @@ import {module} from '../module';
/**
* Formats a phone number putting a space every three digits.
*
* @return {String} The formated number
*/
export default function phone() {
return function(input) {
input = input || '';
let out = '';
for(let i = 0; i < input.length; i++) {
out = out + input.charAt(i);
if((i+1) % 3 == 0)
out = out +' ';
for (let i = 0; i < input.length; i++) {
out += input.charAt(i);
if ((i + 1) % 3 == 0)
out += ' ';
}
return out;
};

View File

@ -1,7 +1,7 @@
import {module} from '../module';
export {factory as mdlFactory} from './index.mdl';
import './index.mdl';
import './style.css';
import * as resolveFactory from '../lib/resolveDefaultComponents';
require('./style.css');
const _NAME = 'icon';
export const NAME = 'vnIcon';
@ -12,7 +12,7 @@ export function directive(resolver) {
template: function(_, attrs) {
return resolver.getTemplate(_NAME, attrs);
}
}
};
}
directive.$inject = [resolveFactory.NAME];

View File

@ -3,11 +3,17 @@
*/
export default class Component {
/**
* The element window.
* The component owner window.
*/
get window() {
return this.document.defaultView;
}
/**
* The component owner document.
*/
get document() {
return this.element.ownerDocument;
}
/**
* Contructor.
*
@ -16,7 +22,6 @@ export default class Component {
constructor($element) {
this.element = $element[0];
this.element.$ctrl = this;
this.document = $element[0].ownerDocument;
}
}
Component.$inject = ['$element'];

View File

@ -3,14 +3,14 @@ import {module} from '../module';
export const NAME = 'vnInputAttrsNormalizer';
export class InputAttrsNormalizer {
normalize(attrs) {
if(attrs.field) {
if (attrs.field) {
let split = attrs.field.split('.');
let len = split.length;
if(len == 0)
throw new Error (`Attribute 'field' can not be empty`);
if(len > 3)
throw new Error (`Attribute 'field' must have this syntax: [ctrl].[entity].[field]`);
if (len == 0)
throw new Error(`Attribute 'field' can not be empty`);
if (len > 3)
throw new Error(`Attribute 'field' must have this syntax: [ctrl].[entity].[field]`);
let i = len - 1;
let field = split[i--];

View File

@ -0,0 +1,69 @@
import {module} from '../module';
import Component from '../lib/component';
import * as resolveFactory from '../lib/resolveDefaultComponents';
import * as normalizerFactory from '../lib/inputAttrsNormalizer';
import './style.scss';
import './index.mdl';
export default class Textfield extends Component {
constructor($element, $scope, $attrs) {
super($element);
let input = this.input = this.element.querySelector('input');
input.addEventListener('input',
() => this.checkValue());
input.addEventListener('focus',
() => this.checkValue());
input.addEventListener('blur',
() => this.showClear(false));
let clearButton = this.element.querySelector('button');
clearButton.addEventListener('click',
() => this.onClearClick());
clearButton.addEventListener('mousedown',
event => event.preventDefault());
// input.value = ' ';
let div = this.element.firstChild;
componentHandler.upgradeElement(div);
}
link($scope, $attrs) {
let mdlTextField = this.element.firstChild.MaterialTextfield;
$scope.$watch($attrs.model,
() => mdlTextField.updateClasses_());
mdlTextField.updateClasses_();
}
onClearClick() {
this.input.value = '';
this.checkValue();
let event = this.document.createEvent('HTMLEvents');
event.initEvent('change', false, true);
this.input.dispatchEvent(event);
}
checkValue() {
this.showClear(this.input.value);
}
showClear(show) {
let clearButton = this.element.querySelector('button');
clearButton.style.visibility = show ? 'visible' : 'hidden';
}
}
Textfield.$inject = ['$element', '$scope', '$attrs'];
directive.$inject = [resolveFactory.NAME, normalizerFactory.NAME];
export function directive(resolve, normalizer) {
return {
restrict: 'E',
template: function(_, attrs) {
normalizer.normalize(attrs);
return resolve.getTemplate('textfield', attrs);
},
link: function($scope, $element, $attrs, $ctrl) {
$ctrl.link($scope, $attrs);
},
controller: Textfield
};
}
module.directive('vnTextfield', directive);

View File

@ -5,11 +5,8 @@
name="*[name]*"
ng-model="*[model]*"
vn-validation="*[rule]*"
*[enabled]*
ng-focus="onFocusInput(*[model]*, $event)"
ng-blur="onBlurInput($event)"
/>
<button type="button" class="mdl-chip__action" ng-click="clickClear('*[model]*')">
*[enabled]*/>
<button type="button" class="mdl-chip__action" title="Clear text">
<i class="material-icons">clear</i>
</button>
<label class="mdl-textfield__label" translate>*[label]*</label>

View File

@ -0,0 +1,16 @@
import {module} from '../module';
export const NAME = 'vnTextfieldMdlFactory';
export function factory() {
return {
template: require('./index.mdl.html'),
default: {
label: 'text',
className: 'mdl-textfield--floating-label',
type: 'text'
}
};
}
module.factory(NAME, factory);

View File

@ -1,11 +0,0 @@
.mdl-chip__action{
position: absolute;
top: 0px;
right: -6px;
margin: 22px 0px;
visibility: hidden;
}
.material-icons{
font-size: 18px;
}

View File

@ -0,0 +1,12 @@
vn-textfield {
.mdl-chip__action{
position: absolute;
top: 0px;
right: -6px;
margin: 22px 0px;
visibility: hidden;
}
.material-icons{
font-size: 18px;
}
}

View File

@ -1,59 +0,0 @@
import {module as _module} from '../module';
import * as resolveFactory from '../lib/resolveDefaultComponents';
import * as normalizerFactory from '../lib/inputAttrsNormalizer';
import * as util from '../lib/util';
require('./style.css');
const _NAME = 'textfield';
export const NAME = util.getName(_NAME);
directive.$inject = [resolveFactory.NAME, normalizerFactory.NAME, '$parse'];
export function directive(resolve, normalizer, $parse) {
return {
restrict: 'E',
template: function(_, attrs) {
normalizer.normalize(attrs);
return resolve.getTemplate(_NAME, attrs);
},
link: function(scope, element, attrs) {
scope.$watch(attrs.model, () => {
let mdlField = element[0].firstChild.MaterialTextfield;
if (mdlField)
mdlField.updateClasses_();
});
componentHandler.upgradeElement(element[0].firstChild);
scope.clickClear = function(model) {
setTimeout(() => {
scope.$apply(function() {
$parse(model).assign(scope, "");
});
});
};
scope.onFocusInput = function(model, event) {
var buttonClear = event.target.parentNode.querySelector("button");
checkModelValue(buttonClear, model);
};
scope.onBlurInput = function(event) {
var buttonClear = event.target.parentNode.querySelector("button");
setTimeout(() => {
buttonClear.style.visibility = "hidden";
}, 80);
};
element.on('input', function() {
var buttonClear = this.querySelector("button");
var model = this.querySelector("input").value;
checkModelValue(buttonClear, model);
});
function checkModelValue(buttonClear, model) {
if (model !== undefined && model !== "")
buttonClear.style.visibility = "visible";
else
buttonClear.style.visibility = "hidden";
}
}
};
}
_module.directive(NAME, directive);

View File

@ -1,24 +0,0 @@
import {module as _module} from '../module';
import * as util from '../lib/util';
import * as constant from '../lib/constants';
import template from './textfield.mdl.html';
const _NAME = 'textfield';
const DEFAULT_LABEL = 'text';
const DEFAULT_CLASS = 'mdl-textfield--floating-label';
const DEFAULT_TYPE = 'text';
export const NAME = util.getFactoryName(_NAME + constant.MATERIAL_DESIGN_FRAMEWORK);
export function factory() {
return {
template: template,
default: {
label: DEFAULT_LABEL,
className: DEFAULT_CLASS,
type: DEFAULT_TYPE
}
};
}
_module.factory(NAME, factory);

View File

@ -2,7 +2,7 @@
"ids": {
"User": 2,
"AccessToken": 4,
"Client": 23,
"Client": 24,
"PaymentMethod": 4,
"SalesPerson": 11,
"Address": 87,
@ -28,7 +28,8 @@
"16": "{\"name\":\"Floristeria Antonieta\",\"fi\":\"2345234523d\",\"socialName\":\"23452345assdfgsdfgt\",\"active\":true,\"dueDay\":5,\"id\":16,\"modify\":\"FiscalData\",\"email\":\"antonieta@gmail.com\",\"phone\":\"654654654\",\"mobile\":\"654456456\",\"fax\":\"456456456\",\"street\":\"asdfasdf\",\"salesPerson\":8,\"city\":\"Albalat de la Ribera\",\"postcode\":\"46532\"}",
"19": "{\"name\":\"Planticas Eustaquio\",\"fi\":\"789456123B\",\"socialName\":\"Eustaquio Martinez\",\"active\":true,\"dueDay\":5,\"id\":19,\"email\":\"peustaquio@hotmail.es\",\"city\":\"Polinya\",\"postcode\":\"46231\",\"phone\":\"963215486\"}",
"21": "{\"name\":\"Ramos Antonieta\",\"fi\":\"B89564289\",\"socialName\":\"Antonia SL\",\"active\":true,\"dueDay\":5,\"id\":21,\"email\":\"ramos@rantonieta.es\",\"salesPerson\":8,\"phone\":\"986574232\"}",
"22": "{\"name\":\"Plantas Raimundo\",\"fi\":\"2536418B\",\"socialName\":\"Plantas Raimundo SL\",\"dueDay\":5,\"id\":22,\"email\":\"jose@plantasraimundo.com\",\"phone\":\"963254289\",\"mobile\":\"641967586\",\"salesPerson\":3,\"city\":\"Sueca\",\"postcode\":\"46985\"}"
"22": "{\"name\":\"Plantas Raimundo\",\"fi\":\"2536418B\",\"socialName\":\"Plantas Raimundo SL\",\"dueDay\":5,\"id\":22,\"email\":\"jose@plantasraimundo.com\",\"phone\":\"963254289\",\"mobile\":\"641967586\",\"salesPerson\":3,\"city\":\"Sueca\",\"postcode\":\"46985\"}",
"23": "{\"name\":\"ddddddddd\",\"fi\":\"sdsdsd\",\"socialName\":\"sdsdsd\",\"dueDay\":5,\"id\":23}"
},
"PaymentMethod": {
"1": "{\"name\":\"Tarjeta\",\"id\":1}",

View File

@ -11,12 +11,13 @@
"compression": "^1.0.3",
"cors": "^2.5.2",
"helmet": "^1.3.0",
"loopback": "^2.22.0",
"loopback-boot": "^2.6.5",
"loopback-component-explorer": "^2.4.0",
"serve-favicon": "^2.0.1",
"strong-error-handler": "^1.0.1",
"loopback-connector-mysql": "^3.0.0",
"loopback-datasource-juggler": "^2.39.0",
"loopback": "^2.22.0"
"serve-favicon": "^2.0.1",
"strong-error-handler": "^1.0.1"
},
"devDependencies": {
"eslint": "^2.13.1",

View File

@ -1,7 +1,9 @@
module.exports = function(app) {
/*
var User = app.models.User;
User.create ({
email: 'admin@admin.com',
password: '1234'
}, function (err, user) {});
*/
};

View File

@ -1,4 +1,3 @@
'use strict';
module.exports = function(server) {
// Install a `/` route that returns server status

View File

@ -1,6 +1,16 @@
{
"db": {
"db": {
"name": "db",
"connector": "memory"
},
"auth": {
"name": "mysql",
"connector": "mysql",
"database": "auth",
"debug": false,
"host": "localhost",
"port": 3306,
"username": "root",
"password": ""
}
}

View File

@ -14,12 +14,24 @@
]
},
"User": {
"dataSource": "db",
"public": false
"dataSource": "auth",
"public": true
},
"AccessToken": {
"dataSource": "db",
"public": false,
"dataSource": "auth",
"public": true,
"strict": true
},
"ACL": {
"dataSource": "auth",
"public": true
},
"RoleMapping": {
"dataSource": "auth",
"public": true
},
"Role": {
"dataSource": "auth",
"public": true
}
}

View File

@ -8,11 +8,11 @@
"id": true,
"description": "Identifier"
},
"street": {
"consignee": {
"type": "string",
"required": true
},
"consignee": {
"street": {
"type": "string",
"required": true
},
@ -23,8 +23,8 @@
"postcode": {
"type": "string"
},
"enabled": {
"type": "boolean"
"province": {
"type": "Number"
},
"phone": {
"type": "string"
@ -32,6 +32,9 @@
"mobile": {
"type": "string"
},
"enabled": {
"type": "boolean"
},
"default": {
"type": "boolean"
}

View File

@ -2,8 +2,8 @@
module.exports = function(Client) {
// Validations
Client.validatesUniquenessOf('name', {message: 'El nombre debe ser unico'});
Client.validatesUniquenessOf('fi', {message: 'El nif/cif debe ser unico'});
Client.validatesUniquenessOf('name', {message: 'El nombre debe ser único'});
Client.validatesUniquenessOf('fi', {message: 'El NIF/CIF debe ser único'});
Client.validatesPresenceOf('socialName', {message: 'Debe especificarse la razón social'});
Client.validatesFormatOf('postcode', {allowNull: true, with: /^\d+$/, message: 'El código postal solo debe contener números'});
Client.validatesLengthOf('postcode', {allowNull: true, min: 3, max: 10});

View File

@ -35,12 +35,15 @@
"street": {
"type": "string"
},
"consignee": {
"type": "string"
},
"city": {
"type": "string"
},
"province": {
"type": "Number"
},
"country": {
"type": "Number"
},
"postcode": {
"type": "string"
},
@ -71,7 +74,7 @@
"dueDay": {
"type": "Number"
},
"gestdoc": {
"dms": {
"type": "Number"
},
"surcharge": {
@ -94,6 +97,16 @@
"model": "Account",
"foreignKey": "id"
},
"payMethod": {
"type": "hasOne",
"model": "PayMethod",
"foreignKey": "id"
},
"salesPerson": {
"type": "hasOne",
"model": "Employee",
"foreignKey": "id"
},
"addresses": {
"type": "hasMany",
"model": "Address",

View File

@ -20,11 +20,11 @@
},
"relations": {
"salesPerson": {
"type": "belongsTo",
"model": "SalesPerson",
"type": "hasOne",
"model": "Employee",
"foreignKey": "id"
},
"clients": {
"client": {
"type": "hasOne",
"model": "Client",
"foreignKey": "id"

View File

@ -1,5 +1,5 @@
{
"name": "SalesPerson",
"name": "Employee",
"base": "PersistedModel",
"validateUpsert": true,
"properties": {

View File

@ -1,5 +1,5 @@
{
"name": "PaymentMethod",
"name": "PayMethod",
"base": "PersistedModel",
"validateUpsert": true,
"properties": {

View File

@ -13,6 +13,13 @@
"required": true
}
},
"relations": {
"country": {
"type": "hasOne",
"model": "Country",
"foreignKey": "id"
}
},
"acls": [
{
"accessType": "*",

View File

@ -14,6 +14,7 @@
"loopback": "^2.38.0",
"loopback-boot": "^2.6.5",
"loopback-component-explorer": "^2.7.0",
"loopback-connector-mysql": "^3.0.0",
"loopback-datasource-juggler": "^2.54.0",
"serve-favicon": "^2.0.1",
"strong-error-handler": "^1.2.1"

View File

@ -1,4 +1,4 @@
module.exports = function(server) {
server.enableAuth();
//server.enableAuth();
};

View File

@ -1,6 +1,13 @@
module.exports = function(server) {
var router = server.loopback.Router();
let router = server.loopback.Router();
router.get('/status', server.loopback.status());
server.use(router);
/*
let ds = server.dataSources.auth;
//ds.automigrate(function() {
ds.autoupdate(function() {
console.log('Tables migrated!');
});
*/
};

View File

@ -3,5 +3,25 @@
"name": "db",
"connector": "memory",
"file": "db.json"
},
"auth": {
"name": "mysql",
"connector": "mysql",
"database": "auth",
"debug": false,
"host": "localhost",
"port": 3306,
"username": "root",
"password": ""
},
"vn": {
"name": "mysql",
"connector": "mysql",
"database": "vn",
"debug": false,
"host": "localhost",
"port": 3306,
"username": "root",
"password": ""
}
}

View File

@ -14,60 +14,60 @@
]
},
"User": {
"dataSource": "db",
"dataSource": "auth",
"public": true
},
"AccessToken": {
"dataSource": "db",
"dataSource": "auth",
"public": true,
"strict": true
},
"ACL": {
"dataSource": "db",
"dataSource": "auth",
"public": true
},
"RoleMapping": {
"dataSource": "db",
"dataSource": "auth",
"public": true
},
"Role": {
"dataSource": "db",
"dataSource": "auth",
"public": true
},
"Client": {
"dataSource": "db",
"dataSource": "vn",
"public": true
},
"PaymentMethod": {
"dataSource": "db",
"PayMethod": {
"dataSource": "vn",
"public": true
},
"SalesPerson": {
"dataSource": "db",
"Employee": {
"dataSource": "vn",
"public": true
},
"Address": {
"dataSource": "db",
"dataSource": "vn",
"public": true
},
"Agency": {
"dataSource": "db",
"dataSource": "vn",
"public": true
},
"Province": {
"dataSource": "db",
"dataSource": "vn",
"public": true
},
"Country": {
"dataSource": "db",
"dataSource": "vn",
"public": true
},
"ClientObservation": {
"dataSource": "db",
"dataSource": "vn",
"public": true
},
"Account": {
"dataSource": "db",
"dataSource": "vn",
"public": true
}
}