Possibility of sharing variable from directives to services in Angular - angularjs

I am in situation where have to pass LntLng variables to a service where an $http.get request is made using those latitude and longitude variables as URL parameters.
I tried some ways by using localstorage, this localstorage methodology was useful to some extent but it can set variables only once and not updating to new values if user change his location to some other place.I need to update the variable with new vaule every time user change there address.
.factory('someFactory', function($http) {
return {
get: function(result) {
var latitude='value from directive variable';
var longitude='value from directive variable';
$http.get('http://www.someapi.com/?longitude=' + longitude + '&latitude='+ latitude + '&customer_number=00000')
.success(function(result) {
});
}
}
});
Directive:
popupPromise.then(function(el) {
var searchInputElement = angular.element(el.element.find('input'));
scope.selectPlace = function(place) {
ngModel.$setViewValue(place);
map.setCenter(place.geometry.location); // from here i will get new updated place object with variable i need..My problem is to access the 'place' object in service .
ngModel.$render();
console.log('rendering');
el.element.css('display', 'none');
$ionicBackdrop.release();
};
};
NOTE: I'm going to take value from directive only after the 'place' object is updated and will send that value to service after directive is fully loaded.
Any suggestions please.

You can achieve this easily via $watch or $observe (if place value is coming from the directive attributes) in the directive.
// Service
.factory('someFactory', function($http) {
return {
get: function(lat, lng) {
return $http.get('http://www.someapi.com/?longitude=' + lng + '&latitude='+ lat + '&customer_number=00000');
}
}
});
// Directive
directives.directive('someDirective', ['someFactory', function(someFactory) {
return {
link: function($scope, element, attr) {
function responseHandler() {
// Your code on response from the API
ngModel.$setViewValue(place);
map.setCenter(place.geometry.location); // from here i will get new updated place object with variable i need..My problem is to access the 'place' object in service .
ngModel.$render();
console.log('rendering');
el.element.css('display', 'none');
$ionicBackdrop.release();
}
// Look for the change in place entered by user
attr.$observe('place', function(newValue) {
if (newValue) {
someFactory.get(newValue.lat, newValue.lng).then(responseHandler);
}
});
// OR //
// Watch for any scope variable (if you have place object as stored in Scope)
$scope.$watch('place', function(newValue) {
if (newValue) {
someFactory.get(newValue.lat, newValue.lng).then(responseHandler);
}
});
},
};
}]);

Related

Is there a canonical way to sync query params in the url (angular-ui-router) with variables on a scope with two-way binding?

Here is an example implementation of what I mean:
http://plnkr.co/edit/iwQPZfbDyKYwSzjjAPTN?p=preview
Bound one way:
Given you have the plunkr loaded with preview
When you change the text in the bottom input containing only "fooDefault"
Then you can see both query parameter in the url and the value on scope update all at
And also the other way:
Given you have the plunkr loaded with preview
When you change the text in the top input containing "#/a?foo=fooDefault"
Then you can see both query parameter in the url and the value on scope update all at once
Most relevant code section (StackOverflow asked me to include one):
.factory('syncUrlWithScope', function ($location) {
return function ($scope, defaultValues) {
$scope.$watch(function () {
return $location.search()
}, function (newVal) {
console.log(JSON.stringify(defaultValues), 'from url to scope', newVal)
$scope.queryParams = newVal
}, true)
$scope.$watch('queryParams', function (newVal) {
console.log(JSON.stringify(defaultValues), 'from scope to url', newVal, defaultValues)
for (var key in defaultValues) if (defaultValues.hasOwnProperty(key)) {
$scope.queryParams[key] = $scope.queryParams[key] || defaultValues[key]
}
$location.replace()
$location.search($scope.queryParams)
}, true)
}
})
.controller('ctrl1', function ($scope, syncUrlWithScope) {
syncUrlWithScope($scope, {foo: 'fooDefault'})
})
.controller('ctrl2', function ($scope, syncUrlWithScope) {
syncUrlWithScope($scope, {bar: 'barDefault'})
})
As you can see, I use 2 watchers to bind 'from url to scope' and also 'from scope to url'.
But there is an issue with it:
https://github.com/angular-ui/ui-router/issues/1840
Is there a better way for achieving the same kind of two-way binding without encountering that issue?
To have my view react on search params changed on both js side and manually via url manipulation i had something like:
$scope.$on('$locationChangeSuccess', function () {
console.log('$locationChangeSuccess LogsController');
$scope.searchParams = parseSearchToTags($location.search());
... do something with params ...
});

How to use an angular scope variable outside the scope it is defined in

I have a service like below, which fetches the id of a student from a RESTful (Laravel) API and returns it.
.factory('Student', function($http)
{
return {
getId: function(adm_no) {
return $http.post('/api/student/getId',{adm_no:adm_no})
.then(function(response)
{
return response.data;
},
function(httpError)
{
Notifier.error(httpError.data.error.message,'Error ' + httpError.status + " Encountered");
});
}
}
}
)
Then i use it as follows in a controller.
$scope.adm_no = 98;
Student.getId($scope.adm_no)
.then(function(response)
{
$scope.id = response;
});
// probably i want to use the `$scope.id` when a particular event takes place (or even in another query to the server alltogether), but outside the above function scope e.g.
$scope.showId = function()
{
alert($scope.id);
};
Now, the question is how I can use the a scope variable declared in a 'local scope' outside the scope, for the usage above shows that $scope.id is undefined?
Your $scope.id is undefined in function $scope.showId() because when you call an alert function, your post request hasn't finished yet and so $scope.id hasn't been initialized (it is beeing executed asynchronously). Try this:
$scope.showId = function() {
if ($scope.id) {
alert($scope.id);
}
};
Anyway you don't have to use $rootScope in this case. Your property id from $scope is accesible from your whole controller. You have to wait for the ajax post request and than it is initialized.
In place of $scope you have to use $routescope variable to get id in other place as-
Student.getId($scope.adm_no)
.then(function(response)
{
$routescope.id = response;
});

Angular $watch a service object from a controller and read properties

I've been trying to watch a service object from a controller. I've been trying to solve this problem in a nice way but it has been imposible. the only solution I could find is this one (see the code). The thing is when I "console.log" the returned value, I can see the object but I can not access to the properties. it says "undefined". So the only solution I could find is this one but I don't like it too much. Any ideas???
This is my "watcher" of my controller:
$scope.$watchCollection(function(){return angular.toJson(measuresServ.getFinalMeasuresVal())},function(newVal, oldVal) {
$scope.measures = JSON.parse(newVal);
console.log($scope.measures);
console.log($scope.measures.temperatura);
});
This is the object of my service:
var finalMeasures = {};
return {
getMeasures : function(){
finalMeasures.temperatura = 24;
finalMeasures.humedad = 45;
},
getMeasuresVal: function(){
return finalMeasures;
}
}
I think it should work:
$scope.$watch(function () {
return measuresServ.getFinalMeasuresVal();
}, function (newVal, oldVal) {
console.log(newVal);
}, true);

Injecting data objects into a directive factory in AngularJS

So I have a directive that takes in data objects as an argument into the scope. The problem is that I handle all my data in my service layer.
So this is some normal non-directive code:
angular.module('app').factory('appFactory', ['appValues', function(appValues) {
var getStuff = function() { return appValues.stuff; };
}]);
But if want to reuse the factory inside a directive and get appValues as an argument:
angular.module('app').directive('myDir', [function() {
return {
...
scope: {
values: '='
}
....
};
}]);
But this puts it on the scope and not into my data layer. So now I need to send the values object to every function call in my directive factory:
angular.module('app').factory('myDirFactory', [function() {
var getStuff = function(values) { return values.stuff; };
}]);
Is there any good pattern to solve this and keep data in the data-layer and bypass the scope/controller?
Also, the factory will be a singleton shared amongst instances of the directive? How should I solve that then? Create a new injector somehow? Submit to putting lots of data object logic into the controller (which I've been thought not to do)?
It was a while ago, and I guess that a simple soultion is simply to provide an function initialize(value) {... return {...};} and then the returned object has access to the value argument without providing it as a parameter everywhere:
angular.module('myDir').factory('myDirFactory', [function() {
var initialize = function(values) {
var getStuff = function() {
return values;
};
return {
getStuff: getstuff;
};
};
return {
initialize: initialize
};
}]);

Change controller variable from within a service

In the "ok"-function of a modal, I'm trying to update a variable from the scope that opened the modal. This
$scope.modalOptions.assets.length = 0;
perfectly works: the variable "assets" in the "parent" scope immediatly changes and, while the modal is still open, the data represantation of "assets" is updated and emptied in the main page.
What bugs me is that changing above to
$scope.modalOptions.assets = $scope.modalOptions.assetFactory.query();
has no effect at all. I can verify that the API Controller is called and that it returns new data which should, in effect, change the representation of "assets" as well.
The variable itself is defined in the controller like this:
$scope.assets = bondFactory.query();
And I pass it in to the Modal-Service like this:
assets: $scope.assets
I'd be thankful for tips and ideas..
EDIT
How the Modal is called:
$scope.postModal = function () {
//Pass view
var customModalDefaults = {
templateUrl: 'scripts/app/views/postBond.html'
}
//Pass data
var customModalOptions = {
asset: angular.copy($scope.asset),
assets: $scope.assets,
currencies: globalVariables.currencies,
assetFactory: bondFactory,
validationErrors: [],
action: 'POST'
};
//Show & Callback
ModalAssetService.showModal(customModalDefaults, customModalOptions).then(function (result) {
// fill me with usefull content
});
};
The Modal Service itself:
app.service('ModalAssetService', ['$modal',
function ($modal) {
var modalDefaults = {};
var modalOptions = {};
this.showModal = function (customModalDefaults, customModalOptions) {
//Create temp objects to work with since we're in a singleton service
var tempModalDefaults = {};
var tempModalOptions = {};
//Map angular-ui modal custom defaults to modal defaults defined in service
angular.extend(tempModalDefaults, modalDefaults, customModalDefaults);
//Map modal.html $scope custom properties to defaults defined in service
angular.extend(tempModalOptions, modalOptions, customModalOptions);
//Create controller
tempModalDefaults.controller = function ($scope, $modalInstance, $resource, errorService) {
$scope.modalOptions = tempModalOptions;
// exit modal with "ok"
$scope.modalOptions.ok = function (result) {
//POST
if ($scope.modalOptions.action == 'POST') {
// try and post asset
$scope.modalOptions.assetFactory.save($scope.modalOptions.asset, function () {
// success
$scope.modalOptions.assetFactory.query({}, function (data) {
$scope.modalOptions.assets = data;
// $modalInstance.close();
});
}, function (error) {
// error
$scope.modalOptions.validationErrors = errorService.fn(error);
});
};
//PUT
if ($scope.modalOptions.action == 'PUT') {
//TODO
alert("put");
};
};
// exit modal with "cancel"
$scope.modalOptions.close = function (result) {
$modalInstance.dismiss('cancel');
};
}
return $modal.open(tempModalDefaults).result;
};
}
]);
EDIT 2: the bondFactory
app.factory('bondFactory', ['$resource', function ($resource) {
return $resource('../api/bond/:id', null, {
'update': { method: 'PUT' }
})
}]);
SOLUTION
I still did not figure out exactly how to fix the problem of the scope not being notified about a change if the change results from an assignment of a $resource GET to a variable from said scope.
Anyways, for my specific case, I found a better solution: when clicking "OK" in the modal, the new asset is being sent to the API. If errors happen there (validation failed etc.), the Modal will stay open and some notifications will inform the user. If the new asset was posted successfully, then a new GET is sent to the API and only the last new asset is being added to the existing array of asset-elements. This means no "flashing", just one more row is added to the list of assets.
It's not a good form of programming to assume that the last element from the GET-Request is definitly the new asset and there is of course some overhead in retrieving the complete list of all assets when just the very last of these would suffice, but I guess it works and I'll just add some sort to the API to make sure it always is in right order. Code:
$scope.modalOptions.assetFactory.save($scope.modalOptions.asset, function () {
//success
$scope.modalOptions.assetFactory.query({}, function (result) {
$scope.modalOptions.assets.push(result[result.length - 1]);
$modalInstance.close();
});
}, function (error) {
//error
$scope.modalOptions.validationErrors = errorService.fn(error);
});
Your factory is performing an async operation, so you need to use callbacks to access the data and have the view update:
$scope.modalOptions.assetFactory.query({}, function(data) {
$scope.modalOptions.assets = data;
});
Without knowing how you are calling the modal, or if you are using a hand-rolled one or the one from UI-Bootstrap, I can only guess at how things are working. Having said that, if you are not using the modal from UI-Bootstrap you really should be. It uses promises and has the ability to return just about anything from the modal.
$scope.myModalPromise = $modal.open(modalConfig);
$scope.myModalPromise.result.then(function(data){
//things to do upon closing the modal with $close
//data is any value or object that you want to pass back
}, function(data){
//things to do upon closing the modal with $dismiss
//data is any value or object that you want to pass back
});
The setup lets your modal perform asynch operations and only return the value when they have completed. The beauty of this is that they are returned to the calling controller cleanly and in an Angular way.

Resources