Reputation: 21356
On my main page I had this with Angular 1 Material with ui-router:
<body layout="column">
<md-button class="md-fab md-raised md-primary md-fab-bottom-right" aria-label="Add Test">
<md-icon>add</md-icon>
</md-button>
<md-content>
<ol>
<li>foobar</li>
<!--repeat a bunch of times-->
</ol>
</md-content>
</body>
And that gave me a floating FAB at the bottom right for adding a foobar. When I would scroll the list up, the FAB would stay put. Just what I want.
But if I place that exact content inside a component, it screws up the scrolling!
<body layout="column">
<my-component></my-component>
</body>
If the component has exactly the same content, simply moved to the component template, the fab shows up in basically the correct place… but when I scroll the list up, the FAB scrolls up with it!
If I muck around and add various layers of <md-content>
, I can get the FAB to show up at the bottom of the list, which if larger than the viewport means that the FAB appears below the screen (even worse) --- and still scrolls up with the list.
In short, something about the presence of creating an Angular component completely screws up the FAB placement, and locks it to the list so that it scrolls with the list.
Help! This is very big. I can't break all my FABs out of their components, and put them on the main page separately, with some odd show/hide directives based upon the current state/windows/whatever. How do I work around this?
Upvotes: 1
Views: 310
Reputation: 21356
So Angular 1.5 layout with a FAB is one of the most tedious, brittle things I have seen in a long time.
I wanted a fixed toolbar and tab set, with a scrolling list underneath it. The list should be in a component, and it should maintain its own FAB. The list component should be placed by ui-router. Oh, the pain.
Here is the solution --- the only combination of column/row/md-content/div what-have-you that will work. Changing any one of these throws the whole thing off.
Here's the main page:
<body layout="column">
<md-toolbar>
<div class="md-toolbar-tools">
<h1>Foo</h1><span flex></span><small>bar</small>
</div>
</md-toolbar>
<md-tabs md-border-bottom>
<md-tab label="FooBar" ui-sref="foobar"></md-tab>
</md-tabs>
<div ui-view flex layout="column"></div>
</body>
Here's the ui-router state:
$stateProvider.state({
name: "foobar",
url: "/foobar",
template: "<foobar-widget layout='row'/>"
});
And here's FoobarWidget-template.html
:
<md-content flex layout="column">
<md-button ngclass="md-fab md-raised md-primary md-fab-bottom-right" aria-label="Add Foobar">
<md-icon>add</md-icon>
</md-button>
<md-content>
<md-list>
<md-list-item ng-repeat="foobar in $ctrl.getFoobars()" ng-click="null">
<!-- list-item content -->
</md-list-item>
</md-list>
</md-content>
</md-content>
Note the order of the series of layout="column"
and layout="column"
. It gets especially tricky when you add the extra layer of <ui-view>
, and then put a <foobar-widget>
component inside that, but still you must have a <md-content>
wrapped around the <ui-view>
. Each of those layers has to be accounted for or it will throw off the whole thing. Note that I improved from <md-content layout="column"><ui-view layout="column"></ui-view></md-content>
to <div ui-view flex layout="column"></div>
based on further suggestions from kuhnroyal.
In some cases the problem wasn't that the FAB wasn't being associated with the bottom, right-hand of the block; it's that the parent thing was allowing the block to expand vertically so that the scrolling that appeared was probably from its parent, containing the expanded block, which contained the content with the FAB. Or something like that. I hope this keeps working. I don't want to diddle with this anymore.
kuhnroyal gave me hints of the solution in his answer, and his link to a ui-router issue clued me in to the possibility of just using the component in a ui-router state template to see if I could massage the layout into something that worked. So I'll happily give kuhnroyal the bounty even though his answer wasn't exactly plug-and-play for my situation. Thanks for responding!
Upvotes: 1
Reputation: 7553
The md-fab-bottom-right
CSS-class makes the FAB position: absolute
and as such it gets positioned inside the next DOM element up the chain with position: relative
. In your case that's the <body>
. It appears you want the FAB to always stay on the bottom of your md-list
viewport.
Therefor you have to put the FAB and md-list
inside another md-content
. md-content
provides a relative
wrapper.
Important is also to add flex layout="column"
on the outer md-content
.
<!-- in component template -->
<div layout="row">
<md-content flex layout="column">
<md-button class="md-fab md-raised md-primary md-fab-bottom-right" aria-label="Add FooBar">
<md-icon>add</md-icon>
</md-button>
<md-content>
<md-list>
<md-list-item class="md-2-line" ng-repeat="foobar in $ctrl.getFooBars()" ng-click="null">
<div class="md-list-item-text">
<h3>{{foobar.id}}</h3>
<p>foobar</p>
</div>
</md-list-item>
</md-list>
</md-content>
</md-content>
<md-sidenav md-component-id="add-foobar" class="md-sidenav-right">
<create-foobar-component></create-foobar-component>
</md-sidenav>
</div>
I made a codepen that shows this: http://codepen.io/kuhnroyal/pen/mRyqVW
If that doesn't help, please add a codepen or something that can reproduce your problem.
Update 1: With the use of components and ui-router you might also be running into this issue I created a couple month ago: https://github.com/angular-ui/ui-router/issues/2838
Update 2:
Your angular component is a custom HTML tag by default which behaves like a div
(because it has display: block
by default). For angular-material to work you need to keep a DOM structure that has flex
and layout="column|row"
top to bottom. So if you are using ui-router templates see Update 1, otherwise add it to your angular component template.
Upvotes: 1