var myappWebApp = angular.module('myappWebApp', ['ui.bootstrap']);
//factory
myappWebApp.factory('wired', function () {
this.currOp = false;
return {
currOp1 : this.currOp
}
});
// controller
myappWebApp.controller('wiredCtrl',
function ($scope, $http, wired) {
//data
$scope.currOp = wired.currOp;//why is this undefined?
$scope.currOpInText = wired.currOpInText();
$scope.altOpInText = null;
$scope.boxA = null;
....
How should my scope.currOp always automatically have the same value as wired.currOp?
If you are copying just a bool, you can't. Booleans are copied by value, so you don't get reference-based updates.
You might consider using an object with a boolean:
myappWebApp.factory('wired', function () {
this.state = { curOp: false };
return {
state: this.state
}
});
Then, when you reference it in your scope, you can do this:
myappWebApp.controller('wiredCtrl',
function ($scope, $http, wired) {
$scope.opState = wired.state;
});
And now when curOp changes, the controller will have the change. You can watch for changes:
$scope.$watch("opState.curOp", function(newVal, oldVal) {
// Handle changes in curOp
});
Or you can bind to it:
CurOp: {{state.currOp}}
Note: You asked: "why is this undefined?" The answer is because your service is exposing currOp1 but you are referencing currOp
Related
I have the next 'problem' with Angular 1.
I have this Factory that I use to get the data for the current logged user:
angular.module('myModule')
.factory('authFactory', function ($http, $rootScope, Session, api, backend_url) {
var authFactory = this;
var user = {};
authFactory.init = function(){
// This API returns the information of the current user
api.current_user.get({}).$promise.then(function(res){
user = res;
});
}
// I use this function to return the user
authFactory.user = function () {
return user;
};
}
This is a basic Controller example where I'm trying to access the information retrieved by the above factory:
angular.module('myModule.mypage')
.controller('PageCtrl', function ($scope, authFactory) {
$scope.user = authFactory.user();
authFactory.init();
angular.element(document).ready(function () {
// This will return {} because it's called
// before the factory updates user value
console.log(authFactory.user());
console.log($scope.user);
});
});
The problem is that $scope.user = myFactory.user(); is not being updated once the Factory retrieve the user value.
I think my issue is related with myFactory.user();. I'm using a function, so the value returned by the function is not updated after myFactory.user has changed, I think that's why on PageCtrl the variable $scope.user is not getting any value.
My questions are:
Which is the best approach on my controller to wait until the user info is loaded by authFactory ?
Should I use a service instead ?
Problem with your implementation is that user is being initialized when authFactory.init() is invoked using presumably asynchronous API.
I would suggest you to return promise from authFactory.user method.
angular.module('myModule')
.factory('authFactory', function ($http, $rootScope, Session, api, $q, backend_url) {
var authFactory = this;
var user = {};
authFactory.init = function () {
// This API returns the information of the current user
return api.current_user.get({}).$promise.then(function (res) {
user = res;
});
}
//Return promise from the method
authFactory.user = function () {
var deferred = $q.defer();
if (angular.isDefined(user)) {
deferred.resolve(user);
} else {
authFactory.init().then(function () {
deferred.resolve(user);
});
}
return deferred.promise;
};
});
Then modify controller
angular.module('myModule.mypage')
.controller('PageCtrl', function ($scope, authFactory) {
authFactory.user().then(function (user) {
$scope.user = user;
})
});
angular.module('myModule')
.factory('authFactory', function ($http, $rootScope, Session, api, backend_url) {
var authFactory = this;
authFactory.user = {}
// I use this function to return the user
authFactory.getUser() = function () {
return api.current_user.get({}).$promise.then(function(res){
authFactory.user = res;
});
};
}
angular.module('myModule.mypage')
.controller('PageCtrl', function ($scope, authFactory) {
authFactory.getUser().then(function() {
$scope.user = authFactory.user;
});
});
Provide us a JSFiddle, I tried to help you without any testing environment.
I have a really serious problem, I'm updating, editing, deleting data, and the two-way data binding is not working.
This is one of my controllers:
'use strict';
var EventController = function($timeout, $scope, $state, EventModel) {
this.$timeout = $timeout;
this.$scope = $scope;
this.$state = $state;
this.EventModel = EventModel;
/**
* When the page is requested, retrieve all the data.
*
*/
this.retrieve();
};
EventController.prototype = {
create: function(event) {
var that = this;
this.EventModel.Model.insert(event)
.then(function() {
that.refresh();
});
},
retrieve: function() {
var that = this;
this.EventModel.Model.find()
.then(function(result) {
that.$scope.events = result;
});
},
one: function(id) {
var that = this;
this.EventModel.Model.one(id)
.then(function(result) {
that.$scope.event = result;
});
},
update: function(id, event, state) {
if (state !== undefined) {
event.is_active = state;
}
var that = this;
this.EventModel.Model.update(id, event)
.then(function() {
that.refresh();
});
},
delete: function(id) {
var check = $('[data-controller-input]:checked');
var that = this;
$.each(check, function() {
var target = $(this);
var id = target.prop('id');
that.EventModel.Model.remove(id)
.then(function() {
that.refresh();
});
});
},
clear: function() {
this.$scope.event = angular.copy(this.$scope.initial);
},
refresh: function() {
this.$state.go(this.$state.current, {}, {reload: true});
}
};
angular
.module('adminApp')
.controller('EventController',
[
'$timeout',
'$scope',
'$state',
'EventModel',
EventController
]
);
In the create, update and delete methods I need to update the HTML without refreshing the page, I already tried using, $scope.apply, $scope.digest, $timeout after the result came, but not happens in the HTML.
If I try $scope.apply and $scope.digest the error will be:
Prevent error $digest already in progress when calling $scope.$apply()
So I was trying to wrap the $scope.$apply or $digest with the $timeout, same result, nothing happens.
Thanks.
First of all, your refresh method will never update your controller.it will simply fail just because this.$state.current won't be able to resolve any url ,template or controller.
And this is the main reason you are not able to see updated data ,just check your console you might be getting Error: Cannot transition to abstract state '[object Object]' error.
Update : I have create a plnkr.as i don't have access to event model code i simply removed it and try to create the same scenario.
http://plnkr.co/edit/RsI3TgKwcjGEXcTMKoQR?p=preview
see if this can help you
I am not sure, but try using the following function which checks the current phase before executing your function. It may solve the issue.
$scope.safeApply = function(fn) {
var phase = this.$root.$$phase;
if(phase == '$apply' || phase == '$digest') {
if(fn && (typeof(fn) === 'function')) {
fn();
}
} else {
this.$apply(fn);
}
};
Usage:
$scope.safeApply(function() {
//Your lines
});
I want to call a function or change the value of variable/s which is there inside another controller. I looked online for the solution and understood that I have to create a service and use it inside both the controller, however I am not able to understand that how service will have access to $scope.home_main and $scope.home_main variables as they are in different scope.
JS
app.controller('Controller1', function ($scope, $window) {
$scope.toggle = function() {
$scope.home_main = !$scope.home_main;
$scope.full_page_place_holder = !$scope.full_page_place_holder;
};
});
app.controller('Controller2', function ($scope, $window) {
$scope.onTabSelect=function(){
// here I want to call toggle function which is inside another controller.
};
});
Updated HTML
<div ng-controller="Controller1">
<div ng-hide="home_main"></div>
</div>
<div ng-controller="Controller2">
<div ng-hide="full_page_place_holder"></div>
</div>
Looked here: SO-Q1, SO-Q2. Thanks.
you can create a service as follows,
angular.module('someApp').service('shareDataService', function() {
var popup;
var setDetails = function(param) {
popup = param;
};
var getDetails = function() {
return popup;
};
return {
setDetails: setDetails,
getDetails: getDetails,
};
});
This service will not have access to the $scope variables of the two controllers, instead you can call getDetails and setDetails to get and set the variable in the service.
suppose you want to send the value of 'home_main' from controller1 to controller2, in controller1, you call the service function setDetails
app.controller('Controller1', function ($scope, $window, shareDataService) {
$scope.toggle = function() {
$scope.home_main = !$scope.home_main;
$scope.full_page_place_holder = !$scope.full_page_place_holder;
shareDataService.setDetails($scope.home_main);
};
});
and in controller2, you get the value by calling the service
app.controller('Controller2', function ($scope, $window) {
var home_main_value = shareDataService.getDetails();
});
You can do a similar thing for functions also.....
Hope it helps
You misunderstood the concept service will have a single variable that is going to be shared by two controllers:-
$scope is local for controller and cannot accessed by another controller:-
FOR Example:-
myApp.factory('Data', function () {
var data = {
home_main : ''
};
return {
gethome_main : function () {
return data.home_main ;
},
sethome_main : function (home_main ) {
data.home_main = home_main ;
}
};
myApp.controller('FirstCtrl', function ($scope, Data) {
$scope.home_main= '';
$scope.$watch('home_main', function (newValue, oldValue) {
if (newValue !== oldValue) Data.sethome_main(newValue);
});
});
myApp.controller('SecondCtrl', function ($scope, Data) {
$scope.$watch(function () { return Data.gethome_main(); }, function (newValue, oldValue) {
if (newValue !== oldValue) $scope.home_main= newValue;
});
});
I'm trying to create resusable alert service, which I would call anywhere in my application with just:
alertService.showAlert('warning', 'something went wrong!');
For example after ajax call to backend api.
Right now I'm using a factory and a directive, but It seems I'm doing something wrong, because the directive does not update after a call to showAlert method. Right now I have something like this:
var srv = angular.module('srv', []);
srv. factory('alertService', ['$timeout', function($timeout){
var alertService = this;
alertService.alertNeeded = false;
alertService.alertClass = '';
alertService.alertMessage = '';
alertService.setAlertNeeded = function(){
alertService.alertNeeded = true
};
alertService.setAlertClass = function(type){
if(type === 'warning')
alertService.alertClass = 'alert-warning';
if(type === 'success')
alertService.alertClass = 'alert-success';
if(type === 'info')
alertService.alertClass = 'alert-info';
if(type === 'danger')
alertService.alertClass = 'alert-danger';
};
alertService.setAlertMessage = function(message){
alertService.alertMessage = message;
};
return {
showAlert: function(class, msg){
alertService.setAlertNeeded();
alertService.setAlertClass(class);
alertService.setAlertMessage(msg);
}
};
}]).
directive('myAlerts', ['alertService', function(alertService){
return {
restrict: 'A',
template: '<div ng-class="alertClass" ng-show="alertNeeded">{{alertMessage}}</div>',
link: function(scope){
scope.alertNeeded = alertService.alertNeeded;
scope.alertMessage = alertService.alertMessage;
scope.alertClass = alertService.alertClass;
}
}
}]).
controller('alertShowingController', ['$scope', 'alertService', function($scope, alertService){
alertService.showAlert('warning', 'Warning alert!!!')
}]);
My code doesn't look exactly the same, but I just wanted to show what I'm trying to do: I want to call alertService.showAlert(...) from another controller in another module (which depends on srv module) and this way update the variables in myAlerts directive to show the proper alert.
The thing is after call to showAlert method The values are set, but within the directive code I'm getting alertService.alertNeeded as undefined.
I'm completely new to AngularJs, so maybe I'm getting something wrong, but I spent whole evening to make it work and I still have no idea what is the proper solution for this.
Please help!
Here is a pattern that I used once before
var srv = angular.module('srv', []);
srv.factory('alertService', ['$timeout', function($timeout){
var alertListeners = [];
this.register = function (listener) {
alertListeners.push(listener);
};
this.notifyAll = function (data) {
for (// each listener in array) {
var listenerObject = alertListeners[i];
try { // do not allow exceptions in individual listeners to corrupt other listener processing
listenerObject.notify(data);
} catch(e) {
console.log(e);
}
}
};
}]).
directive('myAlerts', ['alertService', function(alertService){
var alertDirectiveObserver = function($scope, alertService) {
this.notify = function(data) {
/*
* TO DO - use data to show alert
*/
};
alertService.register(this);
};
return {
restrict: 'A',
template: '<div ng-class="alertClass" ng-show="alertNeeded">{{alertMessage}}</div>',
controller: ['$scope', 'alertService', alertDirectiveObserver],
link: function(scope){
}
}
}]).
controller('alertShowingController', ['$scope', 'alertService', function($scope, alertService){
alertService.notifyAll({'warning', 'Warning alert!!!'})
]);
Of course you should also cleanup by registering a function to delete objects on scope destroy.
eg
element.on('$destroy', function() {
alertService.unregister(// some listener id);
});
Your code has two different meanings for alertService. Inside the factory definition, it refers to the factory itself. Everywhere else, it refers to the object returned by the factory. The easiest way to move forward would be to add a few missing methods to the object returned by the factory:
return {
showAlert: function(cssClass, msg){
alertService.setAlertNeeded();
alertService.setAlertClass(cssClass);
alertService.setAlertMessage(msg);
},
alertClass: function() { return alertService.alertClass; },
alertMessage: function() { return alertService.alertMessage; },
alertNeeded: function() { return alertService.alertNeeded; }
};
Then, change your directive's template so that it calls these functions on each digest cycle:
directive('myAlerts', ['alertService', function(alertService){
return {
restrict: 'A',
template: '<div ng-class="alertClass()"' +
' ng-show="alertNeeded()">' +
' {{alertMessage()}}' +
'</div>',
link: function(scope){
scope.alertNeeded = alertService.alertNeeded;
scope.alertMessage = alertService.alertMessage;
scope.alertClass = alertService.alertClass;
}
}
}])
Then you should see your warning message. Try it in a fiddle.
In AngularJS I know I can attach a listener to a variable like this:
$scope.$watch("variableName", listenerFunc);
I'm wondering whether it's possible to query a variable to find out which functions are already listening to it.
Specifically, I'd like to do something like the following
if( listenerIsNotAlreadyAssigned("variableName",listenerFunc) ){
$scope.$watch("variableName", listenerFunc); // assign it
}
Is there a way to implement the code above in Angular? If so, how?
Here's one method that might be considered hacky.. Demo here (click).
app.controller('myCtrl', function($scope) {
function checkWatchString(prop) {
var found = false;
angular.forEach($scope.$$watchers, function(item, i) {
if (item.exp === prop) { found = true; }
});
return found;
}
$scope.$watch('foo', function() {
});
console.log(checkWatchString('bar')); //false
console.log(checkWatchString('foo')); //true;
});
If you store a reference to the creation of a watch, you get a function reference that cancels the watch. You could take the obvious approach and track this manually and also be keeping the cancel function for each watch available.
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
$scope.foo = '123';
var reg = {};
if (!reg.foo) {
reg.foo = $scope.$watch('foo', function() {
console.log('foo changed');
});
}
});
I made an demo using a service for this: http://jsbin.com/AbAwObE/4/edit
Watch service
app.factory('watchService', function() {
var watchService = function(prop, unset) {
if (!watchService[prop]) {
return false;
}
if (unset) {
watchService[prop]();
delete watchService[prop];
}
return true;
};
return watchService;
});
Add $watch
watchService[prop] = $scope.$watch(prop, function() {
++$scope.changeCount;
});
Check $watch
if (watchService(prop)) {
Remove $watch
watchService(prop, true);