AngularJS UI Modal and select doesn't update the scope values - angularjs

I am having a lot of trouble trying to save values from the modal component available in Angular UI.
Here is the page controller that calls the modal dialog
$scope.sourceSchema = [];
$scope.targetSchema = [];
$scope.apiDefinition = [];
$scope.availableSchemas = availableSchemas.get();
$scope.addComponent = function (type) {
$scope.$broadcast('addComponent', [type]);
var templateUrl = "";
var controller = null;
var resolve = null;
var componentSchema = [];
switch (type) {
case "sourceSchema":
templateUrl = 'source-schema.tpl.html';
controller = 'SourceCtrl';
componentSchema = $scope.sourceSchema;
break;
case "targetSchema":
templateUrl = 'target-schema.tpl.html';
controller = 'TargetCtrl';
componentSchema = $scope.targetSchema;
break;
case "api":
templateUrl = 'api.tpl.html';
controller = 'SourceCtrl';
componentSchema = $scope.apiDefinition;
break;
}
var modalInstance = $modal.open({
templateUrl: templateUrl,
controller: controller,
resolve: {
existingSchemas: function () {
return $scope.availableSchemas;
}
}
});
modalInstance.result.then(function (selectedItem) {
componentSchema.push(selectedItem);
}, function () {
// $log.info('Modal dismissed at: ' + new Date());
});
};
Here is the SourceCtrl that controls one of the modal dialogs I am using:
.controller("SourceCtrl", function ($scope, $modalInstance, existingSchemas) {
$scope.existingSchemas = existingSchemas;
$scope.sourceSchema = "";
$scope.ok = function () {
$modalInstance.close($scope.sourceSchema);
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
$scope.$watch('sourceSchema', function(newValue, oldValue) {
console.log(newValue, oldValue);
})
})
And finally here is the template for this controller (SourceCtrl).
<div class="modal-header">
<h3>New Source Schema</h3>
</div>
<div class="modal-body">
<div class="row">
<div class="col-xs-3">
<label for="schema-source">Source</label>
</div>
<div class="col-xs-9">
<select name="sourceSchema" ng-model="sourceSchema" ng-options="s as s.name for s in existingSchemas">
<option value="">-- choose source --</option>
</select>
</div>
<h5>Name: {{sourceSchema.name}}</h5>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-primary" ng-click="ok()">OK</button>
<button class="btn btn-warning" ng-click="cancel()">Cancel</button>
</div>
The funny thing is that when I change the value in the select, the {{sourceSchema.name}} line does show the correct name of the schema, however the changes do not get reflected in the controller and the actual value is not being passed on. I have used a watch to detect when something gets changed and apparently it doesn't. But the value does get changed otherwise why would it get displayed when I select it in the dropdown list.

Make sure that you've got a dot in your ngModel expression - that is - that you are binding to an object property and not directly to the scope. Something like:
.controller("SourceCtrl", function ($scope, $modalInstance, existingSchemas) {
$scope.existingSchemas = existingSchemas;
$scope.source = {
schema: ''
};
$scope.ok = function () {
$modalInstance.close($scope.source.schema);
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
$scope.$watch('source.schema', function(newValue, oldValue) {
console.log(newValue, oldValue);
})
})
And then, in your markup:
<select name="sourceSchema" ng-model="source.schema" ng-options="s as s.name for s in existingSchemas">
<option value="">-- choose source --</option>
</select>
If you can provide a plunker I can help you fixing the code.

Related

Pass object back from ionic model service

I have created a factory for a modal that pulls in an array(list) and I have a ng-click where I get the index and then get the object I want to pass the object back to my controller so I can then use it.
I not sure how I will pass the object back to the controller.
This is the function in my service that fires the open() for the modal and I am passing it the model that i receive from a rest call.
function CopyModalService($ionicModal, $rootScope) {
var $scope = $rootScope.$new(),
myModalInstanceOptions = {
scope: $scope,
animation: 'slide-in-up'
};
return {
open: open
};
function open(model) {
$ionicModal.fromTemplateUrl('templates/copy-modal.html',
myModalInstanceOptions)
.then(function (modalInstance) {
$scope.model = model;
$scope.addCopyCertificate = function(index){
console.log('click', $scope.model[index]);
};
$scope.close = function () {
closeAndRemove(modalInstance);
};
return modalInstance.show(model);
});
}
This is the html in the modal so you can get the picture
<ul class="list">
<li class="item row" ng-repeat="item in model">
<span class="col col-67">{{item.installerReference}}</span>
<span class="col">
<button class="button button-calm button-calm-search ion-ios-arrow-down"
ng-click="addCopyCertificate($index)"></button>
</span>
</li>
</ul>
When I click the button in the html addCopyCertificate() it all appears fine but how do I pass that back to the controller.
In my controller I am using it like this: (which is working)
if (res.length) {
CopyModalService.open(res);
}else{
Alert.showAlert('No matching certificates');
....
}
what about $rootScope.$broadcast? something like:
function CopyModalService($ionicModal, $rootScope) {
var $scope = $rootScope.$new(),
myModalInstanceOptions = {
scope: $scope,
animation: 'slide-in-up'
};
return {
open: open
};
function open(model) {
$ionicModal.fromTemplateUrl('templates/copy-modal.html',
myModalInstanceOptions)
.then(function (modalInstance) {
$scope.model = model;
$scope.addCopyCertificate = function(index){
console.log('click', $scope.model[index]);
$rootScope.$broadcast('update-controller',$scope.model[index]);
};
$scope.close = function () {
closeAndRemove(modalInstance);
};
return modalInstance.show(model);
});
}
and then when you want to get the value ..attach the listener with $rootScope.$on('') (or better $scope.$on()) ..something like
if (res.length) {
CopyModalService.open(res);
$scope.$on('update-controller',function(event, data){
console.log(data);
});
}else{
Alert.showAlert('No matching certificates');
....
}

Display dropdown on tick of checkbox

// I have written java code to fetch data from mongo-db. What i need to do is on tick of checkbox button i have to display those data in drop-down menu using angular-js and bootstrap. Nothing is happening after doing these code.
.html page
<div ng-controller="release">
<div class="col-md-2 col-centered col-fixed">
<label for="cloneRelease" translate="release.form.cloneRelease">CloneRelease</label>
</div>
<div>
<input type="checkbox" ng-model="ticked">
<div class="dropdown-menu" ng-repeat="release in releaseName" ng-show="ticked">{{release.name}}</div>
</div>
</div>
controller.js
releaseApp.controller('release', function($scope, $location, $http, ReleaseNameService){
$scope.releaseName = [];
init();
function init(){
ReleaseNameService.getReleaseName().then(function(data){
$scope.releaseName = data;});
console.log('inside controller: '+$scope.releaseName);
}
});
service.js
releaseApp.factory('ReleaseNameService', function($http){
var releaseName = [];
var factory = {};
factory.getReleaseName = function(){
return $http.get('release/fetchAllReleaseDetails').then(function(response){
releaseName = response.data;
console.log('inside service method'+ releaseName);
return releaseName;
});
};factory;
});
It is simple, u need to bind checkbox with ng-model:
<input type="checkbox" ng-model="ticked">
If its ticked $scope.ticked return true, else return false. If true show data, if false hide it (with ng-show)
Here is an example in jsFiddle without css ofc.
http://jsfiddle.net/RLQhh/2282/
UPDATE:
recreateing case with service.
service.js
app.factory('dataService', function ($http) {
var dataObject= {
async: function () {
var promise = $http.get('data/').then(function (response) {
return response;
});
return promise;
}
};
return dataObject;
})
controller.js
$scope.dataTest = [];
$scope.ticketed = false;
var getData = function(){
dataService.async().then(function (d) {
$scope.dataTest = d.data;
});
}
getData();
html
<input type="checkbox" ng-model="ticketed">
<div ng-show="ticketed" ng-repeat="dat in dataTest">
{{dat.name}}
</div>
...this is tested case so it should work with yours
You can make a REST call to fetch the data from your java function and store it in scope.Then you can use ng-repeat to display data in dropdown.
Here is a very good article on how to do it.
http://www.infragistics.com/community/blogs/dhananjay_kumar/archive/2015/06/29/how-to-work-with-the-bootstrap-dropdown-in-angularjs.aspx

How to change select functions in Angular Directive?

http://plnkr.co/edit/pJRzKn2v1s865w5WZBkR?p=preview
I have a large select dropdown form which is repeated in 2 places. The only thing that changes is the first select tag, which has a different function.
<!--
On simple, change ng-change function to functionOne
On advanced, change ng-change function to functionTwo
-->
<select name="name1" ng-change="functionOne('function1')" id="the-id-1">
<select name="name2" ng-change="functionTwo('function2)" id="the-id-2">
<option value="aaa">aaa</option>
<option value="bbb">bbb</option>
<option value="ccc">ccc</option>
</select>
I tried using ng-hide ng-show however there must be a different way to accomplish this.
var app = angular.module('myApp', [])
.directive('termsForm', function() {
return {
templateUrl : "termsForm.html",
restrict : "E",
scope : false,
controller : 'TermsFormController'
}
})
.directive('selectOptions', function() {
return {
templateUrl : "form.html",
restrict : "E",
scope : false
}
})
.controller('TermsFormController',
['$scope',
function($scope) {
var vs = $scope;
vs.hello = "This is the form.";
vs.showingSimple = true;
vs.showingAdvanced = false;
vs.showForm = function(type) {
if (type === 'simple') {
vs.showingSimple = true;
vs.showingAdvanced = false;
} else if (type === 'advanced') {
vs.showingSimple = false;
vs.showingAdvanced = true;
}
}
vs.functionOne = function(msg) {
alert(msg);
}
vs.functionTwo = function(msg) {
alert(msg);
}
}]);
termsForm.html
<ul class="nav nav-tabs">
<button class="btn btn-info" ng-click="showForm('simple')">Simple</button>
<button class="btn btn-info" ng-click="showForm('advanced')">Advanced</button>
</ul>
<p>The select:</p>
<div ng-show="showingSimple" class="simple-form">
<p>Simple</p>
<select-options></select-options>
</div>
<div ng-show="showingAdvanced" class="advanced-form">
<p>Advanced</p>
<select-options></select-options>
</div>
You already have a directive created for your select, that gets you half way there. Now you just need to pass the function in through whats known as the isolated scope.
.directive('selectOptions', function() {
return {
templateUrl : "form.html",
restrict : "E",
scope : {
changeFunc: '&'
}
}
})
This allows you to pass in the function you want to call on the ng-change event:
<select-options changeFunc="function1"></select-options>
<select-options changeFunc="function2"></select-options>
And then in your form.html you simply put
<select name="name2" ng-change="changeFunc()" id="the-id-2">
This way you are basically passing the funciton in as a parameter. Read this blog for a great guide on isolated scopes.
I would just refactor your markup and controller to adapt based on the simple/advanced context.
In your controller, you'd expose a 'generic' on change function for the dropdown, first...
(function () {
'use strict';
angular.module('app').controller('someCtrl', [someCtrl]);
function someCtrl() {
var vm = this;
vm.isSimple = true;
vm.nameChange = function () {
if(vm.isSimple)
functionOne('function1');
else
functionTwo('function2');
}
// Other things go here.
}
})();
...Then, on your view, your select would change to this*:
<select id="someId" name="someName" ng-change="vm.nameChange()" />
*: Assuming you're using controllerAs syntax, that is. If you're not, don't prepend the vm. on the select.

Passing data from ionic/angular modal using separate template html file

I'm developing a simple Ionic mobile app although the answer likely lies with Angular. The app is really simple, displays a list of employees with an Add button which displays a modal, lets the user enter some details, click Save and it's persists the data to a back-end Firebase store. It has 1 controller and a simple service. Initially I had the template html for the modal inside script tags inside the index.html and it all worked fine. When I decided to structure things out and put the modal template in a separate html file, suddenly the data object assigned to ng-modal via the input boxes no longer passes any data to the event handler to save the data, instead it's always undefined. Everything else works as it should, the modal displays ok, the event handlers are calling the right functions etc. The only change is moving the input template to a separate file. I know it's likely something really simple but can't for the life of me work out why and can't find any info about it anywhere else.
Template HTML file for the modal :
<ion-list>
<h1>Add Employee</h1>
<div class="list list-inset">
<ion-item>
<label class="item item-input">
<input type="text" placeholder="Employee Name" ng-model="data.employeeName">
</label>
<label class="item item-input">
<input type="text" placeholder="Employee Age" ng-model="data.employeeAge">
</label>
</ion-item>
<button class="button button-outline button-block button-balanced"
ng-click="addEmployee(true, data)">
Save & Add Another
</button>
<button class="button button-outline button-block button-positive"
ng-click="addEmployee(false, data)">
Save
</button>
<button class="button button-outline button-block button-assertive"
ng-click="closeAddModal()">
Cancel
</button>
</ion-list>
</ion-modal-view>
addEmployee event - data parameter is now always undefined. Worked fine with embedded template :
$scope.addEmployee = function(retainModal, data) {
var employee = {employeeName:data.employeeName,
employeeAge:data.employeeAge};
employeeService.saveEmployee(employee);
if (! retainModal) {
$scope.closeAddModal();
};
data.employeeName = "";
data.employeeAge = "";
};
Based on this question and other needs I create a service that can be useful.
See this post: Ionic modal service or see in operation: CodePen
(function () {
'use strict';
var serviceId = 'appModalService';
angular.module('app').factory(serviceId, [
'$ionicModal', '$rootScope', '$q', '$injector', '$controller', appModalService
]);
function appModalService($ionicModal, $rootScope, $q, $injector, $controller) {
return {
show: show
}
function show(templateUrl, controller, parameters) {
// Grab the injector and create a new scope
var deferred = $q.defer(),
ctrlInstance,
modalScope = $rootScope.$new(),
thisScopeId = modalScope.$id;
$ionicModal.fromTemplateUrl(templateUrl, {
scope: modalScope,
animation: 'slide-in-up'
}).then(function (modal) {
modalScope.modal = modal;
modalScope.openModal = function () {
modalScope.modal.show();
};
modalScope.closeModal = function (result) {
deferred.resolve(result);
modalScope.modal.hide();
};
modalScope.$on('modal.hidden', function (thisModal) {
if (thisModal.currentScope) {
var modalScopeId = thisModal.currentScope.$id;
if (thisScopeId === modalScopeId) {
deferred.resolve(null);
_cleanup(thisModal.currentScope);
}
}
});
// Invoke the controller
var locals = { '$scope': modalScope, 'parameters': parameters };
var ctrlEval = _evalController(controller);
ctrlInstance = $controller(controller, locals);
if (ctrlEval.isControllerAs) {
ctrlInstance.openModal = modalScope.openModal;
ctrlInstance.closeModal = modalScope.closeModal;
}
modalScope.modal.show();
}, function (err) {
deferred.reject(err);
});
return deferred.promise;
}
function _cleanup(scope) {
scope.$destroy();
if (scope.modal) {
scope.modal.remove();
}
}
function _evalController(ctrlName) {
var result = {
isControllerAs: false,
controllerName: '',
propName: ''
};
var fragments = (ctrlName || '').trim().split(/\s+/);
result.isControllerAs = fragments.length === 3 && (fragments[1] || '').toLowerCase() === 'as';
if (result.isControllerAs) {
result.controllerName = fragments[0];
result.propName = fragments[2];
} else {
result.controllerName = ctrlName;
}
return result;
}
} // end
})();
Usage:
appModalService
.show('<templateUrl>', '<controllerName> or <controllerName as ..>', <parameters obj>)
.then(function(result) {
// result from modal controller: $scope.closeModal(result) or <as name here>.closeModal(result) [Only on template]
}, function(err) {
// error
});
You can use another service to centralize the configuration of all modals:
angular.module('app')
.factory('myModals', ['appModalService', function (appModalService){
var service = {
showLogin: showLogin,
showEditUser: showEditUser
};
function showLogin(userInfo){
// return promise resolved by '$scope.closeModal(data)'
// Use:
// myModals.showLogin(userParameters) // get this inject 'parameters' on 'loginModalCtrl'
// .then(function (result) {
// // result from closeModal parameter
// });
return appModalService.show('templates/modals/login.html', 'loginModalCtrl as vm', userInfo)
// or not 'as controller'
// return appModalService.show('templates/modals/login.html', 'loginModalCtrl', userInfo)
}
function showEditUser(address){
// return appModalService....
}
}]);
You need to attach your models to the scope:
$scope.data.employeeName = "";
$scope.data.employeeAge = "";
...and similar every time you reference them.

Using $watch for two variables angularjs

I have a pop up screen in which a user require to select from two drop down lists.
After the selections were completed i return the selections to the service and save them in an object.
app.service('OriginalService', [ '$modal',
function ($modal) {
var that = this;
this.filtersMananger = { // my-ng-models for the two drop-down-lists
firstFilter: "",
secondFilter: ""
};
this.openDialog = function(){
var modalInstance = $modal.open({
templateUrl: 'ModalScreen.html',
controller: 'ModalController',
resolve: {
filtersManagerObject: function () {
return that.filtersMananger;
}
}
});
modalInstance.result.then(function (filtersMananger) {
that.filtersMananger.firstFilter = filtersMananger.selectedFirstFilter;
that.filtersMananger.secondFilter = filtersMananger.selectedSecondFilter;
}, function () {
});
};
}
]);
The pop up html:
<div class="data-filters-container">
<div class="data-filter">
<label for="filter-data-drop-down">FIRST FILTER</label>
<select name="filterDataDropDown" ng-model="filtersMananger.selectedFirstFilter" ng-options="filter.value as filter.name for filter in filterDropDownItems"></select>
</div>
<div class="data-filter col-xs-4">
<label for="filter-data-drop-down">SECOND FILTER</label>
<select name="filterDataDropDown" ng-model="filtersMananger.selectedSecondFilter" ng-options="filter.value as filter.name for filter in filterDropDownItems"></select>
</div>
However, this change is important and i have to call to the controller which knows many other services to send them information regarding this change.
In order to do it i used a watch function in the controller:
$scope.$watch('OriginalService.filtersMananger.firstFilter + OriginalService.filtersMananger.secondFilter', function (newVal, oldVal) {
if (newVal !== oldVal) {
DO SOME LOGIC
}
});
I compare between newVal and oldVal because when the app is uploaded the event is called and we enter to this function.
The problem is that the newVal is contains only the value of the secondVariable.
Is there any idea why the newVal is not contains also the first variable?
Use $watchCollection:
$scope.$watchCollection('[serviceName.Object.firstVariable,serviceName.Object.secondVariable]', function (newValues, oldValues) {
});
Or if you're using angular 1.3 use $watchGroup:
$scope.$watchGroup(['serviceName.Object.firstVariable','serviceName.Object.secondVariable'],function(newValues, oldValues){
})
You could also use ng-change on your select.
The ng-change will call a function that do your logic
<div class="data-filters-container">
<div class="data-filter">
<label for="filter-data-drop-down">FIRST FILTER</label>
<select name="filterDataDropDown" ng-change="checkFilterOne()" ng-model="filtersMananger.selectedFirstFilter" ng-options="filter.value as filter.name for filter in filterDropDownItems"></select>
</div>
<div class="data-filter col-xs-4">
<label for="filter-data-drop-down">SECOND FILTER</label>
<select name="filterDataDropDown" ng-change="checkFilterTwo()" ng-model="filtersMananger.selectedSecondFilter" ng-options="filter.value as filter.name for filter in filterDropDownItems"></select>
</div>
It will call the function when your model will change that is like a $watch.

Resources