I am trying to get the http request result to my child controller.
I have something like
<div ng-controller = "parentCtrl">
<button ng-click="callApi()">click me</button>
<div ng-controller = "childCtrl">
<div>{{productDetail}}</div>
</div>
</div>
angular.module('App').controller('parentCtrl', ['$scope','myFactory',
function($scope, myFactory) {
$scope.callApi = function() {
myFactory.request(id)
.then(function(data) {
$scope.productDetail = data
//do something in parent controller here....
})
}
}
]);
angular.module('App').controller('childCtrl', ['$scope',
function($scope) {
//I am not sure how to get the productDetail data here since it's a http request call.
}
]);
angular.module('App').factory('myFactory', function($http) {
var service = {};
service.request = function(id) {
return createProduct(id)
.then(function(obj) {
productID = obj.data.id;
return setProductDetail(productID)
})
.then(getDetail)
.then(function(productDetail) {
return productDetail.data
})
}
var createProduct = function(id) {
return $http.post('/api/product/create', id)
}
var setProductDetail = function(id) {
return $http.post('/api/product/setDetail', id)
}
var getDetail = function() {
return $http.get('/api/product/getDetail')
}
return service;
});
I was able to get the request result for my parentCtrl but I am not sure how to pass it to my child controller. Can anyone help me about it?
Thanks!
Potential approaches:
1) Inject myFactory into the child controller as well.
2) Access the parent scope directly from within childCtrl:
$scope.$parent.productDetail
3) If wanting to access from HTML
$parent.productDetail
Above assumes you are wanting to access that value specifically separate from a potential version on the child scope (existing code doesn't show that).
If it's a child scope, and nothing on the child scope (or a scope in between) is named productDetail, and you're not setting a primitive value in the child scope with that name, then you should be able to see the value directly through prototypical inheritance (but any of the three scenarios listed could force the need for a reference through the parent).
Related
I have a textarea in my HTML like this:
<textarea ng-model="commentBox"></textarea>
To access this i simply use "$scope.commentBox" in my controller. But my question is, how do i access the same commentBox within a different controller?
I do have a factory/service setup for this purpose, but i cant figure out how to get the commentBox value in there, for my other controller to use it.
In my factory i have an object var saved = {} and I want to add a property called "comment", with the value of whatever is inside the textarea. Like this saved.comment = commentbox And then access that value from the other controller.
I'm still new at Angular and tried to send the scope information in a parameter to the facory.
var saved = {};
factory.addComment = function (commentbox) {
saved.comment = commentbox
}
Then have my controller send the scope information on a button click,
$scope.testFunction = function () {
myFactory.addComment($scope.commentBox);
}
But yeah, that did not work out.
Note that i need this to work within the factory, and not by using another ng-controller in the HTML.
You need to return your saved var in your factory
var saved = {};
factory.addComment = function (commentbox) {
saved.comment = commentbox
}
return saved
Here there is an example using a service
app.service('fakeService', function() {
var savedData = {};
var addComment = function(newComment) {
savedData.commnet = newComment;
};
var getComment = function(){
return savedData.comment;
};
return {
addComment: addComment,
getComment: getComment
};
});
To inject a factory/service in your controller
app.controller('fakeController', ['$scope','yourFactory',function ($scope, yourFactory) {
console.log(yourFactory.comment) // here you should log saved comment
}])
Then in your controller, you can inject the factory/service and access to saved data. But remember, is your refresh your page, data will be lost, so, to avoid that, you should persist data on your DB.
The problem that I need to generate link on the fly since the link is set in ng-repeat. I think I need to execute custom function inside ng-repeat loop which gets data from $http and pushes link to $scope.array. Then bound href to $scope.array[someIndex]....The problem I don't know if:
it's the only way
a good design
how to implement it
Example:
HTML
<div ng-repeat-start="item in items">
the link
// here execute $scope.getUrl(item ) somehow
<div class="extra-div">
<div ng-repeat-end=""></div>
Controller:
$scope.arrayOfUrls= [];
$scope.getUrl = function(url){
$http.get(url).then(
function(data){
arrayOfUrls.push(data.link);
}
)
}
How to execute getUrl during ng-repeat cycle?
PS. I cannot bound href directly to getUrl function since there is $http which eventually result in infinite digest loop.
Also promises can be returned not in order so expecting that first call to getUrl will push link to $scope.arrayOfUrls[0] is false assumption.
UPDATE:
As #Claies suggested I trie to prefetch links like this:
Contoller executes $scope.loadFeed();
$scope.loadFeed = function() {
http.jsonp('feed url').then(function(res) {
$scope.feeds = res.data.responseData.feed.entries;
$scope.feeds.forEach(function(e) {
// prefetch content and links for each feed
//hook new entryStateUrl property to feed objects
e['entryStateUrl'] = $scope.getEntryStateUrl(e.link); // e['entryStateUrl'] is undefined
})
})
}
}
$scope.getEntryStateUrl = function(inputUrl) {
$http.get(inputUrl).then(function(data) {
// do stuff
return data.link;
});
}
}
Now seems like I am trying pre-fetch urls but getting undefined for e['entryStateUrl']...
The problem maybe about assigning scope variable when $http is not done getting results... Also it seems like there are nested promises: $http.jsonp and inside it $http.get.
How to fix it?
As this requires UI enhancement, a directive would be a good approach. How about a directive like this ( JSFiddle here ). Please note that I am calling $window.open here - you can replace this with whatever the application requires. :-
todoApp.directive('todoLinks', ['$window',function ($window) {
var directive = {};
directive.restrict = 'A';
directive.transclude = 'true';
directive.scope = { ngModel: '=ngModel', jsOnClick:'&' };
directive.template = '<li ng-repeat="item in ngModel">{{item.name}}</li>';
directive.link = function ($scope, element, attributes) {
$scope.openLink = function (idx) {
$window.open($scope.ngModel[idx].link); //Replace this with what your app. requires
if (attributes.jsOnClick) {
//console.log('trigger post jsOnClick');
$scope.jsOnClick({ 'idx': idx });
}
};
};
return directive;
}]);
When the controller fills the todo items like this:-
todoApp.controller("ToDoCtrl", ['$scope','$timeout','dbService',function($scope, $timeout, dbService)
{
$scope.todo=[{"name":"google","link":"http://www.google.com"},{"name":"bing","link":"http://www.bing.com"},{"name":"altavista","link":"http://www.altavista.com"}];
}]);
Usage of this directive is simple:-
<div todo-links ng-model="todo"></div>
So I totally do this in reverse all the time when using the directive property require: '^ParentCtrl' inside the child directive. Using require to then call the parent function; however, I need to do this in reverse.
Question:
How do I trigger FROM a parent directive the execution of a function IN a child directive.
Note:
1. Child Directive has no function is inside a link:
2. essentially I want a reverse require.
Parent Directive:
'use strict';
angular.module('carouselApp')
.directive('waCarousel', function() {
return {
templateUrl: 'views/carousel/wa.carousel.html',
controller: function($scope) {
var self = this;
// this function is being called based on how many pages there are
self.carouselElLoaded = function(result) {
var count = 1;
Carousel.params.pageRenderedLength += count;
//when all the pages are loaded
if (Carousel.params.pageRenderedLength === Carousel.params.pageLength) {
Carousel.params.carouselReady = true;
// !!!!!!!! Trigger will go here!!!!!!!!!//
ChildCtrl.drawHotspots(); // (**for placement only**)
} else {
Carousel.params.carouselReady = false;
}
};
}
}
})
Child Directive:
'use strict';
angular.module('carouselApp')
.directive('waHotspots', function() {
return {
require: '^waCarousel',
link: function (scope, element, attrs, ctrl) {
//call this directive based on how
scope.drawHotspots = function () {...};
}
})
This is possible by having the parent controller talk to the child controller through a well defined API, that you create. The idea is that you want to maintain loose coupling between the parent and the child directive by having each respective controller knowing as little about each other as possible, but still have enough knowledge to get the job done.
To achieve this, require the parent directive from the child directive, and let the child directive register itself with parent's controller:
Child directive:
require: '^parentDirective',
controller: function(){
this.someFunc = function() {...}
},
link: function(scope,element,attr, parentCtrl){
parentCtrl.register(element);
}
Then in your parent directive, implement the register function, and get the child's controller, and call the child's function when needed:
Parent directive:
controller: function(){
var childCtrl = undefined;
this.register = function (element) {
childCtrl = element.controller();
}
this.callChildFunc = function (){
childCtrl.someFunc();
}
},
link: function (scope,element){
var ctrl = element.controller();
ctrl.callChildFunc();
}
You could always trigger it via a $watch. Just pass in the parent scope value that you want to watch and change it's value.
Parent:
$scope.drawHotspots = false;
Template:
waHotspots the-trigger="drawHotspots"....
Child Directive:
localTrigger: '#' // Receive the value to watch
scope.$watch('localTrigger',function() {
// call drawHotspots if value is set to true
});
Its on old topic but I came here today so might others ...
I think the best approche is to use a Service
angular.module('App').service('SomeService', [SomeService]);
Then inject the service into both the parent and child ...
controller : ['$rootScope', '$scope','SomeService', SomeDirectiveController],
Use the service to talk to each other ...
In their controllers SomeService.setParent(this) and SomeService.setChild(this)
Service would have a field to hold the references :
this.parentCtrl = null;
this.childCtrl = null;//or [] in-case you have multiple childs!
Somewhere in the parent : SomeService.childCtrl.someFunctionInChild()
Or if you want a restricted access , in service make the fields private :
var parentCtrl = null;
var childCtrl = null;//or [] in-case you have multiple childs of the same type!
this.callUserFunc = function(param){childCtrl.someFunctionInChild(param)};
And Somewhere in the parent : SomeService.callUserFunc(myparam)
I have 2 services and would like to update a variable in the 1st service from the 2nd service.
In a controller, I am setting a scope variable to the getter of the 1st service.
The problem is, the view attached to the controller doesn't update when the service variable changes UNLESS I use angular.extend/copy. It seems like I should just be able to set selectedBuilding below without having to use extend/copy. Am I doing something wrong, or is this how you have to do it?
controller
app.controller('SelectedBuildingCtrl', function($scope, BuildingsService) {
$scope.building = BuildingsService.getSelectedBuilding();
});
service 1
app.factory('BuildingsService', function() {
var buildingsList = [];
var selectedBuilding = {};
// buildingsList populated up here
...
var setSelectedBuilding = function(buildingId) {
angular.extend(selectedBuilding, _.find(
buildingsList, {'building_id': buildingId})
);
};
var getSelectedBuilding = function() {
return selectedBuilding;
};
...
return {
setSelectedBuilding: setSelectedBuilding,
getSelectedBuilding: getSelectedBuilding
}
});
service 2
app.factory('AnotherService', function(BuildingsService) {
...
// something happens, gives me a building id
BuildingsService.setSelectedBuilding(building_id);
...
});
Thanks in advance!
When you execute this code:
$scope.building = BuildingsService.getSelectedBuilding();
$scope.building is copied a reference to the same object in memory as your service's selectedBuilding. When you assign another object to selectedBuilding, the $scope.building still references to the old object. That's why the view is not updated and you have to use angular.copy/extend.
You could try the following solution to avoid this problem if you need to assign new objects to your selectedBuilding:
app.factory('BuildingsService', function() {
var buildingsList = [];
var building = { //create another object to **hang** the reference
selectedBuilding : {}
}
// buildingsList populated up here
...
var setSelectedBuilding = function(buildingId) {
//just assign a new object to building.selectedBuilding
};
var getSelectedBuilding = function() {
return building; //return the building instead of selectedBuilding
};
...
return {
setSelectedBuilding: setSelectedBuilding,
getSelectedBuilding: getSelectedBuilding
}
});
With this solution, you have to update your views to replace $scope.building bindings to $scope.building.selectedBuilding.
In my opinion, I will stick to angular.copy/extend to avoid this unnecessary complexity.
I dont believe you need an extend in your service. You should be able to watch the service directly and respond to the changes:
app.controller('SelectedBuildingCtrl', function($scope, BuildingsService) {
// first function is evaluated on every $digest cycle
$scope.$watch(function(scope){
return BuildingsService.getSelectedBuilding();
// second function is a callback that provides the changes
}, function(newVal, oldVal, scope) {
scope.building = newVal;
}
});
More on $watch: https://code.angularjs.org/1.2.16/docs/api/ng/type/$rootScope.Scope
I have a provider:
AdviceList.provider('$adviceList',function(){
this.$get = function ($rootScope,$document,$compile,$http,$purr){
function AdviceList(){
$http.post('../sys/core/fetchTreatments.php').success(function(data,status){
this.treatments = data;
console.log(this.treatments); // the correct object
});
this.adviceCategories = [
// available in the controller
];
}
return{
AdviceList: function(){
return new AdviceList();
}
}
}
});
Further, i have this controller:
AdviceList.controller('AdviceListCtrl',function($scope,$adviceList){
var adv = $adviceList.AdviceList();
$scope.treatments = adv.treatments; // undefined
});
Why is it, that the controller's $scope.treatments stays undefined, this.treatments inside the provider however, is filled correctly? Also, adviceCategories is available in my controller.
The call you get teatment is async in nature so the results may not have been populated when you try to assign them.
So here
var adv = $adviceList.AdviceList();
$scope.treatments = adv.treatments; //The treatments would only get filled after the server call is over.
You need to rewrite the code in a way that you assign it to your scope property on the success callback.
I will recommend you to simplify your code
1) Use simple factory method of angular instead of provider
2) return a promise to avoid using callbacks
AdviceList.service('adviceList', function ($http) {
return {
adviceList: function () {
return $http.post('../sys/core/fetchTreatments.php');
}
}
});
AdviceList.controller('AdviceListCtrl', function ($scope, $adviceList) {
adviceList.AdviceList().then(function (data) {
$scope.treatments = data //set value to data when data is recieved from server
});
});