I am using angular UI-Router and bootstrap collapse panels to build a sidebar for a style guide. My sidebar as is "works" but I'm getting a
Error: Cannot transition to abstract state 'parent'
when clicking the child states. In my real solution there are many parents with child groupings and the parents truly are abstract (i.e. they don't represent a physical page or state). I know I can't link directly to the parent states, and I don't believe I am, I just need to set their ui-sref in the parent panel so that I can get the parent to stay open by setting the ui-sref-active attribute.
I have an example running on plunker: http://plnkr.co/edit/bnvGcaOvzW4que8g3vh7?p=preview
code for reference:
angular.module('app', ['ui.router'])
.config(function($stateProvider, $urlRouterProvider){
$urlRouterProvider.otherwise("/");
$stateProvider
.state('home', {
url: "/",
templateUrl: "layout.html"
})
.state('parent', {
abstract: true,
url: '/parent',
templateUrl: "layout.html"
})
.state('parent.child', {
url: "/child",
templateUrl: "child.html"
})
});
layout.html
<div class="container-fluid">
<div class="row">
<div class="col-xs-3">
<a ui-sref="home">Home</a>
<div class="panel-group" id="sidebar">
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">
<a data-toggle="collapse" data-parent="#sidebar" data-target="#parent"
class="collapsed">Parent</a>
</h4>
</div>
<div id="parent" ui-sref="parent" class="panel-collapse collapse" ui-sref-active="in">
<div class="list-group">
<a ui-sref="parent.child" ui-sref-active="active" class="list-group-item">Child</a>
</div>
</div>
</div>
</div>
</div>
<div class="col-xs-9">
<div ui-view></div>
</div>
</div>
</div>
Short Answer:
Just remove ui-sref="parent" from your code. This particular ui-sref attribute is not required in order for ui-sref-active to work as you expect.
Long Answer:
ui-sref-active works by finding all child elements with a ui-sref and adding these elements to a states array attached to the element. On $stateChangeSuccess, each element loops through this array and if a state is found that matches the array, then the class is applied.
As an example:
<ul>
<li ui-sref-active="open">
<a ui-sref="app.home">Link</a>
<ul>
<li>
<a ui-sref="app.home.this"></a>
</li>
<li>
<a ui-sref="app.home.that"></a>
</li>
<li>
<a ui-sref="app.home.whatever"></a>
</li>
<li>
<a ui-sref="temp"></a>
</li>
</ul>
</li>
</ul>
In the above example, assume app is an abstract. All 4 of those links will trigger the open class on the parent element, as they are contained within. It doesn't matter that they are not all part of the app.home structure.
Related
I'm using AngularJS for a store app(using Laravel for back-end as API). But now I want to implement a blog on the same app. For example I'm in the store(this has one layout), I click on the Blog link in the menu and it redirects me to another page that has a totally different layout. Is this possible?
Here is my index.html:
<div class="row">
<!-- Menu -->
<div class="col-sm-2">
<div ng-controller="NavbarCtrl" class="navbar navbar-default navbar-static-top">
<div class="navbar-header">
<a class="navbar-brand" href="/#/"><i class="ion-ios7-pulse-strong"></i> Ugurt</a>
</div>
<div class="collapse navbar-collapse navbar-ex1-collapse">
<ul class="nav navbar-nav">
<li ng-if="isAuthenticated()">Post</li>
<li ng-if="isAuthenticated()">Blog</li>
</ul>
<ul ng-if="!isAuthenticated()" class="nav navbar-nav pull-right">
<li>Login</li>
<li>Sign up</li>
</ul>
<ul ng-if="isAuthenticated()" class="nav navbar-nav pull-right">
<li>Logout</li>
</ul>
</div>
</div>
</div>
<!-- Different Partial Files -->
<div class="col-sm-10">
<div ui-view></div>
</div>
</div>
Here is my stateProvider config(a portion):
angular.module('Ugurt', ['ngResource', 'ngMessages', 'ngAnimate', 'toastr', 'ui.router', 'satellizer', 'checklist-model', 'ngCart', 'textAngular'])
.config(function($stateProvider, $urlRouterProvider, $authProvider) {
$stateProvider
.state('home', {
url: '/',
controller: 'HomeCtrl',
templateUrl: 'partials/home.html'
})
.state('store', {
url: '/store',
controller: 'StoreCtrl',
templateUrl: 'partials/store.html'
})
Every link I click on shows a different partial file because of the ui-view attribute. But when I click on the Blog link, I want it to lead to a different file that a completely different layout and menu. I want to put the both in the same app because the register and login is going to be the same for both parts. And when one user is logged in in the store, if he goes to the Blog part, I still want him to be logged in without doing anything.
Any ideas?
If you have ui-router like structure, you have route names, so you just need to create new route with different layout and redirect to new route.
Well there are already a way to do that, using a proper router like ui-router.
For example:
You have two main states, store and blog. What you can do is configure those two states in te $stateProvider under app.config.
For example:
myApp.config(function ($stateProvider){
$stateProvider.state('store', {
url: '/store',
templateUrl: 'store-layout.html'
});
// and then, when it requires to have substate of store you must inherit this state from store state
// also you'll have to specify a new ui-view inside store-layout.html to be the outlet of the nested states
$stateProvider.state('store.categories', {
url: '/store/categories',
templateUrl: 'store-categories.html'
});
$stateProvider.state('blog', {
url: '/blog',
templateUrl: 'blog-layout.html'
});
$stateProvider.otherwise('/store');
});
The Problem: Im currently building a process simulation. I ve 2 bootstrap taps, one contains an editor(which works fine), the otherone is used to display the content during simulation. JsPlumb is used in order to display different simulation objects. The Problem is that the connections between these objects arent correctly displayed if the tap is switched from the editor to the simulation. They "snap" into the right position after an element is dragged, but I want them to render correctly on the tap switch.
Here is a fiddle: Problem fiddle
Code:
html:
<div class="container-fluid">
<ul class="nav nav-pills">
<li class="active">
<a href="#editor" data-target="#editor" data-toggle="tab">
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>Editor
</a>
</li>
<li>
<a href="#display" data-target="#display" data-toggle="tab" id="testlink">
<span class="glyphicon glyphicon-fast-forward" aria-hidden="true"></span> Simulation
</a>
</li>
</ul>
</div>
<div class="tab-content">
<div class="tab-pane fade in active" id="editor"> </div>
<div class="tab-pane fade" id="display">
<div class="container center-block content">
<div id="test1" class="window">
a
</div>
<div id="test2" class="window">
b
</div>
</div>
</div>
Javascript:
jsPlumb.ready(function() {
jsPlumb.Defaults.Container = $('#display');
jsPlumb.connect({
source: $('#test1'),
target: $('#test2'),
anchor: "Continuous",
endpoint: "Blank",
detachable: false
});
jsPlumb.draggable($(".window"));
});
What I know: The problem is directly related to the fact that the tab content is not displayed on load, since placing the test divs into the editor tap (which is the active one on load) resolves the problem. fiddle: placing divs in aktive tab
Not an optimal solution but this works:
$('a[data-toggle="tab"]').on('shown.bs.tab', function (event) {
if (event.target.id == 'display-tab') {
jsPlumb.detachEveryConnection();
redrawEveryConnection();
}
});
This code removes every connection on a tab switch to the simulation and calls the redraw function, which has to be implemented depending on the situation (the start and endpoints ve to be saved somewehere e.g. server).
I am creating an Bootstrap/Angular app and using $routeProvider to render the views, and want to have tabs within one of those views. However, the active tab Bootstrap functionality isn't toggling. Here's what my index.html, app.routes.js, and main.html look like:
Index.html
<!doctype html>
<head>...</head>
<body ng-app="app">
<nav>...</nav>
<div ng-view=""></div>
...
<script src="Scripts/jquery-1.9.1.min.js"></script>
<script src="Scripts/angular.min.js"></script>
<script src="Scripts/angular-route.min.js"></script>
<script src="Scripts/bootstrap.min.js"></script>
<script src="assets/js/custom.js"></script>
</body>
app.routes.js
var app = angular.module('app');
app.config(function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'components/main/main.html',
controller: 'MainCtrl',
controllerAs: 'main'
})
...
main.html
<ul class="nav nav-tabs">
<li class="active"><a data-toggle="tab" href="#tab1">Tab1</a></li>
<li><a data-toggle="tab" href="#tab2">Tab2</a></li>
<li><a data-toggle="tab" href="#tab3">Tab3</a></li>
<li><a data-toggle="tab" href="#tab4">Tab4</a></li>
</ul>
<div class="container">
<div class="tab-content">
<div id="tab1" class="tab-pane fade in active">
...
</div>
<div id="tab2" class="tab-pane fade">
...
</div>
<div id="tab3" class="tab-pane fade">
...
</div>
<div id="tab4" class="tab-pane fade">
...
</div>
</div>
</div>
How can I run my Javascript within the main.html route and more specifically get the Bootstrap active tab script working?
The problem is that AngularJS doesn't play well with Bootstrap's default JS. Upon another developer's recommendation, I replaced Bootstrap's JS with ui-bootstrap. Following the documentation there on tabs, I got my app working! Here's a good resource on the issue.
If using bootstrap try angular-bootstrap it wraps bootstraps components in angular directives to create a tab component you'll be using their tab component directive here is a plunker:
http://embed.plnkr.co/TMN3KNlS4Dv90SvbTQKJ/
documentation http://angular-ui.github.io/bootstrap/#/tabs
Good luck
I have some href's in my navbar which point to different subsections on a page.
The problem is that if a user clicks the href, the browser points to a wrong position on the page (presumably the location before the ng-repeat has been rendered.)
Here is part of my navbar:
<ul class="nav navbar-nav">
<!--Classes-->
<li class="dropdown">
Classes
<ul class="dropdown-menu" role="menu">
<li>
Class Types
</li>
<li>
Timetables
</li>
<li>
Class Descriptions
</li>
<li>
Instructors
</li>
<li>
Testimonials
</li>
</ul>
</li>
</ul>
And my Classes page:
<h2 class="mai-header-red" id="faq">FAQ</h2>
<h4 class="mai-header-white mai-contact-desc">Some description stuff here
</h4>
<div class="container ng-cloak" ng-controller="faqController">
<div class="panel-group" id="accordion">
<div class="panel panel-default" ng-repeat="faq in questions">
<div class="panel-heading">
<div data-toggle="collapse" data-parent="#accordion" href="#collapse{{$index}}">
<h4 class="panel-title">
{{$index + 1}}. {{faq.question}}
</h4>
</div>
</div>
<div id="collapse{{$index}}" class="panel-collapse collapse">
<div class="panel-body">
<p>{{faq.answer}}</p>
</div>
</div>
</div>
</div>
</div>
<h2 class="mai-header-red" id="testimonials">Testimonials</h2>
Any help would be appreciated.
I don't think you are having issue with ng-repeat but with angular routing system.
Angular routing break default anchor since it use hash to display your path.
Class Types
There is two times hash in your link and I assume your browser just consider the all string as an anchor (or angular route just catch it and prevent anchor effect).
To reproduce the anchor effect, you need to use $location.hash and $anchorScroll
In your app define :
app.run(function($rootScope, $location, $anchorScroll) {
//when the route is changed scroll to the proper element.
$rootScope.$on('$routeChangeSuccess', function(newRoute, oldRoute) {
if($location.hash()) $anchorScroll();
});
});
Then you can use links like
Test/Foo
I have a single accordion item that I am using for a read more / less link on a page.
The purpose of this is to click it to read more, and a few paragraphs will follow.
I have this working, but I need it to stay closed when the page loads, at the moment the page loads with the item open.
What can I add to fix this?
<div class="accordionMod panel-group">
<div class="accordion-item">
<h4 class="accordion-toggle">Read More / Less</h4>
<section class="accordion-inner panel-body">
<p>more info.</p>
<h4 class="title">More titles</h4>
<p>more content</p>
</section>
</div>
</div>
I think this will work
<div class="accordion" id="myAccordion">
<div class="accordion-group">
<div class="accordion-heading">
<a class="accordion-toggle" data-toggle="collapse" data-parent="#myAccordion" href="#collapseOne">
Title
</a>
</div>
<div id="collapseOne" class="accordion-body collapse">
<div class="accordion-inner">
Content
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-heading">
<a class="accordion-toggle" data-toggle="collapse" data-parent="#myAccordion" href="#collapseTwo">
Title
</a>
</div>
<div id="collapseTwo" class="accordion-body collapse">
<div class="accordion-inner">
Content
</div>
</div>
</div>
</div>
if you add the class in to accordion-body collapse it will be open by default.
Sounds like you don't need an accordion, which has multiple panels, only one of which can open at a time. Instead, just use collapse which allows you to toggle the visibility of a section of the page.
Panel Visibility
From the collapse docs:
The collapse plugin utilizes a few classes to handle the heavy lifting:
.collapse hides the content
.collapse.in shows the content
.collapsing is added when the transition starts, and removed when it finishes
Thus, to start off collapsed, just make sure your extra content has the class collapse and not in
Simple HTML
To use collapse, you really just need to specify a data-toggle="collapse" and then point the collapse to the css selector of the section you would like to toggle using data-target.
Here's a bare bones example that just exposes the collapsing functionality:
<a data-toggle="collapse" data-target="#ReadMoreInfo" href="#">
Read More / Less
</a>
<div id="ReadMoreInfo" class="collapse">
<p> More Info Here </p>
</div>
HTML with Bootstrap classes
All the other bootstrap classes are just to help make the collapsible panel look like a collapsible panel. I would recommend formatting them like this or doing a lot of custom CSS work. Even if you wanted to do the CSS yourself, starting off with this template and then overriding the styles would be best.
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">
<a data-toggle="collapse"
data-target="#ReadMoreInfo"
href="#ReadMoreInfo">
Read More / Less
</a>
</h4>
</div>
<div id="ReadMoreInfo" class="panel-collapse collapse">
<div class="panel-body">
<p>more info.</p>
<h4 class="title">More titles</h4>
<p>more content</p>
</div>
</div>
</div>
Working Demo in jsFiddle
Screenshot:
Removing the in class did not work for me in my case(though normally it should). I was just looking for the answer to this question yesterday. I used this to make the accordion default to close:
$( document ).ready(function() {
$('.collapse').collapse({
toggle: false
});
});
See also this question on:
Jquery accordion not collapse by default
if accordion is with icon, add collapsed class name to :
<a class="accordion-toggle accordion-toggle-styled collapsed" >
and add collapse class name to:
<div id="collapse0" class="panel-collaps collapse">
complete code is:
<div class="panel-group accordion" id="accordion0">
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">
<a class="accordion-toggle accordion-toggle-styled collapsed" data-toggle="collapse" data-parent="#accordion0" href="#collapse0">
Collapsible Header Text
</a>
</h4>
</div>
<div id="collapse_3_1" class="panel-collaps collapse">
<div class="panel-body">
<p> body text
</p>
</div>
</div>
</div>
</div>
It's easy. In Bootstrap 5, edit your HTML to remove the "show" class from your first accordion item. See Make accordion closed by default in Bootstrap, the answer that starts "Was recently working with Bootstrap".