Lorenzo Gonzalez Gascón
I'm teacher in computer Science in a high school, the high school is named "CIPFP Mislata" in Spain-Valencia-Mislata .
¿Quieres que mis alumnos hagan las prácticas en tu empresa?
¡Ponte en contacto conmigo!
<div ng-controller="FacturaController">
<h3>Dentro del controlador Factura</h3>
Empresa:{{factura.empresa}}<br>
<div ng-controller="LineaFacturaController">
<h3>Dentro del controlador LineaFactura</h3>
Empresa:{{factura.empresa}}<br>
Concepto:{{lineaFactura.concepto}}<br>
<button ng-click="avisar(lineaFactura.concepto)">Avisar</button>
</div>
</div>
app.controller("FacturaController",FacturaController);
function FacturaController($scope) {
$scope.factura={
id:4,
importeTotal:34,
empresa:"Persianas López"
};
$scope.tiposIVA={
general:21,
reducido:10,
superreducido:4
}
$scope.avisar=function(message) {
alert(message);
}
}
app.controller("LineaFacturaController",LineaFacturaController);
function LineaFacturaController($scope) {
$scope.lineaFactura={
id:45,
cantidad:2,
precioUnitario:5,
concepto:"Mosquitera"
}
}
Sencillo: Solo el controlador y el HTML
$scope soup: No podemos saber cada propiedad de donde viene. Ej: "factura" , "lineaFactura", "avisar"
Código muy acoplado
<div ng-controller="FacturaController as facturaController">
<h3>Dentro del controlador Factura</h3>
Empresa:{{facturaController.factura.empresa}}<br>
<div ng-controller="LineaFacturaController as lineaFacturaController">
<h3>Dentro del controlador LineaFactura</h3>
Empresa:{{facturaController.factura.empresa}}<br>
Concepto:{{lineaFacturaController.lineaFactura.concepto}}<br>
<button ng-click="facturaController.avisar(lineaFacturaController.lineaFactura.concepto)">Avisar</button>
</div>
</div>
app.controller("FacturaController",FacturaController);
function FacturaController() {
this....;
}
app.controller("LineaFacturaController",LineaFacturaController);
function LineaFacturaController() {
this.....;
}
Ya no usamos "$scope" sino "this"
Ya sabemos a que controlador pertenece cada propiedad
Ya no hay herencia de "$scope". Está aislado
Usamos un "prefijo" delante de las propiedades
Dependemos de que el controlador padre exista y que tenga la propiedad exacta que necesitamos. Ej: "facturaController" con la propiedad "factura"
Mas complejo el usar "$watch"
Código acoplado
Entrada
Salida
Ej: Calendario , Botón , Arbol , etc.
Ej: ng-repeat , ng-hide , etc.
<!-- directiva -->
<mi-factura></mi-factura>
<!-- template de <mi-factura> -->
<h3>Dentro de la directiva Factura</h3>
Empresa:{{miFactura.factura.empresa}}<br>
<mi-linea-factura
factura='miFactura.factura'
on-button="miFactura.avisar(mensaje)"
></mi-linea-factura>
<!-- template de <mi-linea-factura> -->
<h3>Dentro de la directiva LineaFactura</h3>
Empresa:{{miLineaFactura.factura.empresa}}<br>
Concepto:{{miLineaFactura.lineaFactura.concepto}}<br>
<button ng-click="miLineaFactura.onButton({mensaje:miLineaFactura.lineaFactura.concepto})">Avisar</button>
app.directive("miFactura",FacturaDirective);
function FacturaDirective() {
return {
templateUrl :"mi-factura-tpl.html",
controller:function() {
this....
},
controllerAs:"miFactura",
scope:{
},
bindToController:{
}
}
}
app.directive("miLineaFactura",LineaFacturaDirective);
function LineaFacturaDirective() {
return {
templateUrl :"mi-linea-factura-tpl.html",
controller:function() {
this....
},
controllerAs:"miLineaFactura",
scope:{
},
bindToController:{
factura:"=",
onButton:"&"
}
}
};
Uso de parámetros: Ya no dependemos para nada del controlador padre
Usamos un "prefijo" delante de las propiedades que ahora es innecesario
Un poco "verbose" para crear el componente
Acceso a información privada
.....
controller:function() {
......
this.privateValue="s3cret";
}
......
.....
require:"^miFactura",
link: function(scope, element, attrs, miFacturaController) {
console.log("Valor privado:"+miFacturaController.privateValue)
}
......
El "this" del controlador es un API pública
para ser usada por otra directiva.
Directiva "miFactura"
Directiva "miLineaFacura"
Problema con el prefijo si reusamos el HTML
<linea-factura-update></linea-factura-update> //Prefijo lineaFacturaUpdate
<linea-factura-insert></linea-factura-insert> //Prefijo lineaFacturaInsert
<linea-factura-delete></linea-factura-delete> //Prefijo lineaFacturaDelete
<!-- template de <mi-linea-factura> -->
<h3>Dentro de la directiva LineaFactura</h3>
Empresa:{{lineaFactura.factura.empresa}}<br>
Concepto:{{lineaFactura.lineaFactura.concepto}}<br>
<button ng-click="vm.onButton({mensaje:lineaFactura.lineaFactura.concepto})">Avisar</button>
Varias directivas que usan la misma plantilla
Plantilla con prefijo genérico llamado "lineaFactura"
El prefijo ya no es el nombre de la directiva sino algo "generico" .
component Angular 1.5
app.component("miFactura", {
templateUrl: "mi-factura-tpl.html",
controller: function () {
this...
},
bindings: {
}
});
app.component("miLineaFactura", {
templateUrl: "mi-linea-factura-tpl.html",
controller: function () {
this...
},
bindings: {
factura: "=",
onButton:"&"
}
});
Simplificamos la creación
Es lo "moderno"
Seguimos usando un prefijo inútil
Seguimos accediendo a datos privados
Sigue siendo mas complejo usar el "watch"
@Component({
selector: 'mi-linea-factura',
inputs: ['factura'],
outputs: ['button']
})
@View({
template: `
<h3>Dentro de la directiva LineaFactura</h3>
Empresa:{{factura.empresa}}<br>
Concepto:{{lineaFactura.concepto}}<br>
<button (click)="button(lineaFactura.concepto)">Avisar</button>
`
})
class miLineaFactura {
this...
}
Prácticamente ventajas .......
Aun no disponible
¿TypeScript?
app.directive("miLineaFactura",LineaFacturaDirective);
function LineaFacturaDirective() {
return {
templateUrl :"mi-linea-factura-tpl.html",
controller:['$scope',function(vm) {
vm...
this...
}],
scope:{
factura:"=",
onButton:"&"
}
}
};
¿Y si a "$scope" lo llamo "vm"?
<!-- directiva -->
<mi-factura></mi-factura>
<!-- template de <mi-factura> -->
<h3>Dentro de la directiva Factura</h3>
Empresa:{{factura.empresa}}<br>
<mi-linea-factura
factura='factura'
on-button="avisar(mensaje)"
></mi-linea-factura>
<!-- template de <mi-linea-factura> -->
<h3>Dentro de la directiva LineaFactura</h3>
Empresa:{{factura.empresa}}<br>
Concepto:{{lineaFactura.concepto}}<br>
<button ng-click="onButton({mensaje:lineaFactura.concepto})">Avisar</button>
Ya no necesitamos el prefijo
Eliminamos los prefijos superfluos "miFactura" y miLineaFactura"
Es mas fácil usar "$watch" ,etc.
Si otra directiva usa nuestro controlador , no exponemos las interioridades del $scope sino solo las del "this". Ej: require: "^miFactura"
Mezclamos $scope y this, ¿pero quizás eso sea bueno?
(no hablo de peticiones/s)
400 Rutas
400 Componentes
400 Controladores
100 HTML
Total: 1300 Artefactos
(13 por Entidad)
Ruta ==> /factura/new Componente ==> <factura-new> Controlador ==> FacturaNewController HTML ==> factura-detail.html
Ruta ==> /factura/update/:id Componente ==> <factura-update> Controlador ==> FacturaUpdateController HTML ==> factura-detail.html
Ruta ==> /factura/delete/:id Componente ==> <factura-delete> Controlador ==> FacturaDeleteController HTML ==> factura-detail.html
GenericNewController GenericUpdateController GenericDeleteController GenericViewController
400 Rutas
400 Componentes
4 Controladores
100 HTML
Total: 904 Artefactos
<factura-new>
<factura-update>
<factura-delete>
<factura-view>
<linea-factura-new>
<linea-factura-update>
<linea-factura-delete>
<linea-factura-view>
<albaran-new>
<albaran-update>
<albaran-delete>
<albaran-view>
.......
.......
<linea-factura-view-1>
<!-- Plantilla HTML -->
<h1>{{lineaFactura.concepto}}</h1>
importe:{{lineaFactura.cantidad*lineaFactura.precioUnitario}} €
<linea-factura-view-2>
<!-- Plantilla HTML -->
concepto: {{lineaFactura.concepto}}<br>
cantidad: {{lineaFactura.cantidad}}<br>
precioUnitario:{{lineaFactura.precioUnitario}}<br>
Ej: Calendario , Botón , Arbol , etc.
Ej: factura-new, factura-delete, factura-update
Ej: ng-repeat , ng-hide , etc.
<generic-component controller="LineaFacturaViewController" [factura]="factura" >
<h1>{{lineaFactura.concepto}}</h1>
importe:{{lineaFactura.cantidad*lineaFactura.precioUnitario}} €
</generic-component>
<generic-component controller="LineaFacturaViewController" [factura]="factura" >
concepto: {{lineaFactura.concepto}}<br>
cantidad: {{lineaFactura.cantidad}}<br>
precioUnitario:{{lineaFactura.precioUnitario}}<br>
</generic-component>
app.controller("LineaFacturaViewController",LineaFacturaViewController);
function LineaFacturaViewController($scope) {
}
<generic-component>
400 Rutas*
1 Componentes
4 Controladores
100 HTML
Total: 505 Artefactos
*Es fácil hacer una función crear las 400 automáticamente
Muy sencillo de usar
No necesitamos los prefijos
El $scope está aislado
Se usan parámetros
Reusable, no dependemos del controlador "padre"
Usamos el $scope o "vm" :-)
¿Migración a AngularJS?
<generic-component controller="GenericUpdateController" entity="Factura" >
<h3>Dentro del controlador Factura</h3>
Empresa:{{factura.empresa}}
<generic-component controller="LineaFacturaController" [factura]="factura" (on-button)="avisar(mensaje)" >
<h3>Dentro del controlador LineaFactura</h3>
Empresa:{{factura.empresa}}<br>
Concepto:{{lineaFactura.concepto}}<br>
<button ng-click="onButton({mensaje:lineaFactura.concepto})">Avisar</button>
</generic-component>
</generic-component>
app.directive('genericComponent', genericComponent);
function genericComponent() {
return {
restrict: 'E',
scope: {
},
priority: 500,
transclude: true,
controller: "@",
link: function (scope, iElement, iAttrs, controller, $transcludeFn) {
$transcludeFn(scope, function (clone) {
iElement.append(clone);
});
}
};
}
function LineaFacturaController($scope,$attrs) {
$scope.factura=$attrs.factura;
}
<generic-component
controller="LineaFacturaController"
[factura]="factura"
(on-button)="avisar(mensaje)"
>
<h3>Dentro del controlador LineaFactura</h3>
Empresa:<input ng-model="factura.empresa"> <br>
Concepto:<input ng-model="lineaFactura.concepto"> <br>
<button ng-click="onButton({mensaje:lineaFactura.concepto})">Avisar</button>
</generic-component>
Usando "$attrs" podemos acceder a los parámetros
Antes en las directivas:
Usamos "$parse" o "$interpolate" según el tipo de binding
<generic-component
controller="LineaFacturaUpdateController"
[factura]="factura"
(on-button)="avisar(mensaje)"
>
<h3>Dentro del controlador LineaFactura</h3>
Empresa:<input ng-model="factura.empresa"> <br>
Concepto:<input ng-model="lineaFactura.concepto"> <br>
<button ng-click="onButton({mensaje:lineaFactura.concepto})">Avisar</button>
</generic-component>
<generic-component
controller="LineaFacturaUpdateController"
factura="{{factura}}"
(on-button)="avisar(mensaje)"
>
<h3>Dentro del controlador LineaFactura</h3>
Empresa:<input ng-model="factura.empresa"> <br>
Concepto:<input ng-model="lineaFactura.concepto"> <br>
<button ng-click="onButton({mensaje:lineaFactura.concepto})">Avisar</button>
</generic-component>
Binding "=" de "factura" y usamos $parse
Binding "@" de "factura" y usamos $interpolate
function LineaFacturaUpdateController($scope,$attrs,$parse,$interpolate) {
$scope.factura= $parse($attrs.factura)($scope.$parent);
$scope.factura=$interpolate($attrs.factura)($scope.$parent);
}
app.decorator("$controller", function ($delegate, $interpolate, $parse) {
var originalFn = $delegate;
newFn = function (expression, locals, later, ident) {
if (locals.$element[0].nodeName==="GENERIC-COMPONENT") {
bindAttrsToScope(locals.$scope, locals.$attrs);
expression=locals.$scope.controller;
}
return originalFn(expression, locals, later, ident);
};
return newFn;
);
app.decorator("$controller", function ($delegate, $interpolate, $parse) {
var originalFn = $delegate;
newFn = function (expression, locals, later, ident) {
expression=controllerFactory(expression,locals.$attrs);
return originalFn(expression, locals, later, ident);
};
return newFn;
);
GenericViewController
LineaFacturaViewController
LineaFacturaViewLargeController
$routeProvider.when('/factura/:idFactura', {
templateUrl: "factura.html",
controller: "GenericUpdateController",
resolve:{
controllerParams:function($route) {
return {
entity:"Factura",
id:$route.current.params.idFactura
}
}
}
});
$routeProvider.when('/mifactura', {
templateUrl: "factura.html",
controller:"GenericViewController",
resolve:{
controllerParams:function() {
return {
entity="Factura",
id:10
}
}
}
});
<generic-component controller="{{controller}}" entity="{{entity}}" id="{{id}}" >
<!-- Aqui el HTML --->
</generic-component>
factura.html
https://github.com/logongas/angularvalencia-controladores
Ejemplos en:
By Lorenzo Gonzalez Gascón
Como usar un controlador y/o componente de 7 formas distintas. El problema de "$scope hell"
I'm teacher in computer Science in a high school, the high school is named "CIPFP Mislata" in Spain-Valencia-Mislata .