Cannot call method 'push' of undefined
I receive that error when my AngularJS runs the following:
$scope.ok = function () {
$modalInstance.close();
$scope.key.push({ title: '', gps:'', desc:''});
};
I declared my $scope.key = []; right after my .controller as I need to be able to use the $scope.key in other parts of the application. Could someone please point out where I should be declaring this?
$scope.ok is my Save Button which pulls the data from my input fields and $scope.plotmarkers is what I am using to pull the data from the inputs that have been pushed.
app.controller('MenuSideController', ['$scope','$modal','$log', function($scope, $modal, $log) {
$scope.key = [];
$scope.createmarker = function () {
var modalInstance = $modal.open({
templateUrl: 'template/modal-add-marker.html',
controller: ModalInstanceCtrl,
resolve: {}
});
modalInstance.result.then(function (selectedItem) {
}, function () {
$log.info('Modal dismissed at: ' + new Date());
});
};
var ModalInstanceCtrl = function ($scope, $modalInstance) {
$scope.ok = function () {
$modalInstance.close();
$scope.key.push({ title: '', gps:'', desc:''});
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
};
$scope.plotmarkers = function() {
console.log($scope.key);
};
}]);
Don't forget to pass a scope to $modal.open! If you don't, it will default to the root scope, which is not a child of the controller's scope, so key is not defined on it or its parents. You can use { scope: $scope.$new(), ... } as a parameter of $model.open to pass it a child of the controller's scope. See the docs for details. Good luck!
I think you are using $modal a bit improperly.
So you have two controller here - one for the application logic and one for the modal window itself. According to best practice you shouldn't interact between different controllers directly (the case with parent-child directive is exclusion but honestly speaking it not direct interaction - rather your linker function used the controller from parent directive). Instead of interaction between controller in general we should use services. It is just additional note.
What is related to your question - you have two things here to keep in mind:
if you want to pass the information from the controller to the modal window you should use resolve property which actually specifies multiple functions which are called to get the data and then injected to the modal windows controller as a function parameter. This way you can pass some data from the main controller.
if you need to pass the result back you should use result property of the modal instance which is the promise (by using your $modalInstance.result.then(function(result){ ... }); ) To pass this object from the modal you can close it with the result as a parameter like this: $modalInstance.close(result);
Hope this helps. For further details you can look at the documentation for the $modal: Angular Bootstrap
Related
I have a variable that i use almost everywhere in my app, in many different controllers.
I'm looking for the best possible way of setting that variable knowing that :
-The variable will need to get updated from a controller and resulting in an update on the whole app ( other controllers mainly).
-Some of those function are instance of an object with function in itself that have a callback , does that cause any issue?
So far there seems to be 2 way of doing that :
rootScope but that rarely advised apparently:
myApp.run(function ($rootScope) {
$rootScope.var = "string";
});
And building a custom directive ( but what about setting it's variable ?)
angular.module('myApp').factory('test', function() {
return {
test : 'string'
};
});
Can anyone point me in the right direction and help me choose betweem the two?
I would recommend using a service or factory save that value in someService.variable and the broadcast n event that this value has been changed. I am attaching sample code. It may contain some syntactical error but I want to give you an idea
angular.module("myapp")
.controller('myCtrl', function($scope, $rootScope, myService) {
$scope.change = function() {
myService.var = 'abc';
$rootScope.broadcast('varChanged');
};
})
.controller('myOtherCtrl', function($scope, $rootScope, myService) {
$rootScope.on('varChanged', function(e) {
use(myService.var);
});
})
.service('mySerice', function() {
this.var = '';
});
The official documentation of AngularJS does not contain anything that describes how $uibModalInstance.close works, in the following code fragment, scope.close is a method used to close the modal window and pass an object to the caller controller
var app = angular.module('myApp');
app.controller('ModalController', ['$uibModalInstance', modalControllerFn]);
function modalControllerFn($uibModalInstance) {
var scope = this;
// some data object
scope.data = {key1: "value1", key2: "value2"};
scope.close = function() {
$uibModalInstance.close(scope.data);
}
}
Question (1)
Does passing anything belonging to the modal scope using `$uibModalInstance.close` (non-literal value, i.e: `scope.x`) prevent angular garbage collection from destroying the entire modal scope? Is this a scenario for causing memory leaks?
Question (2)
How does angular `$uibModalInstance.close(data)` exactly work?
Please have a look at the JavaScript example on Angular UI Bootstrap's website here: Angular UI Bootstrap Modal
Scroll down just a bit and click the JavaScript tab to see the code.
The important part is this:
modalInstance.result.then(function (selectedItem) {
$scope.selected = selectedItem;
}, function () {
$log.info('Modal dismissed at: ' + new Date());
});
Above, the selectedItem variable is what is passed into:
$uibModalInstance.close(rightHereGetsPassedAsResult)
var modalInstance = $uibModal.open({template:tmpl, controller: ctrlr})
In the above code $uibModal.open() returns a promise to be resolved or rejected.
If it is resolved, when the user clicks your 'ok' button, you may have a statement that does something afterwards like..
modalInstance.result.then(function (data) {
console.log('user clicked ok', data)
})
On the $scope of the controller for your modal instance you will have a function as the ng-click for your 'ok' button
$scope.ok = function() {
$uibModalInstance.close(data);
}
The data you pass to $uibModalInstance.close(data) in your $scope function is returned as the data result in the aforementioned statement.
I have small problem to solve.
I have modal controller rejectIssueModalCtrl.js
(function () {
'use strict';
function rejectIssueModalCtrl($modalInstance, issue, $rootScope) {
var self = this;
self.cancel = function () {
$modalInstance.dismiss('cancel');
};
self.reject = function ($rootScope) {
$modalInstance.close(self.reason);
console.log(self.reason);
};
$rootScope.reasono = self.reason;
}
rejectIssueModalCtrl.$inject = ['$modalInstance', 'issue', '$rootScope'];
angular
.module('app')
.controller('rejectIssueModalCtrl', rejectIssueModalCtrl);
})();
When I click the button I can open this modal and write a reason. I want to show this reject reason in table in other controller.
Here's my code from other controller issueDetailsCtrl.js
$scope.reasonoo = $rootScope.reasono;
function rejectIssue() {
var rejectModal = $modal.open({
templateUrl: '/App/Issue/rejectIssueModal',
controller: 'rejectIssueModalCtrl',
controllerAs: 'rejectModal',
size: 'lg',
resolve: {
issue: self.issueData
}
});
rejectModal.result.then(function (reason) {
issueSvc
.rejectIssue(self.issueData.id, reason)
.then(function (issueStatus) {
changeIssueStatus(issueStatus.code);
getIssue();
}, requestFailed);
});
};
and html code
<div>
<span class="right" ng-bind="$root.reasono"></span>
</div>
As you can see I tried to use $rootScope. I can console.log the reason but I cant make it to show in this html. Any help?
We're missing some context, but I believe this is your problem:
self.reject = function ($rootScope) {
$modalInstance.close(self.reason);
console.log(self.reason);
};
$rootScope.reasono = self.reason;
Assuming self.reason is bound to the input in your modal, it won't be defined outside of the reject callback - that's the nature of async. You're able to log to console because you're doing so within the callback.
Define $rootScope.reasono inside of the callback like so:
self.reject = function () {
$modalInstance.close(self.reason);
console.log(self.reason);
$rootScope.reasono = self.reason;
};
Edited to show that $rootScope should be removed as a named parameter in the reject function definition.
Using root scope is not recommended. For this reason it is recommended to create a service for intercommuncation with variable to store reject reason, then inject this service for each controller - that way you will be able to read/write reason from different controllers.
Below is the code for part of a controller I'm trying to unit test. I hate to post something that I have not yet attempted but I'm very lost as to how I should start testing this piece of code. I think part of the problem is I'm not sure how modalInstance is executed. If you can see a good entry point please let me know.
$scope.confirmDelete = function (account) {
var modalInstance = $modal.open({
templateUrl: '/app/accounts/views/_delete.html',
controller: function (global, $scope, $modalInstance, account) {
$scope.account = account;
$scope.delete = function (account) {
global.setFormSubmitInProgress(true);
accountService.deleteAccount(global.activeOrganizationId, account.entityId).then(function () {
global.setFormSubmitInProgress(false);
$modalInstance.close();
},
function (errorData) {
global.setFormSubmitInProgress(false);
});
};
$scope.cancel = function () {
global.setFormSubmitInProgress(false);
$modalInstance.dismiss('cancel');
};
},
resolve: {
account: function () {
return account;
}
}
});
To start with, to make it properly testable, you might want to extract the nested controller to a first-class controller in the same angular module as this.
You can then test this current controller ( call it CtrlA) up to the point of confirmDelete and then test the newly extracted, so to say, 'modal controller' (lets call CtrlB) it separately.
In other words, in your tests for CtrlA, you ought to invoke confirmDelete and expect $modal.open toHaveBeenCalled. Note that in these tests, you would mock $modal.open:
spyOn(modal, 'open');
Under the hood, angular-ui-bootstrap instantiates a modal and sets up all the references correctly so that $modalInstance injected in CtrlB is same as modalInstance in your CtrlA that $modal.open returns. Since this is third party code, you need not 'test' the validity of this claim through your unit tests. You unit tests would simply assume that $modalInstance CtrlB has received is same as that created in CtrlA.
All that is left now is for you to test that each of the scope methods in CtrlB behave the way you them to. For that, you would create a spy object and inject it into the CtrlB as $modalInstance and you'd go about writing test as if you were writing tests for any ordinary controller.
For example, invoke cancel and test whether the spy $modalInstance.dismiss has been called (and so forth)
Best of luck!
Is it possible to instantiate a controller in AngularJS and pass arguments to its constructor like in OOP ? I can't figure out how to refactor 3 identical controller with just variables name and content which change...
Thanx.
If you have 3 separate sections on the page that have very similar controller code, it sounds like you should consider using a directive. Even if you don't need to control the DOM directly (which is the classic reason to use directive), and only need the standard Angular data-bindings, then this is a nice way to reuse controllers in different contexts by the attributes set on the directive.
You can see a working plunkr at
http://plnkr.co/edit/qclp6MOxGWP7Ughod4T8?p=preview
But the key point is directives can bind-to variables in their parent scope's controller. Say, in the parent scope you have 3 variables, so:
$scope.myVariable1 = 'Value 1';
$scope.myVariable2 = 'Value 2';
$scope.myVariable3 = 'Value 3';
Then you can setup 3 instances of the directive in the template:
<my-directive my-param="myVariable1"></my-directive>
<my-directive my-param="myVariable2"></my-directive>
<my-directive my-param="myVariable3"></my-directive>
Then each directive can use the variable in the 'my-param' attribute
scope: {
'myParam':'='
}
The '=' means that in the scope of the directive you have a variable, called 'myParam', that is equal (+ bound to) the variable specified by the 'my-param' attribute on the directive. So on the template of the directive, you can use:
<div>Value of parameter: {{myParam}}</div>
And in the controller of the directive, you have access to is as:
$scope.myParam
And should then be able to customise its behaviour based on that instance's myParam.
You can create services with a common interface and then pass the corresponding services to each controller through dependency injection. In my code this is the case of table controllers where the only thing that changes is the data source:
Imagine you have some code that looks like this
angular.module('myapp').controller('TableACtrl', ['$scope', function($scope) {
$scope.data = // get the data from some source
$scope.someFunction = function () { ... };
$scope.someOtherFunction = function () { ... };
}]);
angular.module('myapp').controller('TableBCtrl', ['$scope', function($scope) {
$scope.data = // get the data from some other source
$scope.someFunction = function () { ... };
$scope.someOtherFunction = function () { ... };
}]);
It looks something like:
var tableCtrl = function($scope, dataSource) {
$scope.data = dataSource.getData();
$scope.someFunction = function () { ... };
$scope.someOtherFunction = function () { ... };
};
angular.module('myapp')
.controller('TableACtrl', ['$scope', 'dataSourceA', tableCtrl])
.controller('TableBCtrl', ['$scope', 'dataSourceB', tableCtrl])
angular.module('myapp')
.service('dataSourceA', function() {
return {
getData: function() { ... }
};
});
angular.module('myapp')
.service('dataSourceB', function() {
return {
getData: function() { ... }
};
});
I would still put things within a module, so you don't pollute the global window. But that's a different issue.