First type questioner, long time reader. Newbie to Angular.
I am trying to create a popup modal for expanding a text box. (If you have ever dealt with Access, think shift F2). So, I have a form with multiple text boxes which utilize ng-model for two-way binding. I want to open a modal with a <textarea> so the user can type (and see) more than a simple text box.
Currently the data that is bound to each field will open correctly into the textarea on the popup (Passing data TO the modal). However, how do I get the data back to my original form and into the correct field?
mainForm.cshtml
<div class="col-md-4">
<button type="button" ng-click="openTextEditor(vm.firstName)">First Name</button>
<input class="form-control" type="text" name="firstName" ng-class="{'edited':vm.firstName}" ng-model="vm.firstName">
</div>
<div class="col-md-4">
<button type="button" ng-click="openTextEditor(vm.middleName)">Middle Name</button>
<input class="form-control" type="text" name="middleName" ng-class="{'edited':vm.middleName}" ng-model="vm.middleName">
</div>
<div class="col-md-4">
<button type="button" ng-click="openTextEditor(vm.lastName)">Last Name</button>
<input class="form-control" type="text" name="lastName" ng-class="{'edited':vm.lastName}" ng-model="vm.lastName">
</div>
mainForm.js
$scope.openTextEditor = function(textValue) {
$uibModal.open({
templateUrl: '~/textEditorModal.cshtml',
controller: 'textEditorModal as vm',
backdrop: 'static',
resolve: {
textValue: function() {
return textValue;
}
}
});
};
textEditorModal.cshtml
<div>
<div class="modal-header">
<h4 class="modal-title">
</h4>
</div>
<div class="modal-body">
<div busy-if="vm.loading">
<form name="textEditor">
<div class="input-group margin-bottom-10">
<textarea id="textBox" type="text" class="form-control" cols="25" rows="7" placeholder="Type text here...." ng-model="vm.textValue" enter-key="vm.saveModal()"></textarea>
</div>
</form>
</div>
</div>
<div class="modal-footer">
<button type="button" ng-disabled="vm.saving" class="btn btn-default" ng-click="vm.cancelModal()">Cancel</button>
<button type="submit" button-busy="vm.saving" class="btn btn-primary blue" ng-click="vm.saveModal()" ng-disabled="textEditor.$invalid"><i class="fa fa-save"></i> <span>Save</span></button>
</div>
</div>
textEditorModal.js
appModule.controller('common.views.common.textEditorModal', [
'$scope', '$uibModalInstance', 'textValue',
function($scope, $uibModalInstance, textValue) {
var vm = this;
vm.loading = false;
vm.textValue = textValue;
vm.cancelModal = function() {
$uibModalInstance.dismiss();
};
vm.saveModal = function() {
vm.saving = true;
$uibModalInstance.close(vm.textValue);
};
}
]);
Many thanks in advance!
You are almost there! In mainForm.js:
$scope.openTextEditor = function(textValue) {
var modalInstance = $uibModal.open({
templateUrl: '~/textEditorModal.cshtml',
controller: 'textEditorModal as vm',
backdrop: 'static',
resolve: {
textValue: function() {
return textValue;
}
}
});
modalInstance.result.then(function (savedText) {
// when the modal is dismissed with the save button
// do your thing with savedText
}, function () {
// when the modal is dismissed with the cancel button
console.log('Modal dismissed at: ' + new Date());
});
};
In mainForm.js, declare the callback function to get the result :
$scope.openTextEditor = function(textValue) {
$uibModal.open({
templateUrl: '~/textEditorModal.cshtml',
controller: 'textEditorModal as vm',
backdrop: 'static',
resolve: {
textValue: function() {
return textValue;
}
}
})
.result.then(function(returnedInput) {
// here is the problem
});
};
The remaining problem is the param in the openTextEditor function.
You should have a way to set a new value to the input in the original form but as you transmit in the function a string value, it will be more complicated to modify the value.
You should transmit in the openTextEditor function a parameter which allows to retrieve the property to value and not only the value of the property.
For example, you could transmit only the property name in the ng-click function :
<div class="col-md-4">
<button type="button" ng-click="openTextEditor('firstName)">First Name</button>
<input class="form-control" type="text" name="firstName" ng-class="{'edited':vm.firstName}" ng-model="vm.firstName">
</div>
And in the JS side, you could use the property name like that :
$scope.openTextEditor = function(propertyName) {
$uibModal.open({
templateUrl: '~/textEditorModal.cshtml',
controller: 'textEditorModal as vm',
backdrop: 'static',
resolve: {
propertyName: function() {
return propertyName;
}
}
})
.result.then(function(returnedInput) {
vm[propertyName]=returnedInput;
});
};
In this way, you use the property name in the modal dialog to give a label to the input and a way to fill the input in the original form.
Related
The full source code.
I don't understand why $scope not working in my LoginGuideCtrl controller. I try click on login button and should show a <p> with new data but the $scope is not updating...
Don´t forget I'm trying to achieve a modular design.
I have the following code:
guides.js
var guides = angular.module('main.guides', ['ui.router']).config(function ($stateProvider) {
$stateProvider.
state('guides.login', {
url: '/login',
templateUrl: 'modules/guides/views/login.html',
controller: 'LoginGuideCtrl'
}).
...
state('guides.mobile', {
url: '/web',
template: '<div>guildes mobile</div>',
controller: 'ListCtrl'
});
});
controller.js
var guides = angular.module('main.guides');
guides.controller('IndexCtrl', function() {
console.log('Index');
})
.controller('LoginGuideCtrl', function($scope) {
console.log('feck');
$scope.checkLogin = function(){
$scope.message = "Welcome "+$scope.name+"!"
};
})
.controller('ListCtrl', function() {
console.log('List');
})
login.html
<div class="form-group col-sm-2">
<label for="usr">Name:</label>
<input type="text" class="form-control" id="usr" ng-model="name">
</div>
<div class="form-group col-sm-2">
<label for="pwd">Password:</label>
<input type="password" class="form-control" id="pwd" ng-model="password">
</div>
<button type="button" class="btn btn-default" ng-click="checkLogin()">Login</button>
<p ng-model="message"></p>
ng-model is used with <input> tags to capture user input, by two way binding with your model value.
Since a <p> tag does not collect user input, it won't work with ng-model. Instead just do a one way binding with the value using the curly brackets:
<p>{{message}}</p>
I am trying to modify a form in a view by means of a button in my directive's template (template is in another file), this is for a basic CRUD where each item has a delete/edit button.
In order to avoid replicating the form I decided to that on edit's click a function would send the item to the controller in questions in order to be updated with the new information.
But I've been having troubles making the connection, so far I tried changing $root, to $rootScope back and forth and using , $broadcast or $emit.
So how can I send the function onChange my item's information based on the template's button click?
Template:
<strong>{{item.type}}</strong> {{item.description}}
<div class="material-switch pull-right">
<button type="button" class="btn btn-warning btn-circle" ng-show="item.editable" ng-click="onChange()">
<span class="glyphicon glyphicon-edit" ></span>
</button>
<button type="button" class="btn btn-danger btn-circle" ng-controller="View1Ctrl" ng-show="item.editable" ng-click="EliminarItem(item)">
<span class="glyphicon glyphicon-minus" ></span>
</button>
<input ng-model="item.isDone"
id="someSwitchOptionDefault{{itemIndex}}"
name="someSwitchOption001{{itemIndex}}"
type="checkbox" />
<label for="someSwitchOptionDefault{{itemIndex}}" class="label-info"></label>
</div>
Directive:
'use strict';
angular.module('myApp.items.directive', [])
.directive('itemSwitch', [ function() {
return {
restrict: 'A',
scope: {
item: '=',
itemIndex: "="
},
templateUrl: 'templates/itemSwitchTemplate.html',
link : function($scope){
$scope.$broadcast('onChange', item);
}
}
}]);
Controller
.controller('View1Ctrl', ['$scope','itemsService',function($scope,itemsService) {
$scope.items = itemsService.getItems();
$scope.classMap = {GROCERIES:"success",CAR:"danger",UNIVERSITY:"warning",PAYMENTS:"info"};
$scope.newItem = {};
$scope.$on('onChange', function(event, args) {
if ($scope.btnEdit) {
$scope.newItem = args;
} else {
$scope.newItem = {};
}
});
$scope.enableEdit = function (item) {
item.editable = true;
};
$scope.disableEdit = function (item) {
item.editable = false;
};
}]);
View
<div class="col-xs-12">
<div ng-model="currentItem" ng-repeat="item in items" item-switch item="item" item-index="$index" class="notice notice-{{classMap[item.type]}}" ng-mouseover="enableEdit(item)" ng-mouseleave="disableEdit(item)">
</div>
<!-- FORMULARIO -->
<form name = "myForm" class="form-horizontal">
<fieldset>
<div id="legend">
<legend class="">Task</legend>
</div>
<div class="control-group">
<!-- Name-->
<label class="control-label">Name</label>
<div class="controls">
<input type="text" name="itemName" ng-model="newItem.name" placeholder="Task Name" class="input-xlarge" ng-required="true" >
<p class="help-block"></p>
</div>
</div>
<div class="control-group">
<!-- Description -->
<label class="control-label">Description</label>
<div class="controls" >
<input type="text" ng-model="newItem.description" placeholder="Task Description" class="input-xlarge">
<p class="help-block"></p>
</div>
</div>
<div class="control-group">
<!-- Button -->
<div class="controls">
<a class="btn icon-btn btn-success" ng-disabled="myForm.$invalid" ng-click="addOrSaveItem()">
<span class="glyphicon btn-glyphicon glyphicon-save img-circle text-success"></span>Save</a>
</div>
</div>
</fieldset>
</form>
</div>
FiddleJS
Look nd Feel
Using "onChange" as an event name is a poor choice as it is likely to conflict with other events with that name. My recommendation is to use the directive's name as part of the event name.
In your directive
angular.module('myApp.items.directive', [])
.directive('itemSwitch', function() {
return {
restrict: 'A',
scope: {
item: '=',
itemIndex: "="
},
template: '<button ng-click="doIt()">Do It</button>',
link : function(scope){
scope.doIt = function() {
scope.$emit('itemSwitch.doIt', scope.item, scope.itemIndex);
};
}
}
});
In your controller
$scope.doItItems = [];
$scope.$on("itemSwitch.doIt", function(item, itemIndex) {
doItItems.push(item);
});
In this example, on each click of the Do It button, an item is pushed to the doItItems list.
In your itemSwitch directive, you can do
$rootScope.$broadcast('onChange', item);
And then you can pick it up in any scope that is listening (in this case, your controller), with
$scope.$on('onChange', function(event, args) { ... }
This works because $broadcast moves downward from parent to children, while $emit moves upward from child to parents.
So for example, $rootScope.$emit would only be picked up by $rootScope.$on since $rootScope has no parents, while $scope.$broadcast would only be available to that scope's children and not to $rootScope.$on, since $rootScope is not a child of $scope
I have a bootstrap modal. On pressing close button, the value of the array is getting changed but it shouldn't.
controller.js
$scope.open = function(){
var modalInstance = $modal.open({
animation: true,
templateUrl: 'views/view1.html',
controller: 'controller2',
resolve: {
items: function(){
return $scope.array;
}
}
});
modalInstance.result.then(function (changed_array){
$scope.array = changed_array;
},function(){
// no change
});
};
code for second controller
angular.module('storagewebApp').controller('controller2',function($scope, $modalInstance, items) {
$scope.array = items;
$scope.ok = function(){
$modalInstance.close($scope.array);
};
$scope.cancel = function(){
$modalInstance.dismiss('cancel');
};
});
view2.html
<div class="modal-header">
<h4 class="modal-title">Set threshold</h4>
</div>
<div class="modal-body">
<div class="form-group" align="left">
<div> E:</div> <input type="text" ng-model="array[0]">
<div> M:</div><input type="text" ng-model="array[1]">
<div>T:</div><input type="text" ng-model="array[2]">
<div>F: </div><input type="text" ng-model="array[3]">
<div> I:</div><input type="text" ng-model="array[4]">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-warning" ng-click="cancel()">Close</button>
<button type="button" class="btn btn-primary" ng-click="ok()">Save</button>
</div>
</div>
The values are changed via input box but on pressing close button values should not be sent to the first controller, but on clicking close button value of changed array is passed to first controller.
This behaviour is a common AngularJS/JavaScript mistake. When you instantiate your modal controller you are passing a reference of your array. Then inside of your modal controller you manipulate that reference, even if you don't pass it back.
When you write:
$scope.array = items
What happends in memory is that $scope.array points to the same location as items. When you modify in any way $scope.array's object you are modifying items also.
As a solution you need to deep copy your initial array into the new one, in this way creating a new object and reference. AngularJS has an inbuilt function that does this: https://docs.angularjs.org/api/ng/function/angular.copy
angular.copy
See this plunkr for example: http://plnkr.co/edit/W6EYUwQ1K1YAnfnJ2r4a?p=preview
You should create a new scope for the modal window. Like this:
var modalScope = angular.extend(
$scope.$new(), {
val1ToPassToModal: $scope.originalValue1,
val2ToPassToModal: $scope.originalValue2,
});
$modal.open({
templateUrl: '…',
controller: '…',
scope: modalScope,
resolve: {
…
}
});
Of course if you don't want to pass new values to the modal window, you can write just:
scope: $scope.$new().
I'm using Angular-ui modal instance and i'm trying to get the return value but for some reason the bindings is not working correctly
var openNewAlbum = function() {
var modal = $modal.open({
templateUrl :'/app/modals/NewAlbumModal.html',
controller : function($scope, $modalInstance) {
$scope.albumTitle;
$scope.cancel = function() {
$modalInstance.dismiss('cancel');
};
$scope.accept = function() {
$modalInstance.close($scope.albumTitle);
}
},
keyboard: false,
backdrop: 'static'
});
return modal;
}
this is in the controller:
$scope.newAlbum = function() {
var modalInstance = ModalService.openNewAlbum();
modalInstance.result.then(function(response) {
console.log(response);
});
}
the var response that gets logged is Whatever I assign to it inside the Modal's Controller and not what gets passed in the in Input
here is the modal template:
<div style="padding:6px">
<div class="modal-body" style="font-weigth:bold;font-size:18px">
<label for="">Nombre del Album</label>
<input class="form-control" type="text" ng-model="albumTitle">
{{albumTitle}}
<br>
</div>
<button class="btn btn-danger" ng-click="cancel()" >Cancelar</button>
<button class="btn btn-primary pull-right" ng-click="accept()">Aceptar</button>
</div>
when checking {{albumTitle}} I cant see that it changes, but when I click Accept, the return value is ' ' ....... (the value that was assign to $scope.albumTitle = '';)
I have a table with a view button, when view is clicked modal display but now I want to display certain data on the modal. I am using .html pages.
I am not sure what am I missing here
html
<td>
<span>
<input class="btn btn-sm btn-dark" data-ng-click="launch('create',client)" type="button" value="view" />
</span>
</td>
This will luanch the modal
Modal
<div class="modal fade in" ng-controller="dialogServiceTest">
<div class="modal ng-scope">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">
<span class="glyphicon glyphicon-star"></span> Client Details
</h4>
</div><div class="modal-body">
<ng-form name="nameDialog" novalidate="" role="form" class="ng-pristine ng-invalid ng-invalid-required">
<div class="form-group input-group-lg" ng-class="{true: 'has-error'}[nameDialog.username.$dirty && nameDialog.username.$invalid]">
<label class="control-label" for="username">Name:</label>
<input type="text" name="username" id="username" ng-model="client.ClientName" ng-keyup="hitEnter($event)" required="">
<span class="help-block">Enter your full name, first & last.</span>
</div>
<div>{{client.ClientName}}</div>
</ng-form>
</div><div class="modal-footer">
<button type="button" class="btn btn-default" ng-click="cancel()">Cancel</button>
<button type="button" class="btn btn-primary" ng-click="save()" ng-disabled="(nameDialog.$dirty && nameDialog.$invalid) || nameDialog.$pristine" disabled="disabled">Save</button>
</div>
</div>
</div>
</div>
Angular
angular.module('modalTest', ['ngRoute','ui.bootstrap', 'dialogs'])
.controller('dialogServiceTest', function ($scope,$http, $rootScope, $timeout, $dialogs) {
$scope.clients = []; //Array of client objetcs
$scope.client = {}; //Single client object
$scope.launch = function (which,client) {
var dlg = null;
alert(client.ClientName);
dlg = $dialogs.create('/templates/Modal.html', 'whatsYourNameCtrl', {}, { key: false, back: 'static' });
dlg.result.then(function () {
$scope.client.ClientName = client.ClientName;
});
})
.run(['$templateCache', function ($templateCache) {
$templateCache.put('/templates/Modal.html');
}]);
here is some of my code
$scope.showScreenSizePicker = function(){
$scope.actionmsg = "";
var modalWindow = $modal.open({
templateUrl: '{{url()}}/modals/modalScreenSizePicker',
controller: 'modalScreenSizePickerController',
resolve: {
titletext: function() {return "Screen Sizes: ";},
oktext: function() {return "Close";},
canceltext: function() {return "Cancel";},
labeltext: function() {return "";},
}});
modalWindow.result.then(function(returnParams) {
$scope.setViewSize(returnParams[0], returnParams[1]);
});
}
you can see i am passing variables into modal using resolve. If you want to pass values back from the modal you can grab the variable returnParms (array)
and here is my controller code:
angular.module('myWebApp').controller('modalScreenSizePickerController', function($scope, $modalInstance, titletext, labeltext, oktext, canceltext) {
$scope.titletext = titletext;
$scope.labeltext = labeltext;
$scope.oktext = oktext;
$scope.canceltext = canceltext;
$scope.customHeight = 800;
$scope.customWidth = 600;
$scope.selectCustomSize = function(width, height){
if (width < 100){ width = 100; }
if (height < 100){ height = 100; }
$scope.selectItem(width, height);
}
$scope.selectItem = function(width, height) {
var returnParams = [width, height];
$modalInstance.close(returnParams);
};
$scope.cancel = function() {
$modalInstance.dismiss();
};
});
hope my sample helps
I think what you are looking for is the resolve property you can use with the $modal service. I am not exactly sure which version of UI Bootstrap you are using, but the latest one works as follows:
var myModal = $modal.open({
templateUrl: '/some/view.html',
controller: 'SomeModalCtrl',
resolve: {
myInjectedObject: function() { return someObject; }
});
myModal.result.then(function(){
// closed
}, function(){
// dismissed
});
Then you can use the injected resolved value inside the modals controller:
app.controller('SomeModalCtrl', function ($scope, $modalInstance, myInjectedObject) {
// use myInjectedObject however you like, eg:
$scope.data = myInjectedObject;
});
You can acces the client in modal by using "$scope.$parent.client" - "$parent" give you $scope from witch the modal was open with all data.