I have one controller named "contractController"
contractController contains a method save()
contractController opens one model window with controller named "PopUpcontroller"
from PopUpcontroller i want to call save method on contractController
tried to call save method like $parent but nothing works.
Please advice.
If you are using Angular UI Bootstrap (http://angular-ui.github.io/bootstrap/), then, make use of the "resolve" attribute when you call $modal.open().
Whatever you add to resolve, will be available if you hook it up as a dependency injection.
In the example on their page, the "item" is available as below because it is hooked up in "resolve".
Resolve:
var modalInstance = $modal.open({
templateUrl: 'myModalContent.html',
controller: 'ModalInstanceCtrl',
size: size,
resolve: {
items: function () {
return $scope.items;
}
}
});
Use:
angular.module('ui.bootstrap.demo').controller('ModalInstanceCtrl', function ($scope, $modalInstance, items) {
If the two controllers are nested so that:
<div ng-controller="contractController">
<div ng-controller="PopUpcontroller">
</div>
</Div>
you can just call $scope.save() on the popUpController and it automatically goes up to find a parent with that method (till the $rootScope).
if they are not nested you should use the services in order to perform the communication among controllers
You need to make sure the popup is created from contractController's scope if you want to retain its prototype. How are you creating the popup?
Something like this in Angular UI Boostrap (I don't use it but the API should accept it):
$modal.open({
scope: $scope,
/* ... other opts */
});
Related
Suppose I have a general purpose controller, TableController, that can be used in multiple places in the app to display a table of Key x Value pairs via a custom directive, ui-table, that generates an HTML table.
angular.module('ui.table', [])
.controller('TableController', ['$scope', 'data',
function($scope, data) { $scope.data = data; }])
.directive('uiTable', function() {
return { templateUrl: 'table.html' }
});
I could use the controller in the following template:
<div ng:controller="TableController">
<div ui-table></div>
</div>
And create a factory to pass data to this controller.
.factory('data', function() {
return [{'App':'Demo', 'Version':'0.0.1'}];
})
But, I have multiple controllers (sometimes in the same views), so I need to "bind" a particular factory to a particular controller (e.g., UserProfile, AppData, etc.)
I have started to look at angular-ui-router's $stateProvider, but it seems too complicated for what must be a typical use case? What I'd really like to be able to do is use the template to annotate which factory (what I think of as a model) should be used for that particular controller. E.g., something like:
<div ng:controller="TableController" ng:model="AppData"></div>
What is the right approach?
EDIT:
I've figured out $stateProvider and "resolve" allows me to map provider services onto injected values for the state's main controller -- but the controller I want to influence is a child of this controller.
$stateProvider
.state('home', {
url: '/home',
templateUrl: '/home/view.html',
controller: 'MainViewController',
resolve: {
'data': 'AppData'
}
});
So, I still can't figure out how to influence the controllers inside the state's view.
I think what you are looking for is simply passing your data into the directive through attributes. Then use an isolated scope in directive so you can have multiple instances active at the same time
<div ng-controller="ViewController">
<div ui-table dataSource="tableData"></div>
</div>
Then your directive would be written in a generic way to be re-usable regardless of the data passed in.
.factory('SomeService', function(){
var data ={
headings: ['ID','Age','Name'],
rows:[
{id:1, 33,'Bill'}......
]
};
return {
get:function(){ return data;}
};
})
.controller('ViewController', function($scope, SomeService){
$scope.tableData = SomeService.get();
})
.directive.directive('uiTable', function () {
return {
scope: {
dataSource: '=' // isolated scope, 2 way binding
}
templateUrl: 'table.html',
controller: 'TableController', // this controller can be injected in children directives using `require`
}
});
In essence this is just reversing your layout of controller/directive. Instead of TableController wrapping the directive, it is used internally within directive. The only reason it is a controller in the directive is to allow it to be require'd by child directives such as perhaps row directive or headings directive and even cell directive. Otherwise if not needing to expose it for injection you can use link and put all sorts of table specific operations in there
As mentioned in my comments there are various approaches to creating a table directive. One is with heavy configuration objects, the other is with a lots of declarative view html that use many child directives. I would suggest analyzing the source of several different grid/table modules to see what best suits your coding style
Thanks in part to #charlietfl (above) I have an answer:
<ui-graph model="SomeGraphModel"></ui-graph>
Then:
angular.module('ui.graph', [])
.directive('uiGraph', [function() {
return {
controller: 'GraphController',
scope: {
model: '#model' // Bind the element's attribute to the scope.
}
}
}]);
.controller('GraphController', ['$injector', '$scope', '$element',
function($injector, $scope, $element) {
// Use the directive's attribute to inject the model.
var model = $scope.model && $injector.get($scope.model);
$scope.graph = new GraphControl($element).setModel(model);
}])
Then somewhere else in the app (i.e., not necessarily in the directive/controller's module):
angular.module('main', [])
.factory('SomeGraphModel', function() {
return new GraphModel();
})
I have a controller that needs a thing provided by a route resolve function:
$routeProvider.when('/some/url', {
controller: MyController,
controllerAs: 'myCtrl',
resolve: {
theAnswer: ['deepThought', function(deepThought) {
return deepThought.computeTheAnswerAndReturnAPromise();
}]
}
});
var MyController = ['$route', function($route) {
this.theAnswer = $route.current.theAnswer;
}];
Now I want to do an end-to-end test, checking that the route matches and that parameters are propagated properly:
// ...set up the routes...
$location.path('/some/url');
$rootScope.$digest();
var ctrl = ???;
expect(ctrl.aThing).toBe(42);
In the non-test setup, I can put in a log statement and see that the controller is being created successfully and gets the correct data injected. The only problem is: how to get hold of the controller in the test?
There is $route.current.controller, but it contains the controller's constructor function and not the controller instance.
The documentation promises a $route.current.locals.$scope, from which I could get myCtrl, but the $scope property doesn't actually exist unless we also use ngView (it gets set here).
The controller isn't registered with any module, so I can't use $provide to intercept its creation and stash the controller somewhere.
Found it, thanks to #PSL's comment. The thing that actually constructs the controller is the ngView link function. We can fake that easily enough:
var ctrl = $controller(MyController, $route.current.locals);
I'm using ui-router with $modal and my set up is like this in my routing page:
.state('resourcesControl.resource.dataStuff', {
url: "/:resourceId/dataStuff",
onEnter: ['$stateParams', '$state', '$modal', '$timeout', 'resourceService', function($stateParams, $state, $modal, $timeout, resourceService){
var modalInst = $modal.open({
templateUrl: "templates/dataStuff.html",
windowClass: 'data-modal',
controller: 'dataStuffCtrl'
});
modalInst.result.then(function(){
}, function(){
$state.go('resourcesControl.resource');
});
}]
})
My understanding with UI router is that all the child states have access to their parents controller and scope variables. In theory my dataStuffCtrl should have access to resource and resourcesControl controllers and their scopes.
However, when I wrap curly brackets around a parent scope item in the dataStuff view, nothing renders. Is there a way around this? I remember seeing other people posting about parent controllers with $modal but I can't find them on SO atm.
The issue is that the $modal service adds the element to the end of the document. So it doesn't sit in you scope hierarchy and instead is only a child of the $rootScope. You have a couple of options.
Pass the data to the modals controller via the $rootScope or broadcast/emit events (messy)
Manually pass the modal the scope you want to use as part of the modal.open options object via the scope property. (You can call $scope.$new() to manually create a child scope).
Use the resolve property of the modal.open options object to pass the data to be injected into the modals controller just like angular route resolves
i am using angular ui modal to create modal in my project.
Everything works fine until I need to refer to variable in parent scope. see plunker code
It seems like modal can't access parent scope. Is there anyway to overcome this?
Angular UI's modals use $rootScope by default (See the documentation here).
You can pass a scope parameter with a custom scope when you open the modal – e.g. scope: $scope if you want to pass the parent scope. The modal controller will create a sub-scope from that scope, so you will only be able to use it for your initial values.
You'll need to refer to the parent scope in your $modal options. Angular documentation
Corrected Plunker Version
Below is also a code snippet of what I added to make it work.
var modalInstance = $modal.open({
templateUrl: 'myModalContent.html',
controller: ModalInstanceCtrl,
scope:$scope, //Refer to parent scope here
resolve: {
items: function () {
return $scope.items;
}
}
});
You can add an ID to the parent div and use his scope.
<div id="outerdiv" ng-controller="OuterCtrl">
<h2>Outer Controller</h2>
<input type="text" ng-model="checkBind">
<p>Value Of checkbind: {{checkBind}}</p>
And set up a "fake" binding within the controller
//init
$scope.checkBind = angular.element(document.getElementById('outerdiv')).scope().checkBind;
$scope.$watch('checkBind', function (newValue, oldValue) {
//update parent
angular.element(document.getElementById('outerdiv')).scope().checkBind = $scope.checkBind;
});
See http://plnkr.co/edit/u6DuoHJmOctFLFhvqCME?p=preview
The problem
I'm using UI Bootstrap's dialog service in my application, this service creates modal dialog and I do that using the following method on my outer $scope:
$scope.showRouteEditDialog = function (template, route) {
// argument `route` is not currently used
var dialog = $dialog.dialog({
backdrop: true,
keyboard: false,
backdropClick: true,
templateUrl: template,
controller: 'RouteController'
});
dialog.open().then(function(result) {
alert(result); // This line is called when dialog is closed
});
}
This method is later called from partial view with the following markup
<i class="halflings-icon edit"></i>
My dialog handles editing of a route (route is a sub-model within main model) and therefore I would like to pass that route to the dialog so that it treats it like it's own model without knowing anything about the outer model.
My initial guess on this problem was to assign the route argument to dialog variable, something like dialog.route = route and have it later used within controller with the following code:
Application.controller('RouteController', ['$scope', 'dialog', function ($scope, dialog) {
// `dialog` is our dialog and it is injected with injector
doSomethingWith(dialog.route)
}]);
Though this approach creates a dependency and doesn't look like an angular's way of doing things
I have also found this post saying that I should be using service for that purpose, but it seems like this solution is an overkill for such minor problem.
Question
What is the angular way of passing a value from outer controller to an inner one with scenario described above.
Thank you
You can use "resolve" -
var dialog = $dialog.dialog({
resolve: {route: function(){return "route"}}
});
and later you can inject the "route" value inside of your controller
.controller("SomeCtrl",function(route){
})