<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css"/>
<script src="https://code.angularjs.org/1.3.0/angular.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.18/angular-ui-router.min.js"></script>
<script src="lib/app.js"></script>
<script src="observerLib/router-observer.js"></script>
</head>
<body ng-app="miApp">
<div ng-controller="appController">
<header class="well">
<h1>Mi Sitio AngularJS: {{title}}</h1>
<div class="btn-group">
<a class="btn btn-primary" ui-sref="inicio">Inicio</a>
<a class="btn btn-primary" ui-sref="productos">Productos</a>
<a class="btn btn-primary" ui-sref="contacto">Contacto</a>
</div>
</header>
<div class="container">
<div ui-view></div>
</div>
<hr>
<footer>
<p>© 2025 - Layout Persistente</p>
</footer>
</div>
</body>
</html>
window.layout = angular.module("layout", ["ui.router"]);
window.layout.config([
"$stateProvider",
"$urlRouterProvider",
"$provide",
function($stateProvider, $urlRouterProvider, $provide) {
$provide.factory("$stateProvider", function () {
return $stateProvider;
});
$provide.factory("$urlRouterProvider", function () {
return $urlRouterProvider;
});
}]);
window.layout.service('RouteMonitorService', ['$rootScope', function($rootScope) {
var arrancado = false;
this.iniciar = function(callback) {
if (arrancado) return;
RouteObserverLib.getInstance().init(
new AngularJsFactory($rootScope),
callback
);
arrancado = true;
};
}]);
window.layout.run([
"$stateProvider",
"$urlRouterProvider",
"$rootScope",
"RouteMonitorService",
function($stateProvider, $urlRouterProvider, $rootScope, RouteMonitorService) {
$urlRouterProvider.otherwise("/inicio");
$stateProvider
.state('inicio', {
url: "/inicio",
templateUrl: "lib/inicio.html"
})
.state('productos', {
url: "/productos",
templateUrl: "lib/productos.html"
})
.state('contacto', {
url: "/contacto",
templateUrl: "lib/contacto.html"
});
RouteMonitorService.iniciar(function(data) {
console.log(`*** Servicio Monitor: Ruta -> ${data.name}`);
$rootScope.rutaActual = data.name;
})
}]);
var app = angular.module('miApp', ['layout']);
app.controller('appController', ['$scope', function($scope) {
$scope.title = "Routing Dinámico";
}]);
<div>
<h2>Contáctanos</h2>
<p>Escríbenos a: <strong>AlgunEmail@hotmail.com</strong></p>
<button ng-click="alert('Enviado!')">Enviar Mensaje</button>
</div>
<div>
<h2>{{titulo}}</h2>
<p>Esta es la página principal. El header de arriba no ha cambiado.</p>
</div>
<div>
<h2>Catálogo de Productos</h2>
<ul>
<li ng-repeat="item in lista">{{item}}</li>
</ul>
</div>
[ng\:cloak],
[ng-cloak],
[data-ng-cloak],
[x-ng-cloak],
.ng-cloak,
.x-ng-cloak {
display: none !important;
}
h1,
p {
font-family: sans-serif;
}
/* Estilos del Layout Fijo */
.header-fijo {
background-color: #2c3e50;
color: white;
padding: 20px;
text-align: center;
}
.header-fijo a {
color: #ecf0f1;
text-decoration: none;
font-size: 18px;
margin: 0 10px;
}
.footer-fijo {
background-color: #95a5a6;
padding: 10px;
text-align: center;
position: fixed;
bottom: 0;
width: 100%;
}
/* Estilos del Contenido Dinámico (ng-view) */
.contenido-dinamico {
padding: 40px;
background-color: #ecf0f1;
min-height: 300px;
border: 2px dashed #bdc3c7; /* Borde para que veas dónde se inyecta la vista */
margin: 20px;
}
class RouteObserverLib {
constructor() {
this.listener = null;
}
static getInstance() {
if (!RouteObserverLib.instance) {
RouteObserverLib.instance = new RouteObserverLib();
}
return RouteObserverLib.instance;
}
init(factory, callback) {
if (this.listener) {
this.listener.stopListening();
}
this.listener = factory.createListener();
this.listener.startListening(callback);
console.log('%c RouteObserverLib: Inicializado correctamente.', 'color: green; font-weight: bold;');
}
}
// Implementación concreta del Listener para AngularJS
class AngularJSListener {
constructor($rootScope) {
this.$rootScope = $rootScope;
this.unsubscribe = null;
}
startListening(callback) {
// Nos suscribimos al evento nativo de UI-Router
this.unsubscribe = this.$rootScope.$on(
'$stateChangeSuccess',
(event, toState, toParams) => {
const routeData = {
name: toState.name,
params: toParams,
path: toState.url
};
callback(routeData);
}
);
}
stopListening() {
if (this.unsubscribe) {
this.unsubscribe();
console.log('Listener detenido');
}
}
}
class AngularJsFactory {
constructor($rootScope) {
this.$rootScope = $rootScope;
}
createListener() {
return new AngularJSListener(this.$rootScope);
}
}