Pass variable to UI-bootstrap modal without using $scope - angularjs

Since I am a beginner using AngularJS the $scope approach to pass data between different controllers and (in my case) a modal drives me crazy. Due to that reason, I googled around the web and found an interesting blogpost about passing data to a UI-bootstrap modal without using $scope.
I had a deeper look at this blogpost and the delivered plunk which works pretty nice and started to adopt this to my own needs.
What I want to achieve is to open a modal delivering an text input in which the user is able to change the description of a given product. Since this would provide more than a minimal working example I just broke everything down to a relatively small code snippet available in this plunk.
Passing data from the main controller into the modal seems to work as the default product description is displayed in the modal text input as desired. However, passing the data back from the modal to the main controller displaying the data in index.html does not seem to work, since the old description is shown there after it was edited in the modal.
To summarize my two questions are:
What am I doing wrong in oder to achieve a 'two-way-binding' from the main controller into the modal's text input and the whole way back since the same approach works in the mentioned blogpost (well, as the approach shown in the blogpost works there must be something wrong with my code, but I cannot find the mistakes)
How can I implement a proper Accept button in order to accept the changed description only if this button is clicked and discard any changes in any other case (clicking on Cancel button or closing the modal by clicking next to it)?

In your main controller, create two resolver functions: getDescription and setDescription.
In your modal controller, use them.
Your modal HTML
<div class="modal-header">
<h3 class="modal-title">Test Text Input in Modal</h3>
</div>
<div class="modal-body">
Product description:
<input type="text" ng-model="modal.description">
</div>
<div class="modal-footer">
<button ng-click="modal.acceptModal()">Accept</button>
<button ng-click="modal.$close()">Cancel</button>
</div>
Your main controller
function MainCtrl($modal) {
var self = this;
self.description = "Default product description";
self.DescriptionModal = function() {
$modal.open({
templateUrl: 'modal.html',
controller: ['$modalInstance',
'getDescription',
'setDescription',
ModalCtrl
],
controllerAs: 'modal',
resolve: {
getDescription: function() {
return function() { return self.description; };
},
setDescription: function() {
return function(value) { self.description = value; };
}
}
});
};
};
Your modal controller
function ModalCtrl($modalInstance, getDescription, setDescription) {
var self = this;
this.description = getDescription();
this.acceptModal = function() {
setDescription(self.description);
$modalInstance.close();
};
}

Related

how to make custom directive in angular?

I am trying to make custom directive in angular .I try to add input field in my view when I click on button .In other words I am trying to make one custom directive in which when user press the button it add one input field in the browser .I think it is too easy if I am not use custom directive Mean If I use only controller then I take one array and push item in array when user click on button and button click is present on controller.
But when need to make custom directive where I will write my button click event in controller or directive
here is my code
http://play.ionic.io/app/23ec466dac1d
angular.module('app', ['ionic']).controller('appcontrl',function($scope){
$scope.data=[]
}).directive('inputbutton',function(){
return {
restrict :'E',
scope:{
data:'='
},
template:'<button>Add input</button> <div ng-repeat="d in data"><input type="text"></div>',
link:function(s,e,a){
e.bind('click',function(){
s.data.push({})
})
}
}
})
I just need to add input field when user click on button using custom directive ..could you please tell me where i am doing wrong ?
can we make button template and click event inside the directive
The reason it doesn't work is because your registering your click handler with jQuery. So when the click handler fires it is out of the scope of angular so angular does not know it needs to update its bindings.
So you have two options, the first is to tell angular in the click handler, 'yo buddy, update your bindings'. this is done using $scope.$apply
$apply docs: https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$apply
e.bind('click',function(){
s.$apply(function() {
s.data.push({});
});
});
However angular already has built in directive for handling things like mouse clicks you can just use that and let angular do the work for you. This would be the better option.
so first in your view register a click handler on your button
<button ng-click="add()">Add input</button> <div ng-repeat="d in data"><input type="text"></div>
Then in your link simply add the add() method of your scope
s.add = function () {
s.data.push({});
}
Heres a working fiddle showing both examples. http://jsfiddle.net/3dgdrvkq/
EDIT: Also noticed a slight bug in your initial click handler. You registering a click but not specifying the button to apply it to. So if you clicked anywhere in the directive, not just the button, the handler would fire. You should be more specific when registering events manually, using ids, class names attributes etc.
The e or element property of the link function is a jqlite or full jQuery object of the entire directive. If you have jQuery included before angular it will be a full jQuery object. If not it will a jqlite object. A thinned out version of jQuery.
Here is a basic example for your logic .
var TestApp = angular.module('App', []);
// controller
TestApp.controller('mainCtrl', function mainCtrl($scope) {
$scope.data = [];
$scope.addDataItem = function () {
$scope.data.push({
someFilield: 'some value'
});
console.log('pushing value ... ');
}
});
// view
<div ng-app="App" class="container" ng-controller="mainCtrl">
<button type="button" ng-click="addDataItem()">Add an input</button>
<div ng-repeat="d in data track by $index">
<custom-directive model="d"></custom-directive>
</div>
</div>
// directive
TestApp.directive('customDirective', function customDirective() {
return {
restrict: 'E',
scope: {
model: '='
},
template: 'item -> <input type = "text" />',
link: function (scope, elem, attrs) {
console.log('scope.model', scope.model);
},
controller: function ($scope) {
// do staff here
}
}
});

How to update scope variable using service

i have two controllers, the data in the first controller will be updated by click functions in the second controller. I am using service to communicate between controllers and not getting the expected result. whats wrong i am doing
link: http://jsbin.com/hozabigedu/1/
Well, you had several issues. First, I changed your data to be an object and to have a boolean as a property, rather than have itself be a boolean. This is good practice if you want to share info using a service. It's better that you share an object so that different controllers share the same reference. Primitives are problematic for that scenario. So here's the new service:
app.service('testservice', function(){
var data={}; //Changed here
getDataText = function() {
return data;
};
setDataText= function(val) {
data.text = val; //And changed here
return data;
};
return {
getDataText: getDataText,
setDataText: setDataText
};
});
Another issue is that your controller called the wrong function, it should be getDataText():
$scope.inputText = testservice.getDataText();
And lastly, you forgot to close the divs of the click elements, so the click 2 events bubbled up to the click 1 events thus showing right after hiding, so replace the 2nd controller div with this one:
<div ng-controller="secondController">
<div ng-click="showDiv();">click1</div>
<br/>
<br/>
<div ng-click="hideDiv();">click2</div>
</div>
</div>

AngularJS - Close modal function not working

I have made a simple pop up window by using ui.bootstrap but I can't seem to make the OK and CLOSE button to work. What am I missing in this sample codes?
Here is the sample code from plunkr
Thank you
**added exact code image
close and dismiss are methods of $modalInstance object returned by $modal.open:
$scope.open = function() {
$scope.$modalInstance = $modal.open({
scope: $scope,
templateUrl: "modalContent.html",
size: '',
})
};
$scope.ok = function() {
$scope.$modalInstance.close();
};
$scope.cancel = function() {
$scope.$modalInstance.dismiss('cancel');
};
One more problem with your code is that you need to specify scope: $scope in modal config. This is necessary if you want the scope inside modal template to be a child scope of the one, where you are defining ok/cancel methods.
Fixed demo: http://plnkr.co/edit/Y5s4yPm1TZB8S9nfO5CA?p=preview
I updated your code with a working version
http://plnkr.co/edit/iM5o0le3OioqxHBNF3d1?p=preview
There are a few things wrong with your code.
$modal is a factory, you can't call $modal.close()
You would need to do something like:
this.modal = $modal(....)
this.modal.close();
Still, even if you did this in your controller, your modal view does not have access to the scope.
The solution in the forked plunk I offer is to use
ng-click="$parent.$close()"
There are several ways as explained before and it depends in which context you need to use it.
For example if the modal is just a notification, there's no need to use functions for the buttons OK or CANCEL, just using $dismiss() or $close() in the ng-click of the buttons located in the modalContent.html file will be enough.
Here is also an updated plunker
http://plnkr.co/edit/OfCGJX?p=preview
<div class="modal-footer">
<button class="btn btn-primary" ng-click="$close()">OK</button>
<button class="btn btn-warning" ng-click="$dismiss()">Cancel</button>
</div>

Update directive variable from controller

New to AngularJS I have a simple directive, service and controller. I loop through a list of items from a database in the controller embedded in the directive to render a checkbox list of items. From a form I am able to update my list of items in the database. I would at the same time like to update my list of displayed items with the newly added item and was hoping to benefit from Angulars two-way binding but I can't figure out how...
My directive:
angular.module('myModule').directive('menu', function (menuService) {
return {
restrict: 'E',
templateUrl: '../templates/menu.html',
controller: function () {
var me = this;
menuService.getMenuItems().then(function(data) {
me.items = data;
});
},
controllerAs: 'menu'
};
});
and corresponding html:
<div ng-repeat="item in menu.items">
<div class="col-md-4" ng-if="item.menuItem">
<div class="checkbox">
<label for="{{item._id}}">
<input id="{{item._id}}" type="checkbox" name="menuItems" ng-model="menuItems[$index]" ng-true-value="{{item._id}}">
{{item.menuItem}}
</label>
</div>
</div>
</div>
My issue is now that if I add a new item using this controller
EDIT: On my page I have two things. 1: A list of items rendered using the directive above and 2: A simple form with a single input field. I enter a new menuItem and click "Save". That triggers the controller below to be called with the new menuItem. The menuItem is then added to a collection in my database but I would like the list on my page to update with the newly added item. Preferably without having to reload the entire page.
$scope.insertMenuItem = function () {
$http.post('/menuItems', $scope.formData)
.success(function (data) {
$scope.items = data;
});
};
then the "me.items" in my directive remains unchanged hence so does my list in my browser.
How do I "tie it all together" so that when I call insertMenuItem then my.items are updated automagically?
This didn't come out as well as I had hoped but I hope you get the meaning...
Thanks in advance
take a look at this http://jsbin.com/rozexebi/1/edit, it shows how to bind a list of items in a directive to a controller, hope it helps.

AngularJS modal window directive

I'm trying to make a directive angularJS directive for Twitter Bootstrap Modal.
var demoApp = angular.module('demoApp', []);
demoApp.controller('DialogDemoCtrl', function AutocompleteDemoCtrl($scope) {
$scope.Langs = [
{Id:"1", Name:"ActionScript"},
{Id:"2", Name:"AppleScript"},
{Id:"3", Name:"Asp"},
{Id:"4", Name:"BASIC"},
{Id:"5", Name:"C"},
{Id:"6", Name:"C++"}
];
$scope.confirm = function (id) {
console.log(id);
var item = $scope.Langs.filter(function (item) { return item.Id == id })[0];
var index = $scope.Langs.indexOf(item);
$scope.Langs.splice(index, 1);
};
});
demoApp.directive('modal', function ($compile, $timeout) {
var modalTemplate = angular.element("<div id='{{modalId}}' class='modal' style='display:none' tabindex='-1' role='dialog' aria-labelledby='myModalLabel' aria-hidden='true'><div class='modal-header'><h3 id='myModalLabel'>{{modalHeaderText}}</h3></div><div class='modal-body'><p>{{modalBodyText}}</p></div><div class='modal-footer'><a class='{{cancelButtonClass}}' data-dismiss='modal' aria-hidden='true'>{{cancelButtonText}}</a><a ng-click='handler()' class='{{confirmButtonClas}}'>{{confirmButtonText}}</a></div></div>");
var linkTemplate = "<a href='#{{modalId}}' id= role='button' data-toggle='modal' class='btn small_link_button'>{{linkTitle}}</a>"
var linker = function (scope, element, attrs) {
scope.confirmButtonText = attrs.confirmButtonText;
scope.cancelButtonText = attrs.cancelButtonText;
scope.modalHeaderText = attrs.modalHeaderText;
scope.modalBodyText = attrs.modalBodyText;
scope.confirmButtonClass = attrs.confirmButtonClass;
scope.cancelButtonClass = attrs.cancelButtonClass;
scope.modalId = attrs.modalId;
scope.linkTitle = attrs.linkTitle;
$compile(element.contents())(scope);
var newTemplate = $compile(modalTemplate)(scope);
$(newTemplate).appendTo('body');
$("#" + scope.modalId).modal({
backdrop: false,
show: false
});
}
var controller = function ($scope) {
$scope.handler = function () {
$timeout(function () {
$("#"+ $scope.modalId).modal('hide');
$scope.confirm();
});
}
}
return {
restrict: "E",
rep1ace: true,
link: linker,
controller: controller,
template: linkTemplate
scope: {
confirm: '&'
}
};
});​
Here is JsFiddle example http://jsfiddle.net/okolobaxa/unyh4/15/
But handler() function runs as many times as directives on page. Why? What is the right way?
I've found that just using twitter bootstrap modals the way the twitter bootstrap docs say to is enough to get them working.
I am using a modal to house a user edit form on my admin page. The button I use to launch it has an ng-click attribute that passes the user ID to a function of that scope, which in turn passes that off to a service. The contents of the modal is tied to its own controller that listens for changes from the service and updates values to display on the form.
So.. the ng-click attribute is actually only passing data off, the modal is still triggered with the data-toggle and href tags. As for the content of the modal itself, that's a partial. So, I have multiple buttons on the page that all trigger the single instance of the modal that's in the markup, and depending on the button clicked, the values on the form in that modal are different.
I'll take a look at my code and see if I can pull any of it out to build a plnkr demo.
EDIT:
I've thrown together a quick plunker demo illustrating essentially what I'm using in my app: http://embed.plnkr.co/iqVl0Wb57rmKymza7AlI/preview
Bonus, it's got some tests to ensure two password fields match (or highlights them as errored), and disables the submit button if the passwords don't match, or for new users username and password fields are empty. Of course, save doesn't do anything, since it's just a demo.
Enjoy.
There is a working native implementation in AngularStrap for Bootstrap3 that leverages ngAnimate from AngularJS v1.2+
Demo : http://mgcrea.github.io/angular-strap/##modals
You may also want to checkout:
Source : https://github.com/mgcrea/angular-strap/blob/master/src/modal/modal.js
Plunkr : http://plnkr.co/edit/vFslNmBAoKPVXtdmBXgv?p=preview
Well, unless you want to reinvent this, otherwise I think there is already a solution.
Check out this from AngularUI. It runs without twitter bootstrap.
I know it might be late but i started trying to figure out why the handler got called several times as an exercise and I couldn't stop until done :P
The reason was simply that each div you created for each modal had no unique id, once I fixed that everything started working. Don't ask me as to what the exact reason for this is though, probably has something to do with the $('#' + scope.modalId).modal() call.
Just though I should post my finding if someone else is trying to figure this out :)

Resources