$location.path() not working - angularjs

I'm trying to use $location.path() inside of an $interval to automatically navigate from tab to tab on a timer.
However, whenever I call $location.path(x) it only reloads my current view.
My code is as follows:
tabsController.js
(function () {
var injectParams = ['$location', '$routeParams', '$scope', '$rootScope', '$interval', 'teleAiDiagnosticsConfig'];
var TabsController = function ($location, $routeParams, $scope, $rootScope, $interval, teleAiDiagnosticsConfig) {
var vm = this;
$rootScope.autoNavInterval = {};
$rootScope.AutoNavActive = false;
$scope.tabs = [
{ link: '#!/vehicles', label: 'Vehicles', active: (($location.$$url.indexOf('/vehicle') !== -1) ? true : false) },
{ link: '#!/workzones', label: 'Workzones', active: (($location.$$url.indexOf('/workzone') !== -1) ? true : false) },
{ link: '#!/operatorStations', label: 'Operator Stations', active: (($location.$$url.indexOf('/operatorStation') !== -1) ? true : false) },
{ link: '#!/sessionStatus/123', label: 'Session Status (123)', active: (($location.$$url.indexOf('/sessionStatus') !== -1) ? true : false) }
];
// find the tab that should be marked as active
for (var i = 0; i < $scope.tabs.length; i++) {
if ($scope.tabs[i].active == true) {
$scope.selectedTab = $scope.tabs[i];
break;
}
}
if ($scope.selectedTab == undefined)
$scope.selectedTab = $scope.tabs[0];
$scope.setSelectedTab = function (tab) {
$scope.selectedTab = tab;
}
$scope.tabClass = function (tab) {
if ($scope.selectedTab == tab) {
return "active";
} else {
return "";
}
}
$scope.nextTab = function () {
for (var i = 0; i < $scope.tabs.length; i++) {
if ($scope.tabs[i] == $scope.selectedTab) {
if (i == $scope.tabs.length - 1) {
$location.path($scope.tabs[0].link);
if (!$scope.$$phase) $scope.$apply()
$scope.tabs[0].active = true;
$scope.selectedTab = $scope.tabs[0];
break;
}
else {
$location.path($scope.tabs[i + 1].link);
if (!$scope.$$phase) $scope.$apply()
$scope.tabs[i + 1].active = true;
$scope.selectedTab = $scope.tabs[i + 1];
break;
}
}
}
}
$scope.startAutoNav = function () {
$rootScope.AutoNavActive = true;
$rootScope.autoNavInterval = $interval(function () {
$scope.nextTab();
}, teleAiDiagnosticsConfig.autoNavTimer);
}
$scope.stopAutoNav = function () {
$rootScope.AutoNavActive = false;
$interval.cancel($rootScope.autoNavInterval);
}
$scope.startAutoNav();
};
TabsController.$inject = injectParams;
angular.module('teleAiDiagnostics').controller('TabsController', TabsController);
}());
Yes, yes, I know using $rootScope is not advised. I'm going to change it once I get $location.path() working.
I've tried it with and without the if (!$scope.$$phase) $scope.$apply() lines. I've also tried adding a call to $location.replace() but this doesn't do anything for me.
Whenever $location.path() is called the view section flashes but it just reloads the Vehicles view.
app.js
(function () {
var app = angular.module('teleAiDiagnostics', ['ngRoute', 'ngAnimate', 'ui.bootstrap']);
app.config(['$routeProvider', function ($routeProvider) {
var viewBase = 'app/views/';
$routeProvider
.when('/vehicles', {
controller: 'VehiclesController',
templateUrl: viewBase + 'vehicles.html',
controllerAs: 'vm'
})
.when('/vehicle/:id', {
controller: 'VehicleController',
templateUrl: viewBase + "vehicle.html",
controllerAs: 'vm'
})
.when('/workzones', {
controller: 'WorkzonesController',
templateUrl: viewBase + 'workzones.html',
controllerAs: 'vm'
})
.when('/workzone/:id', {
controller: 'WorkzoneController',
templateUrl: viewBase + "workzone.html",
controllerAs: 'vm'
})
.when('/operatorStations', {
controller: 'OperatorStationsController',
templateUrl: viewBase + 'operatorStations.html',
controllerAs: 'vm'
})
.when('/operatorStation/:id', {
controller: 'OperatorStationController',
templateUrl: viewBase + 'operatorStation.html',
controllerAs: 'vm'
})
.when('/sessionStatus/:id', {
controller: 'SessionStatusController',
templateUrl: viewBase + 'sessionStatus.html',
controllerAs: 'vm'
})
.otherwise({ redirectTo: '/vehicles' });
}]);
// Intialize slider
$('.flexslider').flexslider({
animation: 'slide',
slideshow: true,
pauseOnAction: true,
controlNav: true,
pauseOnHover: true,
touch: true,
directionalNav: false,
direction: 'horizontal',
slideshowSpeed: 6000,
smoothHeight: true
});
}());
Not sure why this isn't working. I've followed all the tips I could find and I'm still getting the same result. I'm tempted to just add UI-Router to my application and replace my routes with states.

Found it. The issue was related to the hash-bang (#!) in the URL.
I added a link to my tab definitions which does not contain the hash-bang and it works perfectly now.
tabsController.js
(function () {
var injectParams = ['$location', '$routeParams', '$scope', '$rootScope', '$interval', 'teleAiDiagnosticsConfig'];
var TabsController = function ($location, $routeParams, $scope, $rootScope, $interval, teleAiDiagnosticsConfig) {
var vm = this;
$rootScope.autoNavInterval = {};
$rootScope.AutoNavActive = false;
$scope.tabs = [
{ link: '#!/vehicles', refLink: '/vehicles', label: 'Vehicles', active: (($location.$$url.indexOf('/vehicle') !== -1) ? true : false) },
{ link: '#!/workzones', refLink: '/workzones', label: 'Workzones', active: (($location.$$url.indexOf('/workzone') !== -1) ? true : false) },
{ link: '#!/operatorStations', refLink: '/operatorStations', label: 'Operator Stations', active: (($location.$$url.indexOf('/operatorStation') !== -1) ? true : false) },
{ link: '#!/sessionStatus/123', refLink: '/sessionStatus/123', label: 'Session Status (123)', active: (($location.$$url.indexOf('/sessionStatus') !== -1) ? true : false) }
];
// find the tab that should be marked as active
for (var i = 0; i < $scope.tabs.length; i++) {
if ($scope.tabs[i].active == true) {
$scope.selectedTab = $scope.tabs[i];
break;
}
}
if ($scope.selectedTab == undefined)
$scope.selectedTab = $scope.tabs[0];
$scope.setSelectedTab = function (tab) {
$scope.selectedTab = tab;
}
$scope.tabClass = function (tab) {
if ($scope.selectedTab == tab) {
return "active";
} else {
return "";
}
}
$scope.nextTab = function () {
for (var i = 0; i < $scope.tabs.length; i++) {
if ($scope.tabs[i] == $scope.selectedTab) {
if (i == $scope.tabs.length - 1) {
$location.path($scope.tabs[0].refLink);
$location.replace();
if (!$scope.$$phase) $scope.$apply()
$scope.tabs[0].active = true;
$scope.selectedTab = $scope.tabs[0];
break;
}
else {
$location.path($scope.tabs[i + 1].refLink);
$location.replace();
if (!$scope.$$phase) $scope.$apply()
$scope.tabs[i + 1].active = true;
$scope.selectedTab = $scope.tabs[i + 1];
break;
}
}
}
}
$scope.startAutoNav = function () {
$rootScope.AutoNavActive = true;
$rootScope.autoNavInterval = $interval(function () {
$scope.nextTab();
}, teleAiDiagnosticsConfig.autoNavTimer);
}
$scope.stopAutoNav = function () {
$rootScope.AutoNavActive = false;
$interval.cancel($rootScope.autoNavInterval);
}
$scope.startAutoNav();
};
TabsController.$inject = injectParams;
angular.module('teleAiDiagnostics').controller('TabsController', TabsController);
}());
Hope this helps someone.

Related

AngularJS - moving Material mdDialog to service

I'm trying to cleanup code of one of my controllers which became too big. First I have decided to move attendee registration, which uses AngularJS Material mdDialog, to the service.
Original (and working) controller code looks like:
myApp.controller('RegistrationController', ['$scope','$routeParams','$rootScope','$location','$filter','$mdDialog', function($scope, $routeParams, $rootScope, $location, $filter, $mdDialog){
var attendee = this;
attendees = [];
...
$scope.addAttendee = function(ev) {
$mdDialog.show({
controller: DialogController,
templateUrl: 'views/regForm.tmpl.html',
parent: angular.element(document.body),
targetEvent: ev,
clickOutsideToClose:true,
controllerAs: 'attendee',
fullscreen: $scope.customFullscreen, // Only for -xs, -sm breakpoints.
locals: {parent: $scope}
})
.then(function(response){
attendees.push(response);
console.log(attendees);
console.log(attendees.length);
})
};
function DialogController($scope, $mdDialog) {
var attendee = this;
$scope.hide = function() {
$mdDialog.hide();
};
$scope.cancel = function() {
$mdDialog.cancel();
};
$scope.save = function(response) {
$mdDialog.hide(response);
};
}
}]);
and the code for the controller after separation:
myApp.controller('RegistrationController', ['$scope','$routeParams','$rootScope','$location','$filter','$mdDialog','Attendees', function($scope, $routeParams, $rootScope, $location, $filter, $mdDialog, Attendees){
...
$scope.attendees= Attendees.list();
$scope.addAttendee = function (ev) {
Attendees.add(ev);
}
$scope.deleteAttendee = function (id) {
Attendees.delete(id);
}
}]);
New service code looks like:
myApp.service('Attendees', ['$mdDialog', function ($mdDialog) {
//to create unique attendee id
var uid = 1;
//attendees array to hold list of all attendees
var attendees = [{
id: 0,
firstName: "",
lastName: "",
email: "",
phone: ""
}];
//add method create a new attendee
this.add = function(ev) {
$mdDialog.show({
controller: DialogController,
templateUrl: 'views/regForm.tmpl.html',
parent: angular.element(document.body),
targetEvent: ev,
clickOutsideToClose:true,
controllerAs: 'attendee',
fullscreen: this.customFullscreen, // Only for -xs, -sm breakpoints.
//locals: {parent: $scope}
})
.then(function(response){
attendees.push(response);
console.log(attendees);
console.log(attendees.length);
})
};
//simply search attendees list for given id
//and returns the attendee object if found
this.get = function (id) {
for (i in attendees) {
if (attendees[i].id == id) {
return attendees[i];
}
}
}
//iterate through attendees list and delete
//attendee if found
this.delete = function (id) {
for (i in attendees) {
if (attendees[i].id == id) {
attendees.splice(i, 1);
}
}
}
//simply returns the attendees list
this.list = function () {
return attendees;
}
function DialogController($mdDialog) {
this.hide = function() {
$mdDialog.hide();
};
this.cancel = function() {
$mdDialog.cancel();
};
this.save = function(response) {
$mdDialog.hide(response);
};
}
}]);
but I'm not able to "save" from the spawned mdDialog box which uses ng-click=save(attendee) neither close the dialog box.
What I'm doing wrong?
I'm not able to "save" from the spawned mdDialog box which uses ng-click=save(attendee) neither close the dialog box.
When instantiating a controller with "controllerAs" syntax, use the name with which it is instantiated:
<button ng-click="ctrl.save(ctrl.attendee)">Save</button>
this.add = function(ev) {
$mdDialog.show({
controller: DialogController,
templateUrl: 'views/regForm.tmpl.html',
parent: angular.element(document.body),
targetEvent: ev,
clickOutsideToClose:true,
controllerAs: ̶'̶a̶t̶t̶e̶n̶d̶e̶e̶'̶ 'ctrl',
fullscreen: this.customFullscreen, // Only for -xs, -sm breakpoints.
//locals: {parent: $scope}
})
.then(function(response){
attendees.push(response);
console.log(attendees);
console.log(attendees.length);
return response;
});
To avoid confusion, choose a controller instance name that is different from the data names.
$scope is not available to be injected into a service when it's created. You need to refactor the service and its methods so that you don't inject $scope into it and instead pass the current scope to the service methods when you call them.
I actually have a notification module that I inject which uses $mdDialog. Below is the code for it. Perhaps it will help.
(() => {
"use strict";
class notification {
constructor($mdToast, $mdDialog, $state) {
/* #ngInject */
this.toast = $mdToast
this.dialog = $mdDialog
this.state = $state
/* properties */
this.transitioning = false
this.working = false
}
openHelp() {
this.showAlert({
"title": "Help",
"textContent": `Help is on the way for ${this.state.current.name}!`,
"ok": "OK"
})
}
showAlert(options) {
if (angular.isString(options)) {
var text = angular.copy(options)
options = {}
options.textContent = text
options.title = " "
}
if (!options.ok) {
options.ok = "OK"
}
if (!options.clickOutsideToClose) {
options.clickOutsideToClose = true
}
if (!options.ariaLabel) {
options.ariaLabel = 'Alert'
}
if (!options.title) {
options.title = "Alert"
}
return this.dialog.show(this.dialog.alert(options))
}
showConfirm(options) {
if (angular.isString(options)) {
var text = angular.copy(options)
options = {}
options.textContent = text
options.title = " "
}
if (!options.ok) {
options.ok = "OK"
}
if (!options.cancel) {
options.cancel = "Cancel"
}
if (!options.clickOutsideToClose) {
options.clickOutsideToClose = false
}
if (!options.ariaLabel) {
options.ariaLabel = 'Confirm'
}
if (!options.title) {
options.title = "Confirm"
}
return this.dialog.show(this.dialog.confirm(options))
}
showToast(toastMessage, position) {
if (!position) { position = 'top' }
return this.toast.show(this.toast.simple()
.content(toastMessage)
.position(position)
.action('OK'))
}
showYesNo(options) {
options.ok = "Yes"
options.cancel = "No"
return this.showConfirm(options)
}
uc() {
return this.showAlert({
htmlContent: "<img src='img\\underconstruction.jpg'>",
ok: "OK",
title: "Under Construction"
})
}
}
notification.$inject = ['$mdToast', '$mdDialog', '$state']
angular.module('NOTIFICATION', []).factory("notification", notification)
})()
Then inject notification (lower case) into my controllers in order to utilize it. I store notification in a property of the controller and then call it with something like this:
this.notification.showYesNo({
clickOutsideToClose: true,
title: 'Delete Customer Setup?',
htmlContent: `Are you sure you want to Permanently Delete the Customer Setup for ${custLabel}?`,
ariaLabel: 'Delete Dialog'
}).then(() => { ...
this.notification.working = true
this.ezo.EDI_CUSTOMER.remove(this.currentId).then(() => {
this.primaryTab = 0
this.removeCustomerFromList(this.currentId)
this.currentId = 0
this.notification.working = false
}, error => {
this.notification.working = false
this.notification.showAlert({
title: "Error",
htmlContent: error
})
})

Error: [ng:areq] Argument 'AddInstallationCtrl' is not a function, got Object

i cant find my fail :(
can help me?
in my project i have:
controller principal
define([
"./directives/subscribe",
"./controllers/subscribe",
"./controllers/script",
"angular",
"../models/app.models",
"../login/app.login",
], function (directives, controllers) {
var module = angular.module("app.matrix", ["app.models", "app.login"]).config(['$routeProvider', function ($routeProvder) {
$routeProvder
.when('/matrix', {
templateUrl: 'modules/matrix/templates/main.html',
controller: 'MatrixCtrl'
})
.when('/modals', {
templateUrl: 'modules/matrix/templates/index.html',
controller: 'AddInstallationCtrl'
});
}]);
module = directives.subscribe(module);
return controllers.subscribe(module);
(function () {
"use strict";
var app = angular.module('myApp', [
'ap.lateralSlideMenu',
]);
// service
app.service('number', function () {
return {
isPositive: function (operationPrice) {
return String(operationPrice).indexOf("-") == -1;
}
};
});
})
();
});
controller Secondary
define([
"./matrix",
"./sidebar",
"./script"
], function (matrix, sidebar, script) {
return {
subscribe: function (app) {
app.controller('MatrixCtrl', matrix).controller('SidebarCtrl', sidebar);
app.controller('AddInstallationCtrl', script);
app.directive('sidebarDirective', function () {
return {
link: function (scope, element, attr) {
scope.$watch(attr.sidebarDirective, function (newVal) {
if (newVal) {
element.addClass('show');
return;
}
element.removeClass('show');
});
}
};
});
return app;
}
};
});
Controller Finaly
define(["angular"], function () {
// Code goes here
var app = angular.module('myApp', ['ngAnimate', 'ngSanitize']);
app.config([
'$compileProvider',
function ($compileProvider) {
$compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|chrome-extension):/);
// Angular before v1.2 uses $compileProvider.urlSanitizationWhitelist(...)
}
]);
app.directive('modalDialog', function ($window, $templateCache, $compile, $http) {
return {
restrict: 'EA',
scope: {
show: '=',
modalUser: '=',
saveUser: '&',
templateUser: '#'
},
replace: true, // Replace with the template below
//transclude: true, // we want to insert custom content inside the directive
link: function (scope, element, attrs) {
$http.get(scope.templateUser, { cache: $templateCache }).success(function (tplContent) {
element.replaceWith($compile(tplContent)(scope));
});
scope.dialogStyle = {};
if (attrs.width) {
scope.dialogStyle.width = attrs.width + '%';
scope.dialogStyle.left = ((100 - attrs.width) / 2) + '%';
}
if (attrs.height) {
scope.dialogStyle.height = attrs.height + '%';
scope.dialogStyle.top = ((100 - attrs.height) / 2) + '%';
}
scope.hideModal = function () {
scope.show = false;
};
scope.clone = function (obj) {
if (obj === null || typeof obj !== 'object') {
return obj;
}
var temp = obj.constructor(); // give temp the original obj's constructor
for (var key in obj) {
temp[key] = scope.clone(obj[key]);
}
return temp;
};
var tempUser = scope.clone(scope.modalUser);
scope.save = function () {
scope.saveUser(scope.modalUser);
scope.show = false;
};
scope.cancel = function () {
scope.modalUser = scope.clone(tempUser);
scope.show = false;
};
}
//template: "<div class='ng-modal' ng-show='show'><div class='ng-modal-overlay'></div><div class='ng-modal-dialog' ng-style='dialogStyle'><div class='ng-modal-close' ng-click='hideModal()'>X</div><div class='ng-modal-dialog-content' ng-transclude></div></div></div>"
//templateUrl: 'my-customer.html'
//templateUrl: scope.templateUser
};
});
app.controller('AddInstallationCtrl', function ($scope, $window) {
$scope.modalShown = false;
$scope.modalShown2 = false;
$scope.user = { name: "Mara", surname: "Sanchez", shortKey: "1111" };
$scope.userMod = {};
$scope.toggleModal = function () {
$scope.modalShown = !$scope.modalShown;
};
$scope.toggleModal2 = function () {
$scope.modalShown2 = !$scope.modalShown2;
};
$scope.saveUser = function (usr) {
$scope.userMod = usr;
$window.alert('Desde metodo SALVAR del controller fuera de la ventana: ' + $scope.userMod.shortKey);
}
});
});
With the first controller I declare that the url: modules / matrix / templates / index.html
It is managed by the AddInstallationCtrl driver
With the second controller I declare that
The application has the AddInstallationCtrl driver
And that the controller is in the variable script
App.controller ('AddInstallationCtrl', script);
In the final controller I declare all the functions that I want to execute the letter
Even there is right, right?
Because when you entered modules / matrix / templates / index.html
Does it tell me that AddInstallationCtrl is not a controller?

Angular js directive call with controller data binding

View
<star-rating ratingValue="ratings" readonly="true"></star-rating>
<div><strong>Rating 1:</strong>{{ratings}}</div>
Controller
app.controller('ProductCtrl', function ($scope, $http, $ionicSlideBoxDelegate, $resource, $state, $stateParams, $rootScope, $compile, $ionicPopup, $location, $sce) {
$scope.ratings = 0;
this.isReadonly = true;
this.rateFunction = function(rating) {
console.log('Rating selected: ' + rating);
};
$http.defaults.useXDomain = true;
$http.get(web_service + 'product/get', {
params: {id: $stateParams.ProductId},
headers: {}
}).success(function (response) {
$scope.product = response.product;
console.log(response.product);
$ionicSlideBoxDelegate.update();
$scope.ratings = response.product.rating;
this.rateFunction = function(rating) {
console.log('Rating selected: ' + rating);
};
})
.error(function (err) {
alert("ERROR");
});
}).directive('starRating', starRating);
Directive
function starRating() {
return {
restrict: 'EA',
template:
'<ul class="star-rating" ng-class="{readonly: readonly}">' +
' <li ng-repeat="star in stars" class="star" ng-class="{filled: star.filled}" ng-click="toggle($index)">' +
' <i class="ion-ios-star"></i>' + // or &#9733
' </li>' +
'</ul>',
scope: {
ratingValue: '=?',
max: '=?', // optional (default is 5)
onRatingSelect: '&?',
readonly: '=?'
},
link: function(scope, element, attributes) {
if (scope.max == undefined) {
scope.max = 5;
}
scope.$observe('ratingValue', function(value){
console.log(value);
//$scope.nav.selection = value
});
function updateStars() {
scope.stars = [];
for (var i = 0; i < scope.max; i++) {
scope.stars.push({
filled: i < scope.ratingValue
});
}
};
scope.toggle = function(index) {
if (scope.readonly == undefined || scope.readonly === false){
scope.ratingValue = index + 1;
scope.onRatingSelect({
rating: index + 1
});
}
};
scope.$watch('ratingValue', function(oldValue, newValue) {
if (newValue) {
updateStars();
}
});
}
};
}
When initial value of $scope.ratings is number like 1,2,3 then starts prints but the value retrieved by ajax request is not getting added to directive and in directive values shows "undefined" and no starts getting printed.
The tag below directive in view code gives retrieved value referring to this Codepen: http://codepen.io/TepigMC/pen/FIdHb
What I am missing in directive?
use ng-if so that the directive is called after you have $scope.ratings.
<star-rating ng-if="ratings" ratingValue="ratings" readonly="true"></star-rating>

Angular Service or Root Function

I want to re-organize my code so that I can share a dialog page with different controllers (ie. each controller opens the same dialog)
Should my dialog be a service or a directive... I'm not sure how to make it available to the controllers and have access to scopes
Something like this:
app.controller('PropertiesCtrl', function($scope,$rootScope,$http, DTOptionsBuilder, DTColumnBuilder, apiserv, $filter, $mdDialog, $mdMedia){
$scope.getProperties = function(){
$scope.data=[];
//var url = 'http://www.filltext.com/?rows=10&fname={firstName}&lname={lastName}&delay=3&callback=JSON_CALLBACK';
var data = "?db="+ $rootScope.globals.currentUser.agents[$rootScope.globals.currentDB].db_name;
var url = apiserv+"api.properties.test.php"+data;
$http.jsonp(url).success(function(data){
$scope.data=data;
});
};
var vm = this;
function stateChange(iColumn, bVisible) {
console.log('The column', iColumn, ' has changed its status to', bVisible);
}
// TO-DO: Rather load from a Factory promise, like here: https://github.com/l-lin/angular-datatables/issues/14
// Example here: http://embed.plnkr.co/B9ltNzIRCwswgHqKD5Pp/preview
var data = "?db="+ $rootScope.globals.currentUser.agents[$rootScope.globals.currentDB].db_name;
var url = apiserv+"api.properties.test.php"+data;
vm.dtOptions = DTOptionsBuilder.fromSource(url)
.withBootstrap()
// Active Buttons extension
.withButtons([
//'columnsToggle',
'colvis',
'copy',
'print',
'excel'
])
.withOption('fnRowCallback',myCallback)
.withOption('order', [[ 3, "desc" ]])
.withOption('stateSave',true);
vm.dtColumns = [
DTColumnBuilder.newColumn('File_Num').withTitle('File'),
DTColumnBuilder.newColumn('Description').withTitle('Description'),
DTColumnBuilder.newColumn('street').withTitle('Street'),
DTColumnBuilder.newColumn('bedrooms').withTitle('Bed'),
DTColumnBuilder.newColumn('bathrooms').withTitle('Bath'),
DTColumnBuilder.newColumn('garages').withTitle('Garages'),
DTColumnBuilder.newColumn('car_port').withTitle('Car Ports').notVisible(),
DTColumnBuilder.newColumn('Current_Monthly_Rental').withTitle('Rental').renderWith(function(data, type, full) {
return $filter('currency')(data, 'R ', 2); //could use currency/date or any angular filter
})
];
})
.controller('PagesListCtrl', function($scope,$rootScope,$http, DTOptionsBuilder, DTColumnBuilder, apiserv, $filter, $mdDialog, $mdMedia){
$scope.page = {
title: 'Dashboard',
subtitle: 'Place subtitle here...'
};
});
function myCallback(nRow, aData, iDisplayIndex, iDisplayIndexFull) {
$('td', nRow).bind('click', function() {
//$scope.$apply(function() {
showTabDialog(aData);
//});
});
return nRow;
};
function showTabDialog(ev) {
console.log(ev.file_id);
var data = "?db="+ $rootScope.globals.currentUser.agents[$rootScope.globals.currentDB].db_name+"&file="+ev.file_id;
var url = apiserv+"api.properties.view.php"+data;
console.log(url);
$http({url:url}).then(function(rs){
console.log(rs.data[0]);
$scope.propdata=rs.data[0];
$mdDialog.show({
controller: DialogController,
templateUrl: 'pages/properties/tabDialog.tmpl.html',
//parent: angular.element(document.body),
targetEvent: ev,
//onComplete: afterShowAnimation,
//scope:$scope,
//preserveScope: true
locals: { propdata: $scope.propdata }
})
.then(function(propdata) {
console.log(propdata);
//$scope.$parent.propdata = propdata; // Still old data
//vm.sname = propdata.street;
});
}, function(rs){
console.log("error : "+rs.data+" status : "+rs.status);
});
};
function DialogController($scope, $mdDialog, propdata) {
$scope.propdata = propdata;
$scope.form_size = "form-group-sm";
$scope.font_size = "font-size-sm";
$scope.hide = function(ret) {
//$scope.$apply(); Throws an error
$mdDialog.hide(ret);
}
$scope.changesize = function() {
var fsize = $scope.form_size.split("-").pop(); // "form-group-xs";
switch(fsize) {
case "sm" : fsize = "md";
break;
case "md" : fsize = "lg";
break;
case "lg" : fsize = "sm";
break;
}
$scope.form_size = "form-group-" + fsize;
$scope.font_size = "font-size-" + fsize;
}
}
This is how I had it and it worked, but as you can see the dialog is nested inside the controller and only usefull to that controller:
app
.controller('PropertiesCtrl', function($scope,$rootScope,$http, DTOptionsBuilder, DTColumnBuilder, apiserv, $filter, $mdDialog, $mdMedia){
$scope.page = {
title: 'Dashboard',
subtitle: 'Place subtitle here...'
};
$scope.getProperties = function(){
$scope.data=[];
//var url = 'http://www.filltext.com/?rows=10&fname={firstName}&lname={lastName}&delay=3&callback=JSON_CALLBACK';
var data = "?db="+ $rootScope.globals.currentUser.agents[$rootScope.globals.currentDB].db_name;
var url = apiserv+"api.properties.test.php"+data;
$http.jsonp(url).success(function(data){
$scope.data=data;
});
};
var vm = this;
function stateChange(iColumn, bVisible) {
console.log('The column', iColumn, ' has changed its status to', bVisible);
}
// TO-DO: Rather load from a Factory promise, like here: https://github.com/l-lin/angular-datatables/issues/14
// Example here: http://embed.plnkr.co/B9ltNzIRCwswgHqKD5Pp/preview
var data = "?db="+ $rootScope.globals.currentUser.agents[$rootScope.globals.currentDB].db_name;
var url = apiserv+"api.properties.test.php"+data;
vm.dtOptions = DTOptionsBuilder.fromSource(url)
.withBootstrap()
// Active Buttons extension
.withButtons([
//'columnsToggle',
'colvis',
'copy',
'print',
'excel'
])
.withOption('fnRowCallback',$scope.myCallback)
.withOption('order', [[ 3, "desc" ]])
.withOption('stateSave',true);
vm.dtColumns = [
DTColumnBuilder.newColumn('File_Num').withTitle('File'),
DTColumnBuilder.newColumn('Description').withTitle('Description'),
DTColumnBuilder.newColumn('street').withTitle('Street'),
DTColumnBuilder.newColumn('bedrooms').withTitle('Bed'),
DTColumnBuilder.newColumn('bathrooms').withTitle('Bath'),
DTColumnBuilder.newColumn('garages').withTitle('Garages'),
DTColumnBuilder.newColumn('car_port').withTitle('Car Ports').notVisible(),
DTColumnBuilder.newColumn('Current_Monthly_Rental').withTitle('Rental').renderWith(function(data, type, full) {
return $filter('currency')(data, 'R ', 2); //could use currency/date or any angular filter
})
/*DTColumnBuilder.newColumn('file_id').withTitle('').withClass('dt-right').renderWith(function(data, type, full) {
//return "<a href='#/app/statement/"+full.id+"' class='btn btn-default'>View</a>";
return "<a class=\"btn btn-default\" onclick='$(\"#myview\").click()'>View</a>";
}).notSortable()*/
];
$scope.showTabDialog = function(ev) {
console.log(ev.file_id);
var data = "?db="+ $rootScope.globals.currentUser.agents[$rootScope.globals.currentDB].db_name+"&file="+ev.file_id;
var url = apiserv+"api.properties.view.php"+data;
console.log(url);
$http({url:url}).then(function(rs){
console.log(rs.data[0]);
$scope.propdata=rs.data[0];
$mdDialog.show({
controller: DialogController,
templateUrl: 'pages/properties/tabDialog.tmpl.html',
//parent: angular.element(document.body),
targetEvent: ev,
//onComplete: afterShowAnimation,
//scope:$scope,
//preserveScope: true
locals: { propdata: $scope.propdata }
})
.then(function(propdata) {
console.log(propdata);
//$scope.$parent.propdata = propdata; // Still old data
//vm.sname = propdata.street;
});
}, function(rs){
console.log("error : "+rs.data+" status : "+rs.status);
});
};
function DialogController($scope, $mdDialog, propdata) {
$scope.propdata = propdata;
$scope.form_size = "form-group-sm";
$scope.font_size = "font-size-sm";
$scope.hide = function(ret) {
//$scope.$apply(); Throws an error
$mdDialog.hide(ret);
}
$scope.changesize = function() {
var fsize = $scope.form_size.split("-").pop(); // "form-group-xs";
switch(fsize) {
case "sm" : fsize = "md";
break;
case "md" : fsize = "lg";
break;
case "lg" : fsize = "sm";
break;
}
$scope.form_size = "form-group-" + fsize;
$scope.font_size = "font-size-" + fsize;
}
}
$scope.myCallback = function(nRow, aData, iDisplayIndex, iDisplayIndexFull) {
$('td', nRow).bind('click', function() {
$scope.$apply(function() {
$scope.showTabDialog(aData);
});
});
return nRow;
};
});
Here is factory to store and retrieve user from any controller:
app.factory('Auth', [, function () {
function getUser() {
return user;
}
var currentUser = getUser();
return {
currentUser: currentUser
}]);
And use it:
app.controller('controller', ['Auth', function(Auth) {
var currentUser = Auth.currentUser;
}]);
And don't forget to include new factory on page before controller.

Factory property not updating in controller

I have a property in my factory and a sync() method to update it, but when accessing from controller the value never changes.
My router: (Initialise events with resolve)
'use strict';
myApp
.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/');
$stateProvider
.state('app', {
abstract: true,
views: {
'header': {
controller: 'HeaderCtrl',
templateUrl: 'views/header/_header.html'
},
'footer': {
templateUrl: 'views/footer/_footer.html'
}
}
})
.state('app.events', {
url: '^/',
views: {
'content#': {
controller: 'EventsCtrl',
templateUrl: 'views/events/_events.html',
resolve: {
postPromise: ['EventsServ', function(EventsServ) {
return EventsServ.getAll();
}]
}
}
}
})
.state('app.about', {
url: '^/sobre',
views: {
'content#': {
templateUrl: 'views/about/_about.html'
}
}
})
.state('app.contact', {
url: '^/contato',
views: {
'content#': {
controller: 'ContactCtrl',
templateUrl: 'views/contact/_contact.html'
}
}
});
}]);
My controller: (Now sync with facebook and update events)
'use strict';
myApp
.controller('EventsCtrl', ['$scope', 'EventsServ', '$filter', '$window', 'Facebook', 'postPromise', '$log', '$timeout', function($scope, EventsServ, $filter, $window, Facebook, postPromise, $log, $timeout) {
$scope.alerts = [];
$scope.events = EventsServ.all(); // <<<< Never update, keep the same value obtained in router.
$scope.periods = [{
label: 'HOJE',
labelColor: 'success',
startTime: $filter('clearTime')(new Date()),
endTime: $filter('clearTime')(new Date()),
events: []
}, {
label: 'PRÓXIMOS DIAS',
labelColor: 'primary',
startTime: $filter('clearTime')($filter('addDay')(new Date(), 1)),
endTime: $filter('clearTime')($filter('addDay')(new Date(), 8)),
events: []
}, {
label: 'PRÓXIMAS SEMANAS',
labelColor: 'warning',
startTime: $filter('clearTime')($filter('addDay')(new Date(), 9)),
events: []
}];
if (postPromise.status >= 200 && postPromise.status < 300) {
angular.forEach($scope.events, (function(event) {
var eventDate = $filter('clearTime')(event.start_time);
angular.forEach($scope.periods, (function(period) {
if (eventDate >= period.startTime && (eventDate <= period.endTime || !period.endTime)) {
period.events.push(event);
}
}));
}));
Facebook.getLoginStatus(function(response) {
if(response.status === 'connected') {
EventsServ.sync();
$log.info($scope.events);
} else {
$log.info('notLoggedIn');
}
});
} else {
$scope.alerts.push({
type: 'danger',
msg: 'Ooops! Não foi possível buscar os eventos.'
});
}
$scope.openEvent = function(event) {
$window.open('http://facebook.com/events/' + event.id, '_blank');
};
$scope.closeAlert = function(index) {
$scope.alerts.splice(index, 1);
};
}])
.filter('addDay', function() {
return function(date, days) {
return new Date(date.setDate(date.getDate() + days));
};
})
.filter('clearTime', function() {
return function(date) {
var d = new Date(date);
d.setHours(0,0,0,0);
return d;
};
});
I build my view looping through $scope.events.
My factory:
'use strict';
myApp
.factory('EventsServ', ['$http', '$q', 'Facebook', 'PagesServ', '$log', function($http, $q, Facebook, PagesServ, $log) {
var factory = {
events: []
};
factory.all = function() {
return factory.events;
};
factory.getAll = function() {
return $http.get('/events.json')
.success(function(response) {
angular.copy(response, factory.events);
})
.error(function(response) {
$log.error('Failed to get events.');
});
};
factory.sync = function() {
PagesServ.getAll().then(function(response) {
var pages = PagesServ.pages;
var events = [];
var pagesPromises = [];
var eventsPromises = [];
angular.forEach(pages, function(page, key) {
pagesPromises.push(Facebook.api(page.facebook_id, function(response) {
page.facebook_id = response.id;
page.name = response.name;
page.image = 'image.jpg';
delete page.events;
delete page.created_at;
delete page.updated_at;
}));
eventsPromises.push(Facebook.api(page.facebook_id + '/events', function(response) {
var pageEvents = response.data;
angular.forEach(pageEvents, function(event, key) {
event.page = page;
});
events = events.concat(pageEvents);
}));
});
$q.all(pagesPromises).then(function() {
$log.info(pages);
}, function(reason) {
$log.error('Failed to syncs pages: ' + reason);
});
$q.all(eventsPromises).then(function() {
factory.events = [];
angular.forEach(events, function(event, key) {
var eventSynced = {
facebook_id: event.id,
image: '',
name: event.name,
description: event.description,
start_time: event.start_time,
end_time: event.end_time,
going: 397,
address: '...',
page: event.page
}
factory.events.push(eventSynced);
});
$log.info(factory.events); // <<<<< Here the new value is correct!
}, function(reason) {
$log.error('Failed to syncs events: ' + reason);
});
});
};
return factory;
}]);
The new value of the property inside factory is correct when printing to console, but in controller the value never changes, is always [ ].
When have data I want to stay in sync between a controller and a factory, or a factory and multiple controllers, I set the data item I want to access in my controller to the object or array in the factory:
// in your controller change this
$scope.events = EventsServ.all();
// to use the object
$scope.events = EventsServ.events;
In your sync function where you are iterating over the events, push each one into a temporary array, then use angular.copy to make the update to the array available to the controller.
// at the line right before you log the events in the factory use
angular.copy(tempArray, factory.events);
$log.info(factory.events);
More info: http://www.justinobney.com/keeping-angular-service-list-data-in-sync-among-multiple-controllers/

Resources