Passing locals into an $mdDialog when using confirm() - angularjs

I am trying to show an $mdDialog on a page to confirm deletion of an object. I have a simple delete button on the page, wired to a controller function:
<button ng-click="delete(item, $event)">Delete</button>
In the controller, I have:
$scope.delete = function (item, ev) {
var confirm = $mdDialog.confirm()
.title('Delete item?')
.textContent('The item will be irretrievably deleted!')
.ariaLabel('Delete')
.targetEvent(ev)
.ok('Delete!')
.cancel('Cancel');
$mdDialog.show(confirm).then(function () {
// delete
console.log("test");
}, function () {
// don't delete
});
};
I cannot work out how to pass the item object into the actual delete function. The documentation shows how to pass locals into a dialog, but that seems to preclude the use of confirm() for building the options.
Either provide an $mdDialogPreset returned from alert(), and
confirm(), or an options object with the following properties:
(emphasis mine)

You should be able to just use the 'item'.
$scope.delete = function (item, ev) {
var confirm = $mdDialog.confirm()
.title('Delete item?')
.textContent('The item will be irretrievably deleted!')
.ariaLabel('Delete')
.targetEvent(ev)
.ok('Delete!')
.cancel('Cancel');
$mdDialog.show(confirm).then(function () {
//item is available here
console.log(item);
}, function () {
// don't delete
});
};

Related

Is there a way to enable/disable input/textarea with list items in a list

I am new to angularjs. I have a list of items and when clicked on any list-item, the data is shown in an input/textarea. The behavior expected is when list-item is selected, the textarea in my case, should be disabled. But when I click edit, a call to show is also going. So, edit and show note methods are being called due to which the disable functionality isn't working.
I know the show is getting called because of edit/delete and all the three(show, edit, delete) are linked on the list but is there a way not to call show when edit/delete is clicked clicked?
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
$scope.items = [{"id":1, "name":'One', "description":'Oklahoma'},
{"id":2, "name":'Two', "description":'Utah'},
{"id":3, "name":'Three', "description":'Southampton'}];
$scope.selected;
$scope.showitem = (id) => {
console.log('show item')
$scope.selected = fetchSelecteditem(id);
$scope.selected.enable = false;
}
$scope.edititem = (id) => {
console.log('edit item')
$scope.selected = fetchSelecteditem(id);
$scope.selected.enable = true;
}
$scope.deleteitem = (id) => {
console.log('delete item')
$scope.selected = fetchSelecteditem(id);
$scope.selected.enable = false;
}
let fetchSelecteditem = (id) => {
return $scope.items.find(x => (x.id === id));
}
});
Please check the fiddle https://jsfiddle.net/0hr6nxtk/5. You can relate to the scenario in the console of the fiddle.
Please tell me how to correct this.
Instead of:
<li ng-click="showitem(item.id)"><span>{{item.name}}</span>
you can move the click event into the span:
<span ng-click="showitem(item.id)">{{item.name}}</span>

How to call function on controller with Angular Material Dialog

I have an angular 1.5 typescript based app using angular material.
How can I call a function in my controller from a Dialog ?
In my example it's this.callBack() i would like to call when the user has confirmed
Code snippet
public delete(condition: ModelModule.Condition): void {
var confirm = this.$mdDialog.confirm()
.title('delete condition!')
.textContent('are you sure ?')
.ariaLabel('delete')
.ok('Ok')
.cancel('Cancel');
this.$mdDialog.show(confirm).then(function(answer) {
console.log("You decided to delete "+answer)
// how to call this function on my controller ???
this.callBack()
}, function() {
console.log("You decided cancel")
});
}
Your this refers to the function and not your controller. Don't use a function in Typescript, use an arrow function instead, it won't create a new this-context.
public delete(condition: ModelModule.Condition): void {
var confirm = this.$mdDialog.confirm()
.title('delete condition!')
.textContent('are you sure ?')
.ariaLabel('delete')
.ok('Ok')
.cancel('Cancel');
this.$mdDialog.show(confirm).then((answer) => {
console.log("You decided to delete " + answer);
this.callBack();
}, () => {
console.log("You decided cancel");
});
}
this in Typescript

calling a function when AngularUI Bootstrap modal has been dismissed and animation has finished executing

I'm using the Angular UI bootstrap modal and I ran into a bit of a problem.
I want to call a function when the bootstrap modal dismiss animation is finished. The code block below will call the cancel() function as soon as the modal starts to be dismissed - and NOT when the modal dismiss animation has finished.
Angular UI does not use events, so there is no 'hidden.bs.modal' event being fired (at least, not to my knowledge).
var instance = $modal.open({...});
instance.result.then(function(data) {
return success(data);
}, function() {
return cancel();
})
The cancel() block immediately runs when the modal starts to close. I need code to execute when the closing animation for the Bootstrap modal finishes.
How can I achieve this with angular UI?
Component for reference:
https://angular-ui.github.io/bootstrap/#/modal
Thanks!
A little late but hope it still helps! You can hijack the uib-modal-window directive and check when its scope gets destroyed (it is an isolated scope directive). The scope is destroyed when the modal is finally removed from the document. I would also use a service to encapsulate the functionality:
Service
app.service('Modals', function ($uibModal, $q) {
var service = this,
// Unique class prefix
WINDOW_CLASS_PREFIX = 'modal-window-interceptor-',
// Map to save created modal instances (key is unique class)
openedWindows = {};
this.open = function (options) {
// create unique class
var windowClass = _.uniqueId(WINDOW_CLASS_PREFIX);
// check if we already have a defined class
if (options.windowClass) {
options.windowClass += ' ' + windowClass;
} else {
options.windowClass = windowClass;
}
// create new modal instance
var instance = $uibModal.open(options);
// attach a new promise which will be resolved when the modal is removed
var removedDeferred = $q.defer();
instance.removed = removedDeferred.promise;
// remember instance in internal map
openedWindows[windowClass] = {
instance: instance,
removedDeferred: removedDeferred
};
return instance;
};
this.afterRemove = function (modalElement) {
// get the unique window class assigned to the modal
var windowClass = _.find(_.keys(openedWindows), function (windowClass) {
return modalElement.hasClass(windowClass);
});
// check if we have found a valid class
if (!windowClass || !openedWindows[windowClass]) {
return;
}
// get the deferred object, resolve and clean up
var removedDeferred = openedWindows[windowClass].removedDeferred;
removedDeferred.resolve();
delete openedWindows[windowClass];
};
return this;
});
Directive
app.directive('uibModalWindow', function (Modals) {
return {
link: function (scope, element) {
scope.$on('$destroy', function () {
Modals.afterRemove(element);
});
}
}
});
And use it in your controller as follows:
app.controller('MainCtrl', function ($scope, Modals) {
$scope.openModal = function () {
var instance = Modals.open({
template: '<div class="modal-body">Close Me</div>' +
'<div class="modal-footer"><a class="btn btn-default" ng-click="$close()">Close</a></div>'
});
instance.result.finally(function () {
alert('result');
});
instance.removed.then(function () {
alert('closed');
});
};
});
I also wrote a blog post about it here.

Getting the array value of angular ng-repeat

I have a list that looks like this:
Store 1
Section A
Section B
and so on...
Store 2
Section A
and so on...
So I open a modal window to create a new store. I return the store when I close the window, and so far that's working great!
<div ng-repeat="store in global.user.company2.store">
I need to push the callback store to $scope.global.user.company2.store[?]
modalInstance.result.then(function (newStore) {
// How do I get the IndexOf value of store?
// This is one of the stores in the ng-repeat
$scope.global.user.company2.store[???].section.push(newStore)
}, function () {
$log.info('Modal dismissed at: ' + new Date());
});
The way I'm sending a selected store into the modal is with resolve
$scope.createSection = function (size) {
var modalInstance = $modal.open({
templateUrl: 'createSection.html',
controller: 'SectionModal',
size: size,
resolve: {
items: function () {
// $scope.radio.model === store
// $scope.radio.model2 === section
return $scope.radio;
}
}
});
UPDATE: Here's a basic plunker http://plnkr.co/edit/UGN4niAO9nQETqhg8lxn
The radio model buttons aren't working. If you change resolve items to $scope.radio.model, the modal breaks. So I left it as is. I think maybe it has to do with the btn-radio being part of angular-ui-bootstrap?
When resolving your modal, box your returned object with the array index of the object.
For example:
$modalInstance.close({ radio: $scope.radio, index: $scope.items.indexOf($scope.radio) } );
Then, when resolving your modal's promise, simply unbox your object:
modalInstance.result.then(function (selectedItem) {
$scope.selected = selectedItem.radio;
$scope.selectedIndex = selectedItem.index;
}, function () {});
See the Angular-Bootstrap docs for more details.

I'm new to TDD backbone with jasmine-sinon. change event in backbone unit test

I'm making a test for event change to a selectbox view in backbone which is 'should provide the correct path to router.navigate when changed'. this is the scenario, If I select a value in the dropdown, it should redirect to the correct url.
this is the test (campaign.test.js):
it('should provide the correct path to router.navigate when changed', function() {
var routeName = 'test_campaign';
var routerSpy = sinon.spy(Insights.router, 'navigate');
this.view.$el.bind('change', function() {
expect(routerSpy).toHaveBeenCalledWith('campaign/' + routeName, true);
});
//create the option element holding the test value
this.view.$el.append($('<option value="' + routeName +'" selected="selected" />'));
this.view.$el.trigger('change');
routerSpy.restore();
});
this is the module (campaign.js):
DropdownView: Backbone.View.extend({
tagName: 'select',
className: 'campaign-list',
events: {
'change' : 'navCampaign'
},
initialize: function() {
_.bindAll(this);
this.collection.on('reset', this.render);
this.collection.on('add', this.addCampaign);
//if no campaigns exist then add a dummy placeholder
if(this.collection.models.length === 0) {
this.$el.append('<option value="">No Campaigns</option>');
}
},
navCampaign: function() {
Insights.router.navigate();
},
addCampaign: function(campaign) {
//remove the No Campaigns placeholder when campaigns are added
var firstChild = this.$el.children(':first');
if(firstChild.attr('value') === '') {
firstChild.remove();
}
var view = new Insights.Campaign.DropdownItem({ model: campaign });
var item = view.render().el;
this.$el.append(item);
},
render: function() {
this.collection.each(this.addCampaign);
return this;
}
})
In my test I created a new OPTION element then set a value with attribute selected.
How can I pass this as the currentTarget of the change event and send it to trigger()?
Or is there a easier way to do this?
Im getting this test fail. Error: Expected Function to have been called with 'campaign/test_campaign', true.
Your test have to look like this:
it('should provide the correct path to router.navigate when changed', function() {
var routeName = 'test_campaign';
var routerSpy = sinon.spy(Insights.router, 'navigate');
var view = new DropdownView();
//create the option element holding the test value
this.view.$el.append($('<option value="' + routeName +'" selected="selected" />'));
this.view.$el.trigger('change');
expect(routerSpy).toHaveBeenCalledWith('campaign/' + routeName, true);
routerSpy.restore();
});
First of all you have to create an instance of your view in the test and you have to create the spy before you create the view. Both can also be done in the beforeEach block.
When you adding the expect code in an event handler it can happen that it will called before the navigate method on your router is called. Cause two handlers are added to the listener, one to call navigate on the router and one that test that it was called on the router. As you can't insure which one is called first the test will fail when the listener from the test is called first.
Maybe its better to test it with passing the collection with data to the view, not setting the DOM of the view element directly in the test. So you will also test that your initialize and addData method will work.
Here are the changes, that made this test passed. :)
I changed the value of variable routeName, in campaign.test.js
from
var routeName = 'test_campaign';
to
var routeName = this.view.$el.val();
Then implemented this test to campaign.js
navCampaign: function() {
var newRoute = 'campaign/' + this.$el.val();
Insights.router.navigate(newRoute, true);
}
there I got this green. :)
sorry, I would like to make this clear with the help of my team mate.
var routeName = this.view.$el.val(); is pointing to null
by making the value dynamic it will loose the test's effectiveness,
so I put back to var routeName = 'test_campaign'.
the point of this test is to specify what value it is expecting based on a predefined set of inputs.

Resources