I have the following AngularJS function called editlocation that opens a Modal window that I can edit three pieces of data.
After the result I want to be able to run plotmarkers this is used in another instance of an ng-click. I have tried the following but it doesn't work. I also tried placing it inside the ModalInstanceCtrl2 controller too, but no luck.
$scope.editlocation = function (locations) {
var locationToEdit = locations;
var modalInstance = $modal.open({
templateUrl: 'template/modal-edit-marker.html',
controller: ModalInstanceCtrl2,
resolve: {
locations: function () {
return locationToEdit;
}
},
scope: $scope.$new()
});
modalInstance.result.then(function (selectedItem) {
locationToEdit.title = selectedItem.title;
locationToEdit.gps = selectedItem.gps;
locationToEdit.desc = selectedItem.desc;
$scope.plotmarkers;
}, function () {
console.log('Modal dismissed at: ' + new Date());
});
};
$scope.plotmarkers = function() {
//things will happen here
};
You aren't actually calling the function here. Try this:
$scope.plotmarkers();
Unless plotmarkers in in the same controller/scope (ModalInstanceCtrl2) you are calling it that won't work. a far better approach would be to emit or broadcast an event that would let know every interested party that markers should be plotted you can do it either using the controllers scope or the $rootScope if you have it injected.
Related
I have use requirejs, angularamd and ui.bootstrap in my project. In case of popup form I have $uibModal from ui.bootstrap. But I cannot pass a parameter "items" from resolve. How can I inject parameters for controller which have resolved dynamically?
function open(size, parentSelector) {
var parentElem = parentSelector ?
angular.element($document[0].querySelector('.grid ' + parentSelector)) : undefined;
var modalInstance = $uibModal.open({
animation: vm.animationsEnabled,
ariaLabelledBy: 'modal-title',
ariaDescribedBy: 'modal-body',
size: size,
appendTo: parentElem,
templateUrl: 'Views/Shared/ColSetting.html',
resolve: {
load: ['$q','$rootScope',function ($q, $rootScope) {
var loadController = "Views/Shared/ColSettingController";
var deferred = $q.defer();
require([loadController], function () {
deferred.resolve(items);
$rootScope.$apply();
});
return deferred.promise;
}]
}
});
This is controller I want to call.
'use strict';
define(['application-configuration', 'ajaxService'], function (app) {
function ColSettingController(items) {
var vm = this;
//vm.content = $rootScope.content;
vm.ok = function () {
//$uibModalInstance.close(vm.selected.item);
};
vm.cancel = function () {
//$uibModalInstance.dismiss('cancel');
};
}
app.register.controller("ColSettingController", ColSettingController);
});
According to ui.bootstrap, resolve property is a map object. The map object contains key/value pairs of:
key – {string}: a name of a dependency to be injected into the controller.
factory - {string|function}:
If string, then it is an alias for a service.
Otherwise if function, then it is injected and the return value is treated as the dependency. If the result is a promise, it is resolved before the controller is instantiated and its value is injected into the controller.
In your case, you are using load, but your controller expect items to be injected, I assume it fails saying it can't find items, right? It is because what you are injecting is load.
You need to change the name of your property in the resolve, load for items.
resolve: {
//change 'load' for 'items' here
items: [....rest of your code goes here....]
Also, it is recommended to use the property $inject before declaring controllers/components and others, something like this:
function ColSettingController(items) {
//some code here
}
ColSettingController.$inject = ['items'];
app.register.controller("ColSettingController", ColSettingController);
Hope it helps
Learning AngularJS is a Work In Progress for me so I just want to understand why/when we should use one over another in particular case below. Is it just matter of taste or more than that? See examples below.
In both cases, when user clicks OK button, create() function of parent controller is called from child controller.
RESOLVE STYLE
CreateController
...
var vm = this;
vm.create = create;
function create() {
console.log('Created!');
}
vm.openCreateModal = function() {
vm.modalInstance = $uibModal.open({
...
resolve: {
create: function() {
return create;
},
// Others if any
}
});
}
...
CreateModalController
...
vm.ok = function() {
create();
$uibModalInstance.close('ok');
};
...
SCOPE STYLE
CreateController
...
var vm = this;
vm.create = create;
function create() {
console.log('Created!');
}
vm.openCreateModal = function() {
vm.modalInstance = $uibModal.open({
...
scope: $scope,
resolve: {
}
});
}
...
CreateModalController
...
vm.ok = function() {
$scope.$parent.vm.create();
$uibModalInstance.close('ok');
};
...
Update
The actual reason why I ask this question is, accessining/injecting parent/root/container like objects of one service/controller in/to another controller/service is considered as a "bad practise" in some languages/frameworks I use.
The idea of resolve is that it will run that first before initializing the rest of your code. Generally you would use resolve in your routing like so:
$routeProvider
.when('/', {
templateUrl: "views/view.html",
caseInsensitiveMatch: true,
resolve: {
load: function() {
localStorage['Location'] = "/View";
}
}
})
In the above example resolve will fire the load function before my controller is ever initialized. On the other hand scope is used to bind directly to something in a controller or directive. You should use scope when triggering functions and binding to values between controllers and directives.
To add to this based on the comments below, if the resolve fails it will reject the modal and the window will not open.
I'm trying to figure out AngularJS directives. I've got the following JSFiddle with an example of something I'm trying to do. https://jsfiddle.net/7smor9o4/
As you can see in the example, I expect the vm.alsoId variable to be equal to vm.theId. In the template vm.theId displays the correct value but vm.alsoId does not.
What am I doing wrong? How could I accomplish my goal.
If it helps the final idea is to execute something as the following:
function directive(service) {
var vm = this;
vm.entity = null;
init();
function init() {
service.getEntity(vm.theId).then(function (entity) {
vm.entity = entity;
});
}
}
As you've noticed, the bindToController bindings are not immediately available in the controller's constructor function (unlike $scope, which are). What you're looking for is a feature introduced with Angular 1.5: Lifecycle Hooks, and specifically $onInit.
You had the right idea; simply replace your init function definition and invocation as follows:
vm.$onInit = function () {
service.getEntity(vm.theId).then(function (entity) {
vm.entity = entity;
});
};
And here is your updated fiddle.
(Alternatively, without this solution, you'd have needed a watch.)
Angular recommends that you bind a controller "only when you want to expose an API to other directives. Otherwise use link."
Here's a working fiddle using the link function.
angular.module('app', [])
.directive('directive', directive);
angular.element(function() {
angular.bootstrap(document, ['app']);
});
function directive() {
return {
restrict: 'E',
scope: {
theId: '<'
},
template: `
alsoId: <span ng-bind="alsoId"></span>
theId: <span ng-bind="theId"></span>`,
link: link
};
}
function link(scope, element, attrs) {
init();
function init() {
scope.alsoId = scope.theId;
}
}
I have two different views but for both of them there is only one controller as they have several things in common. But when the second view is loaded all the data stored in the scope of the first view is lost.
For this problem I found this very nice solution here
This solution looks good only if I have few number of data in scope, which is not the case of mine. I have several data stored in the controller scope. So I wanted to have a way to iterate over the data(only data saved by me not angular's data), but I am not sure how do I iterate over these value. Any thoughts?
i had somewhat similar requirement and i have created a directive for the same purpose
csapp.directive("csDataStore", ["$location", "$sessionStorage", function ($location, $sessionStorage) {
var controllerFunction = ["$scope", function ($scope) {
var enter = function () {
var storedValue = $sessionStorage[$scope.key];
if (angular.isDefined(storedValue)) $scope.value = storedValue;
$scope.onLoad();
};
enter();
var exit = function () {
$sessionStorage[$scope.key] = $scope.value;
};
$scope.$on("$destroy", function () { exit(); });
}];
return {
restrict: "E",
scope: { key: '#', value: '=', onLoad: '&' },
controller: controllerFunction
};
}]);
i use the directive like this
<cs-data-store key="stkh.view" value="filter" on-load="loadData()"></cs-data-store>
so what we have here are 3 parameters: key/value and then what to do on loading that value in the scope... so a callback function on-load, which would be called after the the key has been loaded in the $scope
i used $sessionStorage, to keep values even between page refresh...
JQuery to Directive
I want to call a method from the scope of this directive but can't seem to work it out (if possible).
$("my-directive").first().scope().loadData();
Directive Looks Something Like This
I would like to call the loadData function from the directive code below.
app.directive("myDirective", function () {
return {
restrict: "E",
templateUrl: "..."
scope: {},
controller: function ($scope, $element, $attrs) {
var self = this;
$scope.loadData = function () {
...
};
}
};
});
Scope is accessible inside the directive
You can get any child of the element of which directive is applied and get scope of it.
$('my-directive').first().children(":first").scope().loadData()
Strajk's answer is correct!
When Code is Added Dynamically setTimeout Needed
In the following example detail row has a nested directive (random-testees). I get the scope from that to dynamically compile the directive (dynamic and late-bound). The setTimeout is needed because it seems to take a bit before the
var detailRow = e.detailRow;
// Compile the directive for dyanmic content.
angular.element(document).injector().invoke(function ($compile) {
var scope = angular.element(detailRow).scope();
$compile(detailRow)(scope);
});
// Get some data from directive.
var testId = detailRow.find("random-testees").attr("testid");
// Wait, and call method on the directive.
setTimeout(function () {
var randomTesteesScope = $("random-testees").first().children(":first").scope();
randomTesteesScope.loadTestees(this);
}.bind(testId),200);
Not Very Confident About This
This seems a little brittle since I was getting mixed results with a 100 ms timeout sometimes and error when the randomTesteesScope returned undefined.