Change index of AngularJS md-tabs have no effect at all - javascript

In my Angular app, I have a md-tabs whose md-selected directive is binded to a property in my controller. I'd like to change the current tab to the one whose index is set by a function called by ng-click somewhere else in my template.
I did it this way:
<div ng-controller="TrackingCtrl" layout-fill>
<md-content ng-if="isSmart" layout-fill>
<md-tabs md-selected="selectedIndex" layout-fill>
<md-tab>.........</md-tab>
<md-tab>.........</md-tab>
<md-tab>.........</md-tab>
<md-tab>
<md-tab-label>{{ 'tracking.positions.TITLE' | translate }}</md-tab-label>
<md-tab-body>
<md-tab-content layout-fill flex>
<button ng-click="map.panTo(getPosition());displayMap();"></button>
</md-tab-body>
</md-tab>
</md-tabs>
</md-content>
</div>
In my controller I have :
$scope.selectedIndex = 0;
$scope.displayMap = function() {
$scope.selectedIndex = 1;
};
But it has no effect at all when I click my button which calls displayMap();
I've inspected the problem:
When I set $scope.selectedIndex = 1; in my controller, the default tab is the one whose index is 1. OK
When I set md-selected="1" in my template, the default tab is the one whose index is 1. OK
When I set a breakpoint in my code, and when I click my button, displayMap() is called, and $scope.selectedIndex = 1; is executed. OK
It seems everything works fine... except the tab doesn't change.
I'm running Angular Material 1.0.2
I even used $apply to force update (no effect) :
$scope.selectedIndex = 0;
$scope.displayMap = function () {
$timeout(function () {
if (!$scope.$$phase) {
$scope.$apply(function () {
$scope.selectedIndex = 1;
});
}
});
};

I'm glad you've found a workaround for your issue. To avoid that behaviour initially, you should probably have a look at this stackoverflow discussion.
Since your selectedIndex variable holds a primitive, every new Scope introduced - you already mentioned the ngIf - destroys the data binding and changes within the child scope will not have effect on the 'outside'.
In your case, just use...
$scope.vm = {
selectedIndex: 0
};
...to follow the dot rule.

I solved my problem which was certainly caused by a scope issue. I simply used the controller as syntax, and declared every previous scope data with:
var self = this;
self.selectedIndex = 0;
self.displayMap = function (){
self.selectedIndex = 1;
};
and my markup:
<div ng-controller="TrackingCtrl as tracking" layout-fill>
<md-content ng-if="tracking.isSmart" layout-fill>
<md-tabs md-selected="tracking.selectedIndex" layout-fill>
<md-tab>.........</md-tab>
<md-tab>.........</md-tab>
<md-tab>.........</md-tab>
<md-tab>
<md-tab-label>{{ 'tracking.positions.TITLE' | translate }}</md-tab-label>
<md-tab-body>
<md-tab-content layout-fill flex>
<button ng-click="tracking.displayMap();"></button>
</md-tab-content>
</md-tab-body>
</md-tab>
</md-tabs>
</md-content>
</div>
Works perfect now. I guess my ng-if was modifying my scope or so.

Maybe I've misunderstood something about your question but this should work...
I've created a plunker and I cannot reproduce your behaviour, it's just working fine.
View:
<md-tabs class="md-accent" md-selected="selectedIndex">
<md-tab id="tab1">
<md-tab-label>Item One</md-tab-label>
<md-tab-body>
data.selectedIndex = 0;
</md-tab-body>
</md-tab>
<md-tab id="tab3">
<md-tab-label>Item Two</md-tab-label>
<md-tab-body>
data.selectedIndex = 1;
</md-tab-body>
</md-tab>
</md-tabs>
<md-button ng-click="displayMap()">Map</md-button>
Controller:
function AppCtrl ( $scope ) {
$scope.selectedIndex = 0;
$scope.displayMap = function() {
$scope.selectedIndex = 1;
};
Could you please check it? Hope it helps
Plunker here

here is my solution:
<div layout="column" flex>
<md-tabs md-dynamic-height md-border-bottom md-selected="vm.selectedIndex">
<md-tab label="Genel" md-on-select="vm.selectedIndex = 0">
<div class="md-padding">{{vm.selectedIndex}}</div>
</md-tab>
<md-tab label="Sipariş / Planlama" md-on-select="vm.selectedIndex = 1">
<div class="md-padding">{{vm.selectedIndex}}</div>
</md-tab>
<md-tab label="Kalite Kontrol Oranları" md-on-select="vm.selectedIndex = 2">
<div class="md-padding">{{vm.selectedIndex}}</div>
</md-tab>
<md-tab label="E-Posta" md-on-select="vm.selectedIndex = 3">
<div class="md-padding">{{vm.selectedIndex}}</div>
</md-tab>
</md-tabs>
</div>

With md-selected = 0 you go to the first tab. And md-selected = 1 to the second tab.
button:
<md-button ng-click="displayMap()">Map</md-button>
controller:
$scope.displayMap = function() {
$scope.selectedIndex = 1; //select second tab
};

Related

AngularJS, Angular-Material - HTML Not rendering despite object present on $scope

I have searched extensively for a solution to this problem and can't seem to find one. Any help here would be greatly appreciated.
The Basics:
Utilizing angular-material tabs
Upon selection of an item from a dropdown, a call is made to a firebase database and returns a response, which is put into an array on the $scope.
HTML is utilizing ng-repeat on this response object.
The Problem:
Despite the response object being present on the scope, the html does not render anything until the user "clicks" another button on the view - any button at all. In fact, the user has to simply touch/click something on the screen and then the results render.
If user makes a call to the database to get artists in a certain medium (i.e. painting), but does not click anything on the screen, no results will show at all, despite response object being present in $scope.
I am stumped.
HTML:
<md-tabs md-dynamic-height md-border-bottom md-center-tabs><md-tab label="Artists">
<md-content id="tab_background" class="md-padding">
<div class="query_results hide_link" layout-padding>
<a ng-repeat="artist in results | filter: searchText"
href="/#/artist/{{artist.selectedMedium}}/{{artist.uid}}">
<md-card>
<img ng-src="{{artist.profImg}}" class="md-card-image" alt="Washed Out">
<md-card-header>
<div id="card_play_button_included">
<md-card-header-text>
<span class="hide_link md-title">{{artist.name}}{{artist.name_last}}</span>
<span class="hide_link md-subhead">{{artist.selectedSubmedium[0]}}</span>
<span class="hide_link md-caption">{{artist.neighborhood}}</span>
</md-card-header-text>
</div>
</md-card-header>
<md-card-actions layout="row" layout-align="end center">
</md-card-actions>
</md-card>
</a>
</div>
</md-content>
</md-tab>
<md-tab label="Events">
</md-tab>
</md-tabs>
Javascript:
$scope.getArtists = function(medium){
//resetting results array
$scope.firstArray = [];
$scope.results = [];
var Medium = medium.name;
firebase.database().ref('/Artists/' + Medium).once('value').then(function(snapshot){
console.log(snapshot.val());
var obj = snapshot.val();
for (var key in obj) {
var innerObj = obj[key]
innerObj.uid = key;
console.log(innerObj);
$scope.firstArray.push(innerObj);
}
$scope.results = $scope.firstArray;
$scope.runSpinner();
})
}
I used $scope.apply() and it solved it.

$anchorScroll after div is created

I've got a problem using $anchorScroll. I've got a div with an ng-ig condition that isn't visible at the beginning. I need use the $anchorScroll function to go in that div when a button is clicked. This button also make the div visible. Right now doesn't work because i think it fires the $anchorScroll before the div is created. This is the code:
<body ng-app="testApp" data-ng-controller="searchController as searchCtrl" >
<div id="scrollArea">
<form>
//HTML input elements
<div class="buttons">
<md-button class="md-primary md-raised" ng-click="searchCtrl.gotoTable('resultTable')">Start</md-button>
{{searchCtrl.test}}
</div>
</form>
</div>
<br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/>
<br/><br/><br/><br/><br/><br/><br/><br/><br/><br/>
<br/><br/><br/><br/><br/><br/><br/><br/><br/>
<br/><br/><br/><br/><br/><br/><br/><br/>
<br/><br/><br/><br/><br/><br/><br/><br/><br/>
<br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/>
<!-- Smart Table for displaying result -->
<div id="resultTable" class="rtable">
<div ng-if="searchCtrl.test == true" >
//table content
</div>
</div>
</body>
and the angular part
var app = angular.module('testApp', []);
app.controller("searchController", function($scope, $location, $anchorScroll, $timeout){
var self = this;
self.test = false;
self.gotoTable = function(resultTable)
{
self.test = true;
self.anchor(resultTable);
};
self.anchor = function(resultTable) {
$location.hash('resultTable');
$anchorScroll();
} ;
});
Plunker: http://plnkr.co/edit/HrdN2ZACDui61l1kPHEj?p=preview
Thanks
After playing around with your plunker, I've found that updating angularJs for the latest version fixed the problem.
I don't know if you can update your angular version?
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.min.js"></script>

angular material - sidenav from the bottom?

Angular material sidenav seems to have a left/right options:
<md-sidenav class="md-sidenav-**right**">
<md-sidenav class="md-sidenav-**left**">
Although it is called a "side"-nav, is it possible to use it as a "bottom"-nav?
if so how can it be done?
there is no bottom-nav as you said, but you can do it using https://material.angularjs.org/latest/demo/bottomSheet
VIEW
<div class="bottom-sheet-demo inset" layout="row" layout-sm="column" layout-align="center" >
<md-button flex="50" class="md-primary md-raised" ng-click="showBottomSheet()">Show</md-button>
</div>
JS
$scope.showBottomSheet = function() {
$scope.alert = '';
$mdBottomSheet.show({
templateUrl: 'bottom-sheet-template.html',
controller: 'BottomSheetCtrl'
}).then(function(clickedItem) {
$scope.alert = clickedItem['name'] + ' clicked!';
});
};
angular material does not support bottom nav .. you have to achieve it manually.
I think that what you are trying to achieve can be done by overwriting angular material styles to suit your needs. Just add some custom class or id to <md-sidenav> element and apply your styles.

Injecting $mdMenu into scope. Cannot read property open() of undefined

I am trying to get the Angular Material Design menu to work but I don't seem to be able to use the $mdMenu that is supposed to be injected by the ng-click.
My HTML markup:
<div layout="column" layout-fill ng-controller="AuthControl">
<md-toolbar ng-controller="navigationControl">
<div ng-controller="menu as ctrl">
<md-menu>
<md-button class="md-icon-button" ng-click="ctrl.open($mdMenu, $event)">
<md-icon>menu</md-icon>
</md-button>
<md-menu-content width="4">
<md-menu-item>
<md-button>
<md-icon>account_circle</md-icon>
</md-button>
</md-menu-item>
</md-menu-content>
</md-menu>
</div>
</md-toolbar>
</div>
The Angular Controller:
controllers.controller('menu', function menuControl($mdDialog) {
var originatorEv;
this.open = function($mdMenu, ev) {
originatorEv = ev;
$mdMenu.open(ev);
};
});
The contoller gets injected properly but when I run I get the Error
TypeError: Cannot read property 'open' of undefined
Does anybody know how to fix this?
Thanks
Instead of mdMenu, pass mdOpenMenu
<md-button aria-label="menu" class="md-fab md-mini md-primary" ng-click="ctrl.openMenu($mdOpenMenu, $event)">
Controller:
this.openMenu = function($mdOpenMenu, ev) {
originatorEv = ev;
$mdOpenMenu(ev);
};
DEMO
"$mdOpenMenu" has been replaced by "$mdMenu.open" and is now deprecated. Use the latest version of Angular Material and it will work just fine.

Angular Material switch between show and hide programmatically

Is it possible to switch between the attributes show and hide by button click (programmatically)? I have for example a card with a map and a list view.
These are usually displayed side by side. On mobile devices, but is for the List view flex = 100 increases. The map is no longer displayed. The user should however have the possibility to switch between the two views. How I can do that?
My example tags:
<md-card flex-gt-xs="40" flex-xs="100">
<list></list>
</md-card>
<md-button>toggle Views</md-button>
<md-card flex="60" hide-xs show-gt-xs >
<leaflet height="40vh" ></leaflet>
</md-card>
Update:
Summary
I would like to have 2 columns that can be switched on mobile devices, and side by side on larger devices.
I'm not 100% sure what you are asking but this CodePen demonstrates the basics of toggling programatically.
md-button has an ng-click attribute that calls the function toggle() which toggles the view value. view is passed to the ng-if of each card.
Markup
<div ng-controller="AppCtrl" ng-cloak="" ng-app="MyApp" style="height:100%" layout="column">
<md-card flex-gt-xs="40" flex-xs="100" ng-if="view">
Card 1
<list></list>
</md-card>
<md-button ng-click="toggle()">toggle Views</md-button>
<md-card flex="60" hide-xs show-gt-xs ng-if="!view">
Card2
<leaflet height="40vh" ></leaflet>
</md-card>
</div>
JS
angular.module('MyApp',['ngMaterial', 'ngMessages', 'material.svgAssetsCache', 'ngDialog'])
.controller('AppCtrl', function($scope) {
$scope.view = true;
$scope.toggle = function () {
$scope.view = !$scope.view;
}
});
You can replace ng-if with ng-show if you need to retain information in elements that you are toggling as ng-if recreates the element each time it is true.
I have found the solution. $mdMedia does all the magic.
js:
$scope.$watch(function () {
return $mdMedia('sm');
}, function (big) {
$scope.screenIsSmall = $mdMedia('sm');
});
View:
<md-card flex="60" ng-hide="screenIsSmall&&!showMap" style="max-height: 40vh">
<md-button ng-show="screenIsSmall" ng-click="showMap = !showMap">toggle</md-button>
<leaflet height="40vh"></leaflet>
</md-card>
app.component.html
<div fxLayout="column" fxLayout.gt-sm="row wrap">
<div fxFlex="50" class="flex-p">
<mat-slide-toggle
[checked]="isSlideChecked"
(change)="toggleChanges($event)"
>Hide/Show - card</mat-slide-toggle
>
</div>
</div>
<mat-card *ngIf="isSlideChecked">Simple card</mat-card>
app.component.ts
isSlideChecked: boolean = false;
toggleChanges($event: MatSlideToggleChange) {
this.isSlideChecked = $event.checked;
}

Categories