Trying to get a string from Angular UI Modal via resolve object - angularjs

I'm using the Angular UI modal directive: http://angular-ui.github.io/bootstrap/
I'm trying to pass a string from my modal open() function to my modal controller via the resolve object. I feel like I'm doing the right thing, but somehow it isn't working.
Modal open() function:
$scope.showCommentModal = function () {
var modalInstance = $modal.open({
templateUrl: "templates/text-entry-modal.html",
controller: "TextEntryModalCtrl",
resolve: {
value: function () {
alert("VALUE");
return "Hello"
}
}
});
};
Modal controller:
.controller("TextEntryModalCtrl", ["$scope", "$modalInstance", function ($scope, $modalInstance, value) {
alert(value);
$scope.value = value;
$scope.cancel = function () {
$modalInstance.dismiss("cancel");
};
}]);
The alert in the open function shows every time, right before the alert from the controller, so it's doing something, but when it gets to the controller, value is undefined.
One thing to note is that these controllers are not global variables. The example in the link above is using those, but that's not best practice for us.
Also, I have read this post: Angular-ui modal, sending data into modal controller from $http
I feel like this is very close to the answer I'm looking for, but I don't think I'm waiting on a promise to resolve in this case, am I? As far as I can tell, our implementations are very similar, but again I'm not using global variables but they are. Or maybe I just don't understand what's actually going on here. Of course, I don't have enough points to just ask that, so here I am...

Looking at the first line of the modal controller, it appears the array that is the second parameter is missing "value", as the third item in the array (just before the final function in the array). It should be:
.controller("TextEntryModalCtrl", ["$scope", "$modalInstance", "value", function ($scope, $modalInstance, value) {
This particular bit of syntax is using the controller() method to add a constructor function to the TextEntryModalCtrl controller using Angular's inline injection annotation to explicitly specify both the dependencies of your controller along with the constructor function that will ultimately consume those (injected) dependencies.
Though your constructor function was written to consume 3 dependencies, you had only explicitly listed the first 2 dependencies so the value dependency was lost.
https://docs.angularjs.org/guide/controller#setting-up-the-initial-state-of-a-scope-object
https://docs.angularjs.org/guide/di#inline-array-annotation

Related

Call Angular directive controller method from another controller

I have a directive which is associated with one controller and the functions in my controller defined as
MyFormController.prototype.addNewRow = function addNewRow() {
//Adding row code
};
I want to call this method from another controller, possible ways?
I ve user the service and moved the code into that service which is shared across the controllers, however the service code does the DOM manipulation, and then i guess the next question would be that can we use $compile in a service test case
service or factory is used to share data between controller.so it would be best to define function in service and factory.
demo:
(function() {
angular.module('app', [])
.service('svc', function() {
var svc = {};
svc.method = function() {
alert(1);
}
return svc;
})
.controller('ctrl', [
'$scope', 'svc', function($scope, svc) {
svc.method();
}
]);
})();
You should not!!!
That defeats the whole purpose of modularity.
If possible try to make the function generic and create a service/factory. Now both the places where you need, use the same generic function defined in service and do their stuff.
Otherwise you can also look at events to make changes accordingly.
Look at this blog post:
http://ilikekillnerds.com/2014/11/angularjs-call-controller-another-controller/
Last but the worst solution is (avoid using this, this is literally an aweful way) is catching the element inside directive and getting its scope and taking the function from it.
Example,
var otherControllerFunc = $(".inside-directive").scope().yourfunc;

How to call a non-angular callback within a directive

Is there a correct way to pass a non-angular callback (preferably an anonymous function) name to a directive and then have it activated from the directive. My motivation is to interface from the directive to legacy non-angular code.
Here is what I want:
<my-Directive callback="function (param) {some-legacy-function(param)}">
and then have it activated:
app.directive('myDirective', function($scope) {
return {
scope: {
callback: '&'
}
controller: function($scope) {
$scope.someMethod = function() {
$scope.callback($scope.param);
}
}
});
Note that callback function is not defined on any scope and should preferably anonymous (but I can manage with a non-anonymous function as well).
I found one of doing this by simply passing a string and then doing an eval to call the method but after reading around I understand this approach is problematic (security wise).
Any suggestions would be appreciated.
you can wrap your legacy code inside a injectable service, so you can inject it anywhere (and replace/mock it for tests)
// here set on global for example
var legacyFunction=function(){};
//angular code (has to be loaded after your "old" script
myApp.factory('legacyFunction',function(){
return legacyFunction;
})
//now it is available anywhere in you application (directives, controllers, etc) as an injectable -here in a controller so you can bind it to a $scope for example
myApp.controller('myController',['$scope', 'legacyFunction', function($scope, func){
$scope.myFunc = func
}])

angularjs controller members undefined after promise returns

I have a method in a controller which executes the following code:
this.StockService.GetByInvoicesID(this.SelectedInvoice.ID).success((StockItems) =>
{
this.StockItems = StockItems;
this.CreditNoteStockItems = new Array<ViewModels.CreditNoteStockItemViewModel>();
}
Before this service method is called, all members in the controller are defined. However, once the promise resolves, this.StockItems and this.CreditNoteStockItems are all undefined. Furthermore, the assignment of StockItems is not being reflected in the view. I'm guessing this is a scope issue and the promise is returning into a new scope. This has happened with other methods before, it almost seems to happen at random.
My questions are, can anyone tell me why exactly this is happening, and more importantly, how can I prevent it from occurring?
edit: This is a simplified version of my controller (all thats missing is several members and methods)
edit 2: more info about method in controller
export class CreditNoteController
{
static $inject = ['$scope', '$modalInstance', 'StockService'];
StockService: Services.StockService;
ModalInstance: ng.ui.bootstrap.IModalServiceInstance;
constructor($scope, $modalInstance, StockService: Services.StockService)
{
$scope.vm = this;
this.ModalInstance = $modalInstance;
this.StockService = StockService;
}
InvoicesSelectionChanged()
{
this.StockService.GetByInvoicesID(this.SelectedInvoice.ID).success((StockItems) =>
{
this.StockItems = StockItems;
this.CreditNoteStockItems = new Array<ViewModels.CreditNoteStockItemViewModel>();
});
}
}
The controller is injected through the angular UI modal service open method sitting in another controller:
this.ModalService.open(
{
templateUrl: URL,
controller: Controllers.CreditNoteController,
});
edit 2: The javascript that it generates
CreditNoteModalController.prototype.InvoicesSelectionChanged = function () {
var _this = this;
this.StockService.GetByInvoicesID(this.SelectedInvoice.ID).success(function (StockItems) {
_this.StockItems = StockItems;
_this.CreditNoteStockItems = new Array();
});
};
Thanks
After some back and forth in the comment thread, turns out there were 2 things going on here, so consolidating the info into the answer:
When inspecting 'this' in a debugger in a .ts file, know that you will be seeing the actual 'this' value and not the _this alias that is created in the .js file when you use arrow functions. This can sometimes make it look like variables aren't getting set when in reality, you are inspecting the wrong variable. Setting a watch on _this will show the right one. You also have the option of using console.log on 'this' in the TypeScript code itself, since it will compile to using the _this alias.
ng-options won't work without ng-model

Typescript: this is not pointing to the class

I am using ionicframework together with TypeScript. When I create a modal object as seen below, the modal object is created successfully but inside the callback where it says this.$scope.modal = modal; this is referencing the browser window and hence the $scope is undefined. How can I get this to point to the class?
export class MyCtrl {
constructor(private $scope: any, private $ionicModal) {
// Load the modal from the given template URL
this.$ionicModal.fromTemplateUrl('MyApp/myModal.html', (modal) => {
this.$scope.modal = modal;
}, {
// Use our scope for the scope of the modal to keep it simple
scope: this.$scope,
// The animation we want to use for the modal entrance
animation: 'slide-in-up'
});
In TypeScript, the this inside a lambda doesn't have the traditional behavior of this in JavaScript. If you look at the compiled JS it will be clear what's happening, but basically it will usually be a level higher than you'd expect - in your case the window object.
The easiest way to work around this is to do a var self=this; above the line with the comment and then reference self in your lambda, or else just use normal function syntax such as function(modal) { which preserves the traditional functionality of this from JS.
Dan Quirk answered a similar question that I had here: https://typescript.codeplex.com/workitem/1655
You may also wish to take a look at this: Can I access the other this in a TypeScript lambda?
If you call something like new MyCtrl($myScope, $ionicModal), references to this will correctly point to the new instance of MyCtrl.
What I suspect is that you're passing the MyCtrl class into a place where Angular expects a function, not a class!
Your code actually fails unhelpfully late due to a confusing aspect of JavaScript, namely that constructors can be (mis)used as normal functions without using new, in which case this will point to something higher up.
The solution is to use a normal function instead of a class, try:
function myCtrl($scope: any, $ionicModal) {
// Load the modal from the given template URL
$ionicModal.fromTemplateUrl('MyApp/myModal.html', (modal) => {
$scope.modal = modal;
}, {
// Use our scope for the scope of the modal to keep it simple
scope: $scope,
// The animation we want to use for the modal entrance
animation: 'slide-in-up'
});

AngularJS push of undefined

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

Resources