Method binding is not working in Angular - angularjs

I am trying to attached a controller scope method in a directive but when i click in directive button that linked method is not called. Please review code. There is side-nav directive in which i have attached method with select parameter. but when button is clicked method is not called.
index.html
<div class="container" layout="row" flex ng-controller="userController as vm" ng-init="vm.loadUsers()">
<md-sidenav md-is-locked-open="true" class="md-whiteframe-1dp">
<side-nav users="vm.users" select="vm.selectUser(user)"></side-nav>
</md-sidenav>
<md-content id="content" flex>
<user-detail selected="vm.selected" share="vm.share()"></user-detail>
</md-content>
</div>
userController.js
app.controller("userController", ['$scope', 'userService', '$mdBottomSheet', function ($scope, userService, $mdBottomSheet) {
var self=this;
self.users = [];
this.name = "manish";
self.loadUsers = function () {
userService
.loadAllUsers()
.then(function (users) {
self.users = users;
self.selected = users[0];
userService.selectedUser = self.selected;
});
}
self.selectUser = function (user) {
self.selected = user;
userService.selectedUser = self.selected;
}
}]);
directives.js
app.directive("sideNav", function () {
return {
restrict :'AE',
templateUrl: './views/sidenav.html',
scope : {
select : '&',
users : '='
}
}
});
./views/sidenav.html
<md-list>
<md-list-item ng-repeat="user in users">
<md-button ng-click="select({user:user)">
<md-icon md-svg-icon="{{user.avatar}}" class="avatar"></md-icon>
{{user.name}}
</md-button>
</md-list-item>
</md-list>

Try doing it like this:
index.html
<side-nav users="vm.users" select="vm.selectUser"></side-nav>
./views/sidenav.html
<md-button ng-click="select()(user)">

Related

ng-click does not fire the second time with mdDialog

I'm new to angular JS. I'm trying to create a simple mdDialog which contains a form for signing up.
I works the first time but the ng-click does not respond the second time. I don't get any errors in console.
It works on page reload again.
HTML
<div ng-controller="UsersController as uc">
<a ng-click="uc.signUp()">Click here to sign up for the newsletter!</a>
</div>
Controller
function subscribe() {
var successHandler = function () {
$mdDialog.show(
$mdDialog.alert()
.title('Subscription processed')
.textContent('Thank you for signing up')
.ok('Ok')
);
};
if (self.email) {
UsersService.subscribe(self.email, self.firstName, self.lastName).then(successHandler, errorHandler);
self.email = '';
self.firstName = '';
self.lastName = '';
$timeout(function () {
self.showSubscribe = false;
});
}
}
function signUp() {
$mdDialog.show({
clickOutsideToClose: true,
scope: $scope,
templateUrl: '/view/templates/subscribe.html',
controller: function DialogController($scope, $mdDialog) {
$scope.cancel = $mdDialog.cancel;
}
});
}
Template:
<div class="newsletter" layout="row" layout-align="center start" flex>
<form class="form" name="newsletterForm" ng-submit="uc.subscribe();">
<div>... </div>
</form>
<div class="toolbar" flex-auto="10">
<md-button class="md-icon-button" ng-click="cancel()">
<md-icon aria-label="Close">clear</md-icon>
</md-button>
</div>

Pushing item to array after close md-dialog not working

Array does not refresh after pushing item through md-dialog, though I can save the item normally.
I tried to test which level I could push an item into the array manually and I could do so until $scope.showAddUsuario(), after that I can't push an item, even manually:
Usuario.html
<div flex-gt-sm="100" flex-gt-md="100" ng-controller="UsuarioCtrl">
<h2 class="md-title inset">Usuario</h2>
<md-card>
<md-list>
...
</md-list>
</md-card>
<md-button class="md-fab" aria-label="Add" ng-click="showAddUsuario($event)">
<md-icon md-svg-icon="content:ic_add_24px" aria-label="Plus"></md-icon>
</md-button>
</div>
md-dialog:
<md-dialog aria-label="Form">
<md-content class="md-padding">
<form name="userForm">
<div layout layout-sm="column">
<md-input-container flex> <label>Nome</label> <input ng-model="item.nome"> </md-input-container>
</div>
<div layout layout-sm="column">
<md-input-container flex> <label>E-mail</label> <input ng-model="item.email"> </md-input-container>
</div>
<div layout layout-sm="column">
<md-input-container flex> <label>Senha</label> <input ng-model="item.senha"> </md-input-container>
</div>
</form>
</md-content>
<div class="md-actions" layout="row">
<span flex></span>
<md-button ng-click="cancel()"> Cancel </md-button>
<md-button ng-click="saveUsuario(item)" class="md-primary"> Save </md-button>
</div>
</md-dialog>
Controller:
app.controller('UsuarioCtrl', function ($scope, $http, $mdDialog, $interval, $timeout) {
$scope.items = [];
$http({
method : 'GET',
url : 'UsuarioServlet'
})
.success(
function(data, status, headers,
config) {
$scope.items = data;
}).error(
function(data, status, headers,
config) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
$scope.saveUsuario = function(item) {
$scope.items.push({id:100, nome:item.nome, email:item.email, senha:item.senha, status:1});
};
$scope.showAddUsuario = function(ev) {
$mdDialog.show({
controller: 'UsuarioCtrl',
templateUrl : 'CrudUsuario.html',
targetEvent : ev,
locals : {
item : null
}
})
};
});
Only now do I see that you were using $mdDialog and not $modal. So this answer will most likely not apply to your case.
You do however seem to be missing a .finally() part in your code.
From the manual ( https://material.angularjs.org/latest/api/service/$mdDialog )
$mdDialog
.show( alert )
.finally(function() {
alert = undefined;
});
So you could try that.
Otherwise, have you considered using $modal instead?
You don't seem to be handling the return of information from your modal.
(function (){
'use strict';
function myCtrl($modal){
var vm = this;
vm.myArray = [];
...
function openModal(){
var modalInstance = $modal.open({
templateUrl: 'path/to/modal/view.html',
controller: 'MyModalCtrl as vm',
size: 'lg',
resolve: {
data: function(){
return dataThatYouWantToSendFromHereToYourModal;
}
}
});
modalInstance.result.then(function (result){
vm.myArray.push(result);
});
}
...
}
...
})();
The modalIntsance.result.then will trigger when you in your modal-controller (in this case MyModalCtrl) issues a $modalInstance.close(sendThisDataBackToCallingController) call.
So the modal-controller should look along the lines of
...
function MyModalCtrl($modalInstance, data){
...
function init(){
doSomethingWithDataThatYouGotFromTheCallingController(data);
}
...
function save(){
var dataToSendBack = {...};
$modalInstance.close(dataToSendBack);
}
...
}
...
That should get you going in the right direction.
Thanks for the comments! The documentation say to use that:
}).then(function(item) {
$scope.items.push({id:100, nome:item.nome, email:item.email, senha:item.senha, status:1});
});
Works fine!
I solve this problem ,Please replace $mdDialog.show() as
$mdDialog.show({
controller: function (){
this.parent = $scope;
},
templateUrl: 'dialog1.tmpl.html',
scope:$scope.$new(),
targetEvent : ev,
bindToController: true,
clickOutsideToClose:true,
fullscreen: useFullScreen
})

ng-click not firing in md-bottom-sheet

My apologies in advance for a lengthy post. I wanted to include as much data as possible to see if you could assist me in my problem.
I originally developed the project using Bootstrap as a prototype and proof of concept. Now that I'm planning on going into production, I wanted to use angular-material.
Everything worked perfectly in Bootstrap. However, now that I'm using material design, ng-click is not working now that I'm using md-bottom-sheet. Here is the full code snippets;
HTML
index.html
<html ng-app="teamApp">
<head>
<link href="https://ajax.googleapis.com/ajax/libs/angular_material/0.9.4/angular-material.min.css" rel="stylesheet">
<link href="css/style.css" rel="stylesheet">
</head>
<body layout-fill layout-margin layout-padding layout="column" ng-controller="TeamController as teamCtrl">
<md-toolbar layout="row">
<div class="md-toolbar-tools">
<md-button class="md-icon-button" hide-gt-sm ng-click="toggleSidenav('left')">
<md-icon aria-label="Menu" md-svg-icon="https://s3-us-west-2.amazonaws.com/s.cdpn.io/68133/menu.svg"></md-icon>
</md-button>
<h2>Team Builder</h2>
</div>
</md-toolbar>
<div flex layout="row">
<md-sidenav class="md-sidenav-left md-whiteframe-z2" layout="column" md-component-id="left" md-is-locked-open="$mdMedia('gt-sm')">
<md-toolbar layout="row">
....
</md-toolbar>
<md-list>
....
</md-list>
</md-sidenav>
<div flex id="content" layout="column">
<md-content class="md-padding" flex layout="column">
<!-- Custom Directive to team-form.html -->
<team-forms></team-forms>
</md-content>
</div>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.4.2/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.2/angular-animate.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.2/angular-aria.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angular_material/0.10.1/angular-material.min.js"></script>
<script src="js/team.js"></script>
</body>
</html>
You will see the team-forms directive. This is a custom directive that pulls in team-form.html. team-form.html has the button that when clicked pops up the md-bottom-sheet.
team-form.html
<div class="teamForms" layout="column">
<div id="team-list" class="row" flex>
<md-list>
<md-subheader class="md-no-sticky">Teams</md-subheader>
<md-list-item ng-repeat="team in teams">
........
</md-list-item>
</md-list>
</div>
<!-- button that when click, makes md-bottom-sheet pop up -->
<md-button class="md-fab" style="position:absolute; right:0; bottom:0" aria-label="Add Team" ng-click="teamCtrl.showAddTeam($event)">
<span style="font-size:3em; line-height:1.2em">+</span>
</md-button>
</div>
The HTML template used for the md-bottom-sheet is team-add.html. This is what pops up when the button above is clicked.
team-add.html
<!-- FORM TO CREATE team -->
<md-bottom-sheet>
{{formData}}
<md-toolbar layout="row">
<div class="md-toolbar-tools">
<h2>Add Team</h2>
</div>
</md-toolbar>
<form name="add-team">
<md-content layout-padding layout-sm="column" layout="row">
<md-input-container>
<label>Team Name</label>
<input name="teamName" ng-model="formData.name" required type="text">
</md-input-container>
</md-content>
<div layout="row" ng-show="formData.name.length >= 1">
<md-input-container>
<label>Employee Name</label>
<input class="form-control" name="teamEmployee" ng-model="employee.name" type="text">
</md-input-container>
<md-button class="md-raised md-primary" ng-click="addEmployee(formData)" type="submit">Add</md-button>
</div>
<md-content layout="row">
<md-input-container>
<md-button class="md-raised md-primary" ng-click="teamCtrl.createTeam()">Add Team</md-button>
</md-input-container>
</md-content>
</form>
</md-bottom-sheet>
JS
team.js
(function() {
'use strict';
var app = angular.module("teamApp", ["ngMaterial"]);
app.controller("TeamController", ["$scope", "$http", "$mdSidenav", "$mdBottomSheet", function($scope, $http, $mdSidenav, $mdBottomSheet) {
$scope.formData = {};
$scope.formData.employees = [];
// when landing on the page, get all teams and show them
$http.get("/api/teams")
.success(function(data) {
$scope.teams = data;
})
.error(function(data) {
console.log('Error: ' + data);
});
$scope.toggleSidenav = function(menuId) {
$mdSidenav(menuId).toggle();
};
this.showAddTeam = function($event) {
$mdBottomSheet.show({
templateUrl: 'directives/team/team-add.html',
targetEvent: $event
})
};
this.resetForms = function() {
$scope.teamForm = false;
$scope.employeeForm = false;
};
this.getTeam = function(id) {
$http.get("/api/teams/" + id)
.success(function(data) {
$scope.singleTeam = data;
console.log('Success: ' + data);
})
.error(function(data) {
console.log('Error: ' + data);
});
};
// when submitting the add form, send the text to the node API
this.createTeam = function() {
$http.post("/api/teams", $scope.formData)
.success(function(data) {
$scope.formData = {}; // clear the form so our user is ready to enter another
$scope.formData.employees = [];
$scope.teams = data;
console.log(data);
})
.error(function(data) {
console.log('Error: ' + data);
});
};
this.updateTeam = function(id) {
$http.put("/api/teams/" + id, $scope.singleTeam[0])
.success(function(data) {
$scope.singleTeam = {};
$scope.teams = data;
console.log('Success: ' + data);
})
.error(function(data) {
console.log('Error: ' + data);
});
};
// delete a todo after checking it
this.deleteTeam = function(id) {
$http.delete("/api/teams/" + id)
.success(function(data) {
$scope.teams = data;
console.log(data);
})
.error(function(data) {
console.log('Error: ' + data);
});
};
this.getRotation = function() {
$http.post("/api/rotations")
.success(function(data) {
console.log(data);
})
.error(function(data) {
console.log('Error: ' + data);
});
};
}]);
app.directive("teamForms", function() {
return {
restrict: "E",
templateUrl: "directives/team/team-forms.html",
controller: ["$scope", function($scope) {
$scope.employee = {};
$scope.teamForm = false;
$scope.employeeForm = false;
this.showTeamForm = function(value) {
return $scope.teamForm == value;
};
this.setTeamForm = function(value) {
$scope.teamForm = value;
};
this.showEmployeeForm = function(value) {
return $scope.employeeForm == value;
};
this.setEmployeeForm = function(value) {
$scope.employeeForm = value;
};
$scope.addEmployee = function(dataSet) {
dataSet.employees.push($scope.employee);
$scope.employee = {};
};
$scope.removeEmployee = function(dataSet, index) {
dataSet.employees.splice(index, 1);
};
}],
controllerAs: "teamFormCtrl"
};
});
app.directive("teamEditForm", function() {
return {
restrict: "E",
templateUrl: "directives/team/team-edit.html"
};
});
}());
The issue is in the team-add.html. It's the md-button that is trying to call createTeam().
The expected result is that it would post the name of the team to my API endpoint and into my mongoDB setup. This was working perfectly before in bootstrap but I feel that now I'm deeper in the UI with how md-bottom-sheet needs to be setup, that I have some scoping or controller issue in place.
I have even tried adding a fake, non-existent function to ng-click to see if some error was thrown when clicked but no error showed up. Node is not even reporting a post command being sent. It just seems that the button is doing absolutely nothing
Any help would be greatly appreciated and if more code is needed, please let me know and I'll post it up!
Thanks!
In your md-button your ng-click is something like this
<md-button class="md-fab"
ng-click="teamCtrl.showAddTeam($event)">
Your question reads "ng-click not firing", to make ng-click work give this a try
ng-click="showAddTeam($event)">
This will work since you are trying to call a function which is in some controller and your directive's html is within at controller's scope only.

Add dropdown menu inside sidebar with material design?

I would like when click on polls , open dropdown menu which include sub categories of pulls.
How i should change my code in order to add this functionality with material design.
In state provider:
$stateProvider
.state('admin', {
/*abstract: true,*/
url: '/admin',
templateUrl: 'app/admin/admin.html',
controller: 'AdminCtrl',
onEnter: function($rootScope) {
$rootScope.title = $rootScope.titleRoot + ' | Admin';
}
});
});
and in .html
<md-sidenav class="md-sidenav-left md-whiteframe-z2" md-component-id="left" md-is-locked-open="$mdMedia('gt-md')">
<md-toolbar class="md-theme-indigo">
<h1 class="md-toolbar-tools">Admin</h1>
</md-toolbar>
<md-content class="" ng-controller="LeftCtrl">
<md-button ng-click="close()" class="md-primary" hide-gt-md>
Close Sidenav Left
</md-button>
<!--<p hide-md show-gt-md>-->
<!--</p>-->
<ul class="sidenav-menu">
<li class="" ng-repeat="section in sections">
<md-button ui-sref="{{section.link}}">
<i class="fa fa-fw {{section.icon}}"></i> {{section.title}}
</md-button>
</li>
</ul>
</md-content>
</md-sidenav>
Inside Controller:
.controller('AdminCtrl', function($scope, $mdSidenav, $timeout, $log) {
$scope.sections = [
{
title: 'User Management',
icon: 'fa-user',
link: 'usermanagement'
},{
title: 'Polls',
icon: 'fa-files-o',
link: 'polls'
}
];
$scope.toggleLeft = function () {
$mdSidenav('left').toggle()
.then(function () {
//$log.debug("toggle left is done");
});
};
})
.controller('LeftCtrl', function($scope, $timeout, $mdSidenav, $log) {
$scope.close = function() {
$mdSidenav('left').close()
.then(function(){
//$log.debug("close LEFT is done");
});
};
});
Since you are using Angular-Material, I suggest you use it's Menu directive: https://material.angularjs.org/latest/#/demo/material.components.menu

AngularJS : service - controller - view data binding

I am trying to bind data between a service, controller and a view. Below, I have provided plunker link for the app.
Basically 'dataService' has a function to find geolocation. The function sets the variable dataService.locationDone.
The dataService is assigned to $scope.data in the 'HomeController'.
In home.html, I refer the value of data.locationDone to show different labels.
The issue is, after the locationDone variable is modified in the dataService, the labels in the home.html does not change.
plunker link: http://embed.plnkr.co/BomSztgC7PzGMzJyDKoF/preview
Service:
storelocator.factory('dataService', [function() {
var data = {
locationDone: 0,
position: null,
option: null
}
var getLocation = function() {
data.locationDone = 0;
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function (pos) {
data.locationDone = 1;
data.position = pos;
console.log('got location ', data.locationDone);
}, function () {
data.locationDone = 2;
});
} else {
data.locationDone = 3;
}
};
getLocation();
return data;
}]);
Controller:
storelocator.controller('HomeController', ['$scope', '$location', 'dataService', function ($scope, $location, dataService) {
$scope.data = dataService;
console.log('Home ctrl', $scope.data.locationDone);
$scope.fn = {};
$scope.fn.showOne = function () {
$scope.data.option = 3;
$location.path('/map');
};
$scope.fn.showAll = function () {
$scope.data.option = 99;
$location.path('/map');
};
}]);
view:
<div layout="column" layout-align="start center">
<div layout="column" layout-margin layout-padding>
<div flex layout layout-margin layout-padding>
<md-whiteframe flex class="md-whiteframe-z1" layout layout-align="center center" ng-show="data.locationDone==0">
<span flex>Awaiting location information.</span>
</md-whiteframe>
<md-whiteframe flex class="md-whiteframe-z1" layout layout-align="center center" ng-show="data.locationDone==2"><span flex>Error in getting location.</span>
</md-whiteframe>
<md-whiteframe flex class="md-whiteframe-z1" layout layout-align="center center" ng-show="data.locationDone==3"><span flex>The browser does not support location.</span>
</md-whiteframe>
</div>
<md-button flex ng-show="data.locationDone==1" class="md-accent md-default-theme md-raised" ng-click="fn.showOne()">Find Nearest Three Stores</md-button>
<div flex></div>
<md-button ng-show="data.locationDone==1" flex class="md-accent md-default-theme md-raised" ng-click="fn.showAll()">Find All Stores</md-button>
</div>
</div>
You are updating the variable locationDone defined in the scope of the service. It will not have any impact on the object that you have returned during in the service (when updated later). Instead predefine an object in your service and update the property on the reference rather than a variable.
Also note that since you are using native async api, which runs out of the angular context you would need to manually invoke digest cycle by doing $rootScope.$apply() in your case or just use $timeout wrap your updates in $timeout. However better approach would be to property create an api in dataService with method returning $q promise.
With timeout
.factory('dataService', ['$timeout', function($timeout) {
var service = { //Define here;
locationDone: 0,
position: null,
option: null
}
var getLocation = function() {
service.locationDone = 0;
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function (pos) {
$timeout(function(){
service.locationDone = 1;
service.position = pos;
});
}, function () {
$timeout(function(){
service.locationDone = 2;
});
});
} else {
service.locationDone = 3;
}
};
getLocation();
return service; //return it
}]);
With qpromise implementation:
.factory('dataService', ['$q', function($q) {
return {
getLocation:getLocation
};
function getLocation() {
if (navigator.geolocation) {
var defer = $q.defer();
navigator.geolocation.getCurrentPosition(function (pos) {
defer.resolve({locationDone : 1,position : pos})
}, function () {
defer.resolve({ locationDone : 2});
});
return defer.promise;
}
return $q.when({locationDone : 3});
};
}]);
And in your controller:
$scope.data = {};
dataService.getLocation().then(function(result){
$scope.data = result;
});
Demo
angular.module('app', []).controller('HomeController', ['$scope', '$location', 'dataService', function ($scope, $location, dataService) {
$scope.data = {};
dataService.getLocation().then(function(result){
$scope.data = result;
})
console.log('Home ctrl', $scope.data.locationDone);
$scope.fn = {};
$scope.fn.showOne = function () {
$scope.data.option = 3;
$location.path('/map');
};
$scope.fn.showAll = function () {
$scope.data.option = 99;
$location.path('/map');
};
}]).factory('dataService', ['$q', function($q) {
return {
getLocation:getLocation
};
function getLocation() {
if (navigator.geolocation) {
var defer = $q.defer();
navigator.geolocation.getCurrentPosition(function (pos) {
defer.resolve({locationDone : 1,position : pos})
}, function () {
defer.resolve({ locationDone : 2});
});
return defer.promise;
}
return $q.when({locationDone : 3});
};
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="HomeController">
<div layout="column" layout-align="start center">
<div layout="column" layout-margin layout-padding>
<div flex layout layout-margin layout-padding>
<md-whiteframe flex class="md-whiteframe-z1" layout layout-align="center center" ng-show="data.locationDone==0">
<span flex>Awaiting location information.</span>
</md-whiteframe>
<md-whiteframe flex class="md-whiteframe-z1" layout layout-align="center center" ng-show="data.locationDone==2"><span flex>Error in getting location.</span>
</md-whiteframe>
<md-whiteframe flex class="md-whiteframe-z1" layout layout-align="center center" ng-show="data.locationDone==3"><span flex>The browser does not support location.</span>
</md-whiteframe>
</div>
<md-button flex ng-show="data.locationDone==1" class="md-accent md-default-theme md-raised" ng-click="fn.showOne()">Find Nearest Three Stores</md-button>
<div flex></div>
<md-button ng-show="data.locationDone==1" flex class="md-accent md-default-theme md-raised" ng-click="fn.showAll()">Find All Stores</md-button>
</div>
</div>
</div>

Resources