javascript - example - ui router ejemplos
Cómo hacer rutas automáticas de pan dinámico con AngularJS+Angular UI Router (6)
Un componente clave para las aplicaciones web es el pan rallado / navegación. Con Angular UI Router, tendría sentido poner los metadatos breadcrumb con los estados individuales, en lugar de en sus controladores. Crear manualmente el objeto breadcrumbs para cada controlador donde se necesita es una tarea sencilla, pero también muy complicada.
He visto algunas soluciones para Pan rallado automático con Angular, pero para ser honesto, son bastante primitivas. Algunos estados, como los cuadros de diálogo o los paneles laterales, no deberían actualizar las rutas de exploración, pero con los complementos actuales de angular, no hay forma de expresar eso.
Otro problema es que los títulos de las migas de pan no son estáticos. Por ejemplo, si va a una página de detalles del usuario, el título de la ruta de navegación debería ser probablemente el nombre completo del usuario y no un "detalle de usuario" genérico.
El último problema que debe resolverse es utilizar todos los valores de parámetros de estado correctos para enlaces principales. Por ejemplo, si está mirando una página de detalles de usuario de una empresa, obviamente querrá saber que el estado padre requiere un :companyId
.
¿Hay algún complemento para angular que brinde este nivel de soporte para migas de pan? Si no, ¿cuál es la mejor manera de hacerlo? No quiero saturar mis controladores, tendré muchos de ellos, y quiero hacerlo de la manera más automatizada e indolora posible.
¡Gracias!
Después de profundizar en las partes internas del router, entendí cómo podía crear una ruta de exploración usando recursos resueltos.
Aquí hay un plunker para mi directiva.
NOTA: No pude conseguir que este código funcione correctamente dentro del plunker, pero la directiva funciona en mi proyecto. routes.js se proporciona simplemente por ejemplo de cómo puede establecer títulos para sus migas de pan.
Gracias por la solución proporcionada por @egervari. Para aquellos que necesitan agregar algunas propiedades $ stateParams a los datos personalizados de las migas de pan. He ampliado la sintaxis {: id} para el valor de la clave ''título''.
.state(''courses.detail'', {
url: ''/:courseId'',
templateUrl: ''app/courses/courses.detail.html'',
controller: ''CourseDetailController'',
resolve: {
course: function(Model, $stateParams) {
return Model.getOne(''/courses'', $stateParams.courseId);
}
},
breadcrumb: {
title: ''course {:courseId}''
}
})
Aquí hay un ejemplo de Plunker . FYI.
Hice un módulo angular que genera un rastro de pan basado en los estados de ui-enrutador. Todas las características de las que hablas están incluidas (recientemente agregué la posibilidad de ignorar un estado en la ruta de navegación mientras leía esta publicación :-)):
Aquí está el repo github
Permite etiquetas dinámicas interpoladas contra el alcance del controlador (el "más profundo" en el caso de vistas anidadas / múltiples).
La cadena de estados se puede personalizar por opciones de estado (Ver referencia de API )
El módulo viene con plantillas predefinidas y permite plantillas definidas por el usuario.
Me gustaría compartir mi solución a esto. Tiene la ventaja de no requerir que se inyecte nada en los controladores, y admite etiquetas de ruta de navegación con nombre, así como el uso de las funciones de resolve:
para nombrar sus migas de pan.
Ejemplo de configuración de estado:
$stateProvider
.state(''home'', {
url: ''/'',
...
data: {
displayName: ''Home''
}
})
.state(''home.usersList'', {
url: ''users/'',
...
data: {
displayName: ''Users''
}
})
.state(''home.userList.detail'', {
url: '':id'',
...
data: {
displayName: ''{{ user.name | uppercase }}''
}
resolve: {
user : function($stateParams, userService) {
return userService.getUser($stateParams.id);
}
}
})
Luego debe especificar la ubicación de la etiqueta de ruta de navegación (nombre de visualización) en un atributo de la directiva:
<ui-breadcrumbs displayname-property="data.displayName"></ui-breadcrumbs>
De esta manera, la directiva sabrá mirar el valor de $state.$current.data.displayName
para encontrar el texto a usar.
$ nombres de ruta de navegación interpolables
Observe que en el último estado ( home.userList.detail
), displayName utiliza la sintaxis de interpolación angular habitual {{ value }}
. Esto le permite hacer referencia a cualquier valor definido en el objeto de resolve
en la configuración de estado. Normalmente, esto se usaría para obtener datos del servidor, como en el ejemplo anterior del nombre de usuario. Tenga en cuenta que, dado que esto es solo una cadena angular regular, puede incluir cualquier tipo de expresión angular válida en el campo displayName, como en el ejemplo anterior donde le estamos aplicando un filtro.
Manifestación
Aquí hay una demostración en funcionamiento en Plunker: http://plnkr.co/edit/bBgdxgB91Z6323HLWCzF?p=preview
Código
Pensé que era demasiado poner todo el código aquí, así que aquí está en GitHub: https://github.com/michaelbromley/angularUtils/tree/master/src/directives/uiBreadcrumbs
No creo que haya una funcionalidad integrada, pero todas las herramientas están ahí para usted, eche un vistazo al LocationProvider . Simplemente podría hacer que los elementos de navegación usen esto y cualquier otra cosa que quiera saber simplemente inyéctelo.
Resolví esto por mí mismo hace un tiempo, porque no había nada disponible. Decidí no usar el objeto de data
, porque en realidad no queremos que nuestros hijos hereden nuestros títulos de ruta de navegación. A veces hay cuadros de diálogo modales y paneles de la derecha que se deslizan y que técnicamente son "vistas secundarias", pero no deberían afectar la ruta de navegación. Al usar un objeto breadcrumb
lugar, podemos evitar la herencia automática.
Para la propiedad del title
real, estoy usando $interpolate
. Podemos combinar nuestros datos de ruta de navegación con el alcance de resolución sin tener que hacer resuelve en un lugar diferente. En todos los casos que tuve, solo quería usar el alcance de resolver de todos modos, así que esto funciona muy bien.
Mi solución también maneja i18n también.
$stateProvider
.state(''courses'', {
url: ''/courses'',
template: Templates.viewsContainer(),
controller: function(Translation) {
Translation.load(''courses'');
},
breadcrumb: {
title: ''COURSES.TITLE''
}
})
.state(''courses.list'', {
url: "/list",
templateUrl: ''app/courses/courses.list.html'',
resolve: {
coursesData: function(Model) {
return Model.getAll(''/courses'');
}
},
controller: ''CoursesController''
})
// this child is just a slide-out view to add/edit the selected course.
// It should not add to the breadcrumb - it''s technically the same screen.
.state(''courses.list.edit'', {
url: "/:courseId/edit",
templateUrl: ''app/courses/courses.list.edit.html'',
resolve: {
course: function(Model, $stateParams) {
return Model.getOne("/courses", $stateParams.courseId);
}
},
controller: ''CourseFormController''
})
// this is a brand new screen, so it should change the breadcrumb
.state(''courses.detail'', {
url: ''/:courseId'',
templateUrl: ''app/courses/courses.detail.html'',
controller: ''CourseDetailController'',
resolve: {
course: function(Model, $stateParams) {
return Model.getOne(''/courses'', $stateParams.courseId);
}
},
breadcrumb: {
title: ''{{course.name}}''
}
})
// lots more screens.
No quería atar las migas de pan a una directiva, porque pensé que podría haber múltiples maneras de mostrar la ruta de navegación visualmente en mi aplicación. Entonces, lo puse en un servicio:
.factory("Breadcrumbs", function($state, $translate, $interpolate) {
var list = [], title;
function getProperty(object, path) {
function index(obj, i) {
return obj[i];
}
return path.split(''.'').reduce(index, object);
}
function addBreadcrumb(title, state) {
list.push({
title: title,
state: state
});
}
function generateBreadcrumbs(state) {
if(angular.isDefined(state.parent)) {
generateBreadcrumbs(state.parent);
}
if(angular.isDefined(state.breadcrumb)) {
if(angular.isDefined(state.breadcrumb.title)) {
addBreadcrumb($interpolate(state.breadcrumb.title)(state.locals.globals), state.name);
}
}
}
function appendTitle(translation, index) {
var title = translation;
if(index < list.length - 1) {
title += '' > '';
}
return title;
}
function generateTitle() {
title = '''';
angular.forEach(list, function(breadcrumb, index) {
$translate(breadcrumb.title).then(
function(translation) {
title += appendTitle(translation, index);
}, function(translation) {
title += appendTitle(translation, index);
}
);
});
}
return {
generate: function() {
list = [];
generateBreadcrumbs($state.$current);
generateTitle();
},
title: function() {
return title;
},
list: function() {
return list;
}
};
})
La directiva actual breadcrumb se vuelve muy simple:
.directive("breadcrumbs", function() {
return {
restrict: ''E'',
replace: true,
priority: 100,
templateUrl: ''common/directives/breadcrumbs/breadcrumbs.html''
};
});
Y la plantilla:
<h2 translate-cloak>
<ul class="breadcrumbs">
<li ng-repeat="breadcrumb in Breadcrumbs.list()">
<a ng-if="breadcrumb.state && !$last" ui-sref="{{breadcrumb.state}}">{{breadcrumb.title | translate}}</a>
<span class="active" ng-show="$last">{{breadcrumb.title | translate}}</span>
<span ng-hide="$last" class="divider"></span>
</li>
</ul>
</h2>
De la captura de pantalla aquí, puedes ver que funciona perfectamente tanto en la navegación:
Además de la etiqueta html <title>
:
Equipo PS a Angular UI: ¡por favor agregue algo como esto de la caja!