How can I pass variable from one function to another in angularJS? - javascript

$scope.openModal = function (page, size) {
console.log(page); // this is working
$uibModal.open({
animation: true,
templateUrl: 'app/pages/servers/newRole.html',
size: size,
resolve: {
items: function () {
return $scope.items;
}
}
});
};
$scope.hello = function() {
console.log(page); // need value of page from above function
var btn = document.createElement("BUTTON");
var t = document.createTextNode("ABC");
btn.classList.add("btn-primary", 'btn-xs', 'btn');
btn.appendChild(t);
document.getElementById('Id1').appendChild(btn);
}
I have tried using a global variable and assigning the page to this but it is saying undefined.
I can access this variable by calling hello(page) inside openModal, but that won't work as it will call hello() when it's not needed.
I have two buttons and calling openModal function on click of 'btn1', and passing page parameter on this button and then there is another button inside that modal 'btn2', calling hello() on click of btn2.

Tl;Dr : use your scope, or use a service.
If those methods are in the same controller:
You should just be able to pass it through a variable:
Initialize it : $scope.data = {};
Push some data in it: $scope.data = anything; or $scope.data.field = anything.
Use it further in your function : $scope.data ... \\ Do anything
If they're not:
You can use an AngularJS service. When you finished your data treatment, you can save it into your service, then getting it back later. View it as a getter/setter for any data you would like. Example:
A dummy AngularJS service:
var service = angular.module('yourService', [])
.factory('$yourService', function () {
var yourdata = {};
return {
setData: function(data) {
yourdata = data;
},
getData: function() {
return data;
}
};
});
return service;
Then, be sure that you inject it into your controller. Into your first function, you can call your service as:
$yourService.setData(anyData);
And get back the data in your second function: $yourService.getData();

$scope.openModal = function (page, size) {
console.log(page); // this is working
$uibModal.open({
animation: true,
templateUrl: 'app/pages/servers/newRole.html',
controller: ModalInstanceCtrl, //resolved items can be use in this controller
size: size,
resolve: {
items: function () {
return page; // this will return value
}
}
});
};
angular.module('ui.bootstrap.demo').controller('ModalInstanceCtrl', function ($scope, $uibModalInstance, items) {
$scope.hello = function() {
console.log(items); // Here you can get value
}
});

Related

bootstrap modal not close angular js

I am using UI bootstrap modal dialog box with angular js. Modal is successfully loaded. But when I click YES/NO Button, issued occurred & modal did not close.
Error said, ' $uibModal.close is not a function'.
.directive('confirm', function(ConfirmService) {
return {
restrict: 'A',
scope: {
eventHandler: '&ngClick'
},
link: function(scope, element, attrs){
element.unbind("click");
element.bind("click", function(e) {
ConfirmService.open(attrs.confirm, scope.eventHandler);
});
}
}
})
This is my service
.service('ConfirmService', function($uibModal) {
var service = {};
service.open = function (text, onOk) {
var modalInstance = $uibModal.open({
templateUrl: 'modules/confirmation-box/confirmation-box.html',
controller: 'userListCtrl',
resolve: {
text: function () {
return text;
}
}
});
modalInstance.result.then(function (selectedItem) {
onOk();
}, function () {
});
};
return service;
})
This is my controller file. I am trying to yes/no button inside the controller
.controller('userListCtrl',
['$scope','$http','appConfig','$uibModalInstance', '$uibModal','$log','alertService',
function ($scope,$http, appConfig,$uibModalInstance, $uibModal,$log,alertService) {
$scope.ok = function () {
$uibModalInstance.close();
};
$scope.cancel = function () {
$uibModalInstance.dismiss('cancel');
};
}]);
You're attempting to use two usage methods at once time. There are two (probably more) that you can use the $uibModal, but here are the two that I believe you're intermixing:
1) Service controls the modal and returns a promise, I believe this is what I think you're doing. You do not need to call close/dismiss manually in this instance. You can make the following changes:
service.open = function(text, onOK) {
var modalInstance = $uibModal.open({
templateUrl: 'modules/confirmation-box/confirmation-box.html',
controller: 'userListCtrl',
resolve: {
text: function () {
return text;
}
}
});
// Return so you can chain .then just in case. Generally we don't even
// do this, we just return the instance itself and allow the controller to
// decide how to handle results/rejections
return modalInstance.result;
}
In your template file you'd have something like:
<button type="button" ng-click="$close(selectedItem)"></button>
<button type="button" ng-click="$dismiss(readon)"></button>
2) If you want to use the close method directly, then you only need to change the service to:
...
return $uibModal.open({});
then in your controller:
var modal = service.open('confirm');
modal.result.then(...)
modal.close()
Edit - updated with change to op to remove the antipattern as per georgeawg suggestion.

How to pass form data to another page?

im using angularJS v 1.5.6 and want to know how to pass my form data correctly with $location.path.
Here is my code Page A:
<form>
...
<button type="submit" ng-click="submit(formData)">submit</button>
</form>
JS:
app.config(['$routeProvider', function ($routeProvider) {$routeProvider
// Home
.when("/", {
templateUrl: "A.html",
controller: "ACtrl"
})
.when("/B/", {
templateUrl: "B.html",
controller: "BCtrl"
})
//fallback url if nothing matches
.otherwise({
redirectTo: '/'
});
}]);
app.controller('ACtrl', function ( $scope, $location, $http) {
$scope.formData = {};
$scope.submit = function() {
$location.path("/B/" + $scope.formData );
};
});
//controller for B page
app.controller('BCtrl', ['$scope', '$routeParams',
function($scope,$routeParams) {
$scope.formData = $routeParams.formData;
}]);
it is a pretty simple example, but i cant figure out how to solve it :(
By clicking the submit nothing happens. If i remove the $scope from $scope.formData i get a error like: Error: formData is not defined.
The terms in formdata are available, i tested it with console.log($scope.formData) and everything is ok.
here is the link plunker: https://plnkr.co/edit/K5zwcmRRyom5HR4a5Q9o
EDIT
the only issue is now, how to handle the select object correctly in the foreach loop. Need help please
You can do it by creating a service and using setter/getter in order to transfer a variable.
For example like this: https://plnkr.co/edit/IuTXsVLU7dq3TylfnSYP?p=preview
app.service('TransferService', [function(){
var savedData,
service = {
getData: getData,
setData: setData
}
function getData(){
return savedData
}
function setData(data){
savedData = data
}
return service
}])
Don't use location.path...
You could either use a service or use localstorage (or some other browser storage mechanism [sessionStorage, indexdb].
Service Method Below
app.service("SomeService", function () {
var value = null;
this.set = function (val) {
value = val;
return this;
}
this.get = function () {
return value;
}
})
app.controller("ACtrl", function ($scope, SomeService) {
$scope.formData = {};
$scope.submit = function() {
//Assuming you've populated it with some data...
SomeService.set($scope.formData);
$location.path("/B/");
};
})
app.controller("BCtrl", function ($scope, SomeService) {
$scope.formData;
(function () {
//Check that the data is present in the SomeService service.
var dataFromACtrl = SomeService.get();
if (dataFromACtrl) {
$scope.formData = dataFromACtrl;
}
})();
})
Using localStrorage below, could be sessionStorage.
app.controller("ACtrl", function ($scope, SomeService) {
$scope.formData = {};
$scope.submit = function() {
//Assuming you've populated it with some data...
window.localStorage.setItem("form_data", JSON.stringify($scope.form_data));
$location.path("/B/");
};
})
app.controller("BCtrl", function ($scope, SomeService) {
$scope.formData;
(function () {
var dataFromACtrl = window.localStorage.getItem("form_data");
if (dataFromACtrl) {
$scope.formData = JSON.parse(dataFromACtrl);
}
})();
})
Note
Using the localStorage example you would need to do some clean-up, after doing whatever you want to do with that data in Bctrl you'd want to clear the entry in localstorage using either of the below lines of code:
window.localStorage.removeItem("form_data");
delete window.localStorage["form_data"];

Angular scope items not applies after item change

I have items service,items list controller, and item details controller:
.state('dashboard.items', {
url: '/items',
templateUrl: '/js/components/dashboard/items/items.html',
controller:'itemsListCtrl'
})
.state('dashboard.items.details', {
url: '/:id',
templateUrl: '/js/components/dashboard/items/itemDetails.html',
controller: 'itemDetailsCtrl',
resolve:{
items: function (ItemService) {
if(!ItemService.items)
ItemService.getAll().then(function (res) {
ItemService.items = res.data;
});
}
}
})
app.factory('ItemService', function ($http) {
var itemsFactory = {};
itemsFactory.getAll = function () {
return $http.get('/items');
}
itemsFactory.update = function () {
itemsFactory.items[0].name = "sadasd";
}
return itemsFactory;
})
app.controller('itemsListCtrl', function($scope, $state, ItemService){
if(!ItemService.items) {
ItemService.getAll().then(function (res) {
ItemService.items = res.data;
$scope.items = ItemService.items;
});
}else{
$scope.items = ItemService.items;
}
})
app.controller('itemDetailsCtrl', function ($scope, items, ItemService) {
$scope.item = ItemService.items[0];
$scope.item.name = "abc" ;
$scope.update = function(){
ItemService.update();
}
})
I have ng-click button which invokes the edit() function.
I made it simple for the example, when doing the update, and edit the item name, the item that presents in the list doesnt change.
I dont know what I miss here. The list sits in the service, and both controllers use it for their purposes.
What am I doing wrong? What is best practice for this scenario?
Update 1
Found something weird. When I edit the item in the controller initialization, it changes the original value globally. When it happens via the edit() method, it doesn't. What happnes?
Thanks.
$http.get returns a promise that it will return your data, so in .then you can do your stuff and it will execute when done(async)
$http.get('/items').then(function successCallback(response) {
// this callback will be called asynchronously
// when the response is available
return response.data;//this is your data
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});

Unit testing angular-bootstrap $modal

I'm having problems trying to write a jasmine unit test for an Angular-Bootstrap $modal. The exact error is
Expected spy open to have been called with [ { templateUrl : '/n/views/consent.html', controller : 'W2ConsentModal as w2modal', resolve : { employee : Function }, size : 'lg' } ] but actual calls were [ { templateUrl : '/n/views/consent.html', controller : 'W2ConsentModal as w2modal', resolve : { employee : Function }, size : 'lg' } ]
The expected and actual modal options object are the same. What is going on?
Controller
(function () {
'use strict';
angular
.module('app')
.controller('W2History', W2History);
W2History.$inject = ['$scope', '$modal', 'w2Service'];
function W2History($scope, $modal, w2Service) {
/* jshint validthis:true */
var vm = this;
vm.showModal = showModal;
function showModal(employee) {
var modalInstance = $modal.open({
templateUrl: '/n/views/consent.html',
controller: 'W2ConsentModal as w2modal',
resolve: {
employee: function () {
return employee;
}
},
size: 'lg'
});
modalInstance.result.then(function (didConsent) {
// code omitted
});
}
}
})();
Test
describe('W2History controller', function () {
var controller, scope, modal;
var fakeModal = {
result: {
then: function (confirmCallback, cancelCallback) {
//Store the callbacks for later when the user clicks on the OK or Cancel button of the dialog
this.confirmCallBack = confirmCallback;
this.cancelCallback = cancelCallback;
}
},
close: function (item) {
//The user clicked OK on the modal dialog, call the stored confirm callback with the selected item
this.result.confirmCallBack(item);
},
dismiss: function (type) {
//The user clicked cancel on the modal dialog, call the stored cancel callback
this.result.cancelCallback(type);
}
};
var modalOptions = {
templateUrl: '/n/views/consent.html',
controller: 'W2ConsentModal as w2modal',
resolve: {
employee: function () {
return employee;
}
},
size: 'lg'
};
beforeEach(function () {
module('app');
inject(function (_$controller_, _$rootScope_, _$modal_) {
scope = _$rootScope_.$new();
modal = _$modal_;
spyOn(modal, 'open').and.returnValue(fakeModal);
controller = _$controller_('W2History', {
$scope: scope,
$modal: modal,
w2Service: w2Srvc
});
});
});
it('Should correctly show the W2 consent modal', function () {
var employee = terminatedaccessMocks.getCurrentUserInfo();
controller.showModal(employee);
expect(modal.open).toHaveBeenCalledWith(modalOptions);
});
});
Try this:
describe('W2History controller', function () {
var controller, scope, modal;
var fakeModal = {
result: {
then: function (confirmCallback, cancelCallback) {
//Store the callbacks for later when the user clicks on the OK or Cancel button of the dialog
this.confirmCallBack = confirmCallback;
this.cancelCallback = cancelCallback;
}
},
close: function (item) {
//The user clicked OK on the modal dialog, call the stored confirm callback with the selected item
this.result.confirmCallBack(item);
},
dismiss: function (type) {
//The user clicked cancel on the modal dialog, call the stored cancel callback
this.result.cancelCallback(type);
}
};
var modalOptions = {
templateUrl: '/n/views/consent.html',
controller: 'W2ConsentModal as w2modal',
resolve: {
employee: jasmine.any(Function)
},
size: 'lg'
};
var actualOptions;
beforeEach(function () {
module('plunker');
inject(function (_$controller_, _$rootScope_, _$modal_) {
scope = _$rootScope_.$new();
modal = _$modal_;
spyOn(modal, 'open').and.callFake(function(options){
actualOptions = options;
return fakeModal;
});
controller = _$controller_('W2History', {
$scope: scope,
$modal: modal
});
});
});
it('Should correctly show the W2 consent modal', function () {
var employee = { name : "test"};
controller.showModal(employee);
expect(modal.open).toHaveBeenCalledWith(modalOptions);
expect(actualOptions.resolve.employee()).toEqual(employee);
});
});
PLUNK
Explanation:
We should not expect the actual resolve.employee to be the same with the fake resolve.employee because resolve.employee is a function which returns an employee (in this case the employee is captured in closure). The function could be the same but at runtime the returned objects could be different.
The reason your test is failing is the way javascript compares functions. Take a look at this fiddle. Anyway, I don't care about this because we should not expect function implementations. What we do care about in this case is the resolve.employee returns the same object as we pass in:
expect(actualOptions.resolve.employee()).toEqual(employee);
So the solution here is:
We expect everything except for the resolve.employee:
var modalOptions = {
templateUrl: '/n/views/consent.html',
controller: 'W2ConsentModal as w2modal',
resolve: {
employee: jasmine.any(Function) //don't care about the function as we check it separately.
},
size: 'lg'
};
expect(modal.open).toHaveBeenCalledWith(modalOptions);
Check the resolve.employee separately by capturing it first:
var actualOptions;
spyOn(modal, 'open').and.callFake(function(options){
actualOptions = options; //capture the actual options
return fakeModal;
});
expect(actualOptions.resolve.employee()).toEqual(employee); //Check the returned employee is actually the one we pass in.
This is a pass by reference vs pass by value issue. The resolve.employee anonymous function used in $modal.open:
var modalInstance = $modal.open({
templateUrl: '/n/views/consent.html',
controller: 'W2ConsentModal as w2modal',
resolve: {
employee: function () {
return employee;
}
},
size: 'lg'
});
is not the same (by reference) as the resolve.employee anonymous function in your test:
var modalOptions = {
templateUrl: '/n/views/consent.html',
controller: 'W2ConsentModal as w2modal',
resolve: {
employee: function () {
return employee;
}
},
size: 'lg'
};
Your test should be:
resolve: {
employee: jasmine.any(Function)
}
If it's essential that the resolve function be tested, you should expose it somewhere where you can get a reference to the same function in your tests.
I am not sure if this will help you now, but when you spy on something you can get the argument that is passed to the $uibModal.open spy, you can then call that function to test that it returns what is in the resolve method.
it('expect resolve to be have metadataid that will return 9999', () => {
spyOn($uibModal, 'open');
//add test code here that will call the $uibModal.open
var spy = <jasmine.Spy>$uibModal.open;
var args = spy.calls.argsFor(0);
expect(args[0].resolve.metadataId()).toEqual(9999);
});
***** my code is using typescript, but this works for me.**
I have come across the same scenario. I have come across the problem with the below given solution
//Function to open export modal
scope.openExportModal();
expect( uibModal.open ).toHaveBeenCalledWith(options);
expect( uibModal.open.calls.mostRecent().args[0].resolve.modalData() ).toEqual(modalData);
Hope this may help if you want a quick fix.

Angularjs How to communicate between two modals

What I am trying to do here is:
Type in the new language name and click "Add" button, the new language will be added into the existing object.
For example: the existing object: {"default": "English"}, When I type in "German", a new object is added like this: {"default": "English", "German": "German"}
Here is my PLUNKER.
Could someone help me on that? Thanks so much, I will appreciate!
I would prefer to use events. Just subscribe one piece on some event like:
$rootScope.$on('myEvent', function(event, info){
// do something
});
And another one will fire it:
scope.$broadcast('myEvent', info);
The system glitched when I was trying to save your plunkr or I don't have a permission so here the code:
var app = angular.module('plunker', ['ui.bootstrap']);
app.factory('Data', function(){
var data =
{
Language: ''
};
return {
setLanguage: function(language) {
data.Language = language;
}
}
})
var ModalDemoCtrl = function ($scope, $modal, $log) {
$scope.languages = {"sch": "Simple Chinese"};
$scope.$on('newLanguageAdded', function(e, lang){
$scope.languages[lang] = lang;
});
$scope.open = function () {
var modalInstance = $modal.open({
templateUrl: 'myModalContent.html',
controller: ModalInstanceCtrl,
resolve: {
languages: function () {
return $scope.languages;
},
newLanguage: function () {
return $scope.newLanguage;
}
}
});
};
};
// Please note that $modalInstance represents a modal window (instance) dependency.
// It is not the same as the $modal service used above.
var ModalInstanceCtrl = function ($scope, $modal, $modalInstance, languages, newLanguage) {
$scope.languages = languages;
$scope.ok = function () {
$modalInstance.close($scope.languages);
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
$scope.openDialog = function () {
var modalInstance = $modal.open({
templateUrl: 'addNewLanguageDialog.html',
controller: AddNewLanguageCtrl,
});
}
var AddNewLanguageCtrl = function ($scope, $rootScope, $modalInstance, Data){
$scope.newValue = {text: ''};
$scope.$watch('newLanguage', function(newValue) {
if(newValue) Data.setLanguage(newValue);
});
$scope.add = function () {
alert($scope.newValue.text);
$rootScope.$broadcast('newLanguageAdded', $scope.newValue.text);
$modalInstance.close($scope.languages);
}
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
}
}
};
You can just copy this piece into plunkr instead yours.
Also change the layout:
<div class="modal-body">
<input ng-model="newValue.text">
</div>
Let me know if something doesn't work
You need to use a service, by definition singletons, and inject it in both models, adding a watch to the array in the service and updating accordingly in the scope of every model, from the values in the service.
An angular-ui way to achieve what you need would be to use these two basic methodologies found in the angular-ui documentation. See associated plunker for the answer below.
First is to use the close(result) inside the Instance Controller of the modal which updates the result promise property of the Instance Controller
Second is to use the result promise property to get the result(s) passed on to the close() method
Inside The AddNewLanguageCtrl is something like this
$scope.data = {newLanguage: ""};
$scope.add = function() {
$scope.close(data.newLanguage);
};
Inside the your addNewLanguageDialog.html script template
instead of using
<input ng-model="newLanguage">
use this
<input ng-model="data.newLanguage">
This is because whenever a modal is created, a new scope is created under the $rootScope(default) if a scope is not passed on to the options when the $modal.open() is invoked as stated in the angular-ui documentation. If you use newLanguage as the model then it won't receive any updates inside the AddNewLanguageCtrl. You can read this to get a better understanding of what I'm talking about regarding scopes
Inside the first modal ModalInstanceCtrl is something like this
$scope.newLanguages = [];
$scope.openDialog = function () {
var modalInstance = $modal.open({
templateUrl: 'addNewLanguageDialog.html',
controller: AddNewLanguageCtrl,
});
modalInstance.result.then(function(newLanguage) {
if(newLanguage)
$scope.newLanguages.push(newLanguage);
});
};
And then in your ModalDemoCtrl
$scope.languages = [];
$scope.open = function () {
var modalInstance = $modal.open({
templateUrl: 'myModalContent.html',
controller: ModalInstanceCtrl
});
modalInstance.result.then(function(languages) {
$scope.languages = $scope.languages.concat(languages);
});
};

Categories

Resources