Getting access to a DOM element after closing Angular-Bootstrap $modal instance - angularjs

I'm using Malhar Dashboard widget framework at the moment, and struggling with an issue in the close event of the Bootstrap Modal.
FYI: https://github.com/DataTorrent/malhar-angular-dashboard
Specifically, when I close the modal and arrive back to the $modalInstance.result.then function, I need to have access to the parent container which launched the modal in the first place. This proves to be difficult.
For example, when certain click events are triggered, I can get access to the Kendo UI chart element as follows :
var chart = widget.find('.k-chart').data("kendoChart");
chart.resize($(".k-chart"));
But after having launched the $modal like this :
var modalInstance = $modal.open(options);
I want to close the $modal, and still get access to that Kendo chart widget in the modalInstance.result.then() section - but widget is undefined:
modalInstance.result.then(
function (result) {
var chart = widget.find('.k-chart').data("kendoChart"); // widget UNDEFINED !!!
}
)
**** UPDATE - Two things: ****
1) On a positive note, I can get access to the parent node of .modal-body; however, I don't think my solution is very elegenant with the use of el.parent().parent().parent().parent().find() .
2) On the downside, although I can call the kendo.resize() function, it has no effect.
Help is appreciated.
// Set resolve and reject callbacks for the result promise
modalInstance.result.then(
function (result) {
// Call the close callback
onClose(result, widget, scope);
if (!widget.gadgetConfigured) {
widget.gadgetConfigured = true;
widget.setHeight(widget.fixedSize.height);
widget.setWidth(widget.fixedSize.width);
}
scope.$broadcast('widgetSettingsClosed', widget, result);
// attempt to refresh kendo chart - 04/30/2015 BM:
var el = $('.modal-body');
var chartElem = el.parent().parent().parent().parent().find('.k-chart').data("kendoChart");
if (chartElem != undefined) {
chartElem.setOptions({ chartArea: { width: chartElem.getSize().width * .95, height: chartElem.getSize().height * .90 } });
chartElem.resize($(".k-chart"));
}
scope.$emit('widgetChanged', widget);
},
function (reason) {
// Call the dismiss callback
onDismiss(reason, scope);
}
);

Related

How to call a method in the child window in Angular JS

My angular JS application has a view button. When clicking on the view button, it will call method and that method changes the content in the same page. Now I have a request that when clicking on the view button, it has to open in a new tab and display the contents.
Previous code:
HTML:
<md-button ng-click="ctrl.ViewClick(item)">View</md-button>
Controller:
vm.ViewClick = function(item) {
// The browser URL is same but it is a new page with the new contents.
// Calls a angular js service and loads the contents
}
Now, I need to call that function in new browser tab.
I made the following changes but didn't worked. Can you please help me on this.
HTML:
<md-button ng-click="ctrl.NewTabClick(item)">View</md-button>
Controller:
vm.newTabClick = function(item){
$Window.open($location.absURL(), 'blank');
// How do I call vm.ViewClick function after opening the new browser window?
};
This is the old angular JS. Thanks for helping on this.
On workaround you can do in this case is, While opening the page pass query parameter in the URL.
vm.newTabClick = function(item){
// Add `viewLink=true` query parameter with true value
$Window.open($location.absURL() + '&viewLink=true', 'blank');
// Store item in sessionStorage
window.sessionStorage.setItem('item', JSON.stringify(item));
}
And then from the component where you want to call the parameter. You can write below code on $onInit lifecycle hook.
vm.$onInit = function () {
// Inject $routeParams in dependency areay
const storedItem = sessionStorage.getItem('item')
if ($routeParams.viewLink == 'true' && storedItem && storedItem != 'null') {
vm.viewLink(JSON.parse(storedItem));
}
}

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.

Triggering an Angular $broadcast from a factory

I'm running a Dashboard system, where widgets on a page will render some grids and charts. I'm now setting up something we call "Widget Linking", where clicking on a grid will go off to a chart widget and display the data related to the grid row I just clicked on.
Example: "linking a treelist (parent widget) to a bar chart (child widget) "
In this example, I have Kendo treelist select event, which will pass the currently-selected row off to my widgetLinkingFactory factor. This factory should iterate through all available widgets on my page to see if any charts (child widgets) are linked to this treelist (parent widget).
I'd like to setup an Angular watcher, then trigger a $broadcast in my treelist select event. However, I keep running into this issue where I cannot inject $scope into a service or factory.
Factory code with treelist select event (see dataModelOptions.change):
'use strict';
angular.module('app')
.factory('TreeListDataModel', function (WidgetDataModel, widgetLinkingFactory) {
function TreeListDataModel() {
}
TreeListDataModel.prototype = Object.create(WidgetDataModel.prototype);
TreeListDataModel.prototype.constructor = WidgetDataModel;
angular.extend(TreeListDataModel.prototype, {
init: function () {
// Kendo datasource object is initialized here...
// KENDO TREELIST SELECT EVENT !!
this.dataModelOptions.change = function (e) {
var row = this.dataItem(this.select());
var parentObj = _.find(this.dataItems(), { id: row.parentId });
var dataModelOptions = {
row: row,
parentObj: parentObj,
dimensions: this.options.dimensions,
fieldTypes: this.options.fieldTypes
};
// CANNOT DO THIS HERE - $SCOPE NOT AVAILABLE !!!
$scope.$broadcast('refreshLinkedWidgets', dataModelOptions);
widgetLinkingFactory.linkCharts(row, parentObj, this.options.dimensions, this.options.fieldTypes);
},
this.updateScope(this.dataModelOptions);
},
updateScope: function (data) {
this.dataModelOptions = data;
},
destroy: function () {
WidgetDataModel.prototype.destroy.call(this);
}
});
return TreeListDataModel;
});
And in my main directive code, I have no problem setting up the watcher:
scope.$on('refreshLinkedWidgets', function (event, dataModelOptions) {
// call my widgetLinkingFactory update code
});
I'm experimenting with some ideas, so advice is appreciated.
regards,
Bob
Factory/service don't have scope. You can't do it on scope
But you can do it via injecting rooScope in your service.
Like this
Declare a method inside TreeListDataModel named refreshLinkedWidgets
then
$rootScope.$on("refreshLinkedWidgets", TreeListDataModel .refreshLinkedWidgets);
Sample code
angular.module('app')
.factory('TreeListDataModel', function (WidgetDataModel, widgetLinkingFactory,$rootScope) {
var TreeListDataModel={
refreshLinkedWidgets:refreshLinkedWidgets
};
function refreshLinkedWidgets(e,a){
// some code
}
$rootScope.$on("refreshLinkedWidgets", TreeListDataModel .refreshLinkedWidgets);
return TreeListDataModel;
}
Inject $rootScope and $broadcast from it.

Backbonejs - Back button doesn't work if page transition on same page

Short description of my program and finally the problem:
I have got two pages. The first page list products in rows with a short description. If you click on one you will land on a detail page.
The detail page lists the product details and underneath a couple of related products. If you click on one of the releated products the same page is rendered again with the new information fetched from a REST interface.
If I want to use the browser-back-button or the own back-button to get to the previous product-detail-page a blank page appears. This only happens on my iPad. Using Chrome on a desktop browser works fine. I debugged the application and I figured out, that the backbonejs route is never called. I have no idea why.
Here is my code of the details page:
define([
"jquery",
"lib/backbone",
"lib/text!/de/productDetails.html"
],
function(
$,
Backbone,
ContentTemplate
){
var PageView = Backbone.View.extend({
// product details template
template: _.template(ContentTemplate),
// back-button clicked
events:{
'click a#ac-back-button':'backInHistory',
},
// init
initialize: function(options){
this.options=options;
// bind functions
_.bindAll(this,
'render',
'renderRelatedSeriePlainproduct',
'backInHistory'
);
// listen for collection
this.listenTo(this.options.relatedCollectionPlainproduct, 'reset',this.renderRelatedSeriePlainproduct);
},
// back button
backInHistory: function(e){
e.preventDefault();
window.history.back();
},
// render template
render: function(){
// render template
this.$el.html(this.template(this.model.models[0].attributes));
return this;
},
// render related products
renderRelatedSeriePlainproduct: function (){
var models = this.options.relatedCollectionPlainproduct.models;
if(models.length==0){
$('.ac-plainproduct').hide();
} else{
var elem = $('#ac-related-listing-plainproduct');
var ct="";
ct+='<ul id="ac-list-related-plainproduct">';
$.each(models, function(key, value){
ct+='<li>';
ct+='<a href="index.html?article_id='+value.get('article_id')+'&type='+value.get('type')+'&serie='+value.get('series')+'#product-detail">Link';
ct+='</a>';
ct+='</li>';
});
ct+='</ul>';
elem.append(ct);
}
}
});
// Returns the View class
return PageView;
});
I follow one of the links from renderRelatedSeriePlainproduct.If I click on the back button on the new page the backInHistory function is called, but the window.history.back(); does not call the backbone router.
Maybe the problem is the #hash in the URL, that is not changed during page transition. But this would not explain, why it works perfectly with my Chrome on my desktop machine. For me it seemed to be a problem of asynchronous calls but even there I could not find a problem.
Maybe it helps to list my router code as well. First of all I was thinking it is an zombie issue in backbone, but I remove all events and views while making the transition.
// function called by the route
// details page
productdetail: function() {
$.mobile.loading("show");
_self = this;
// lazy loading
require([
'collection/ProductDetailCollection',
'collection/RelatedCollection',
'view/ProductDetailView'
],
function(ProductDetailCollection, RelatedCollection, ProductDetailView){
// get URL parameters
var articleID = _self.URLParameter('article_id');
var type = _self.URLParameter('type');
var serie = _self.URLParameter('serie');
// product - details
var productDetail = new ProductDetailCollection.ProductDetail({id: articleID});
// related products
_self.relatedCollectionPlainproduct = new RelatedCollection({serie:serie, type:"Electronics", article_id:articleID});
// assign binded context
productDetail.fetch({
// data fetched
success: function (data) {
// page transition
_self.changePage(new ProductDetailView({
model:data,
relatedCollectionPlainproduct:_self.relatedCollectionPlainproduct
}));
// fetch data
_self.relatedCollectionPlainproduct.fetch({reset:true});
}
});
});
},
// page transition
changePage:function (page) {
// remove previous page from DOM
this.page && this.page.remove() && this.page.unbind();
// assign
this.page = page;
// assign page tag to DOM
$(page.el).attr('data-role', 'page');
// render template
page.render();
// append template to dom
$('body').append($(page.el));
// set transition
var transition = "fade";
// we want to slide the first page different
if (this.firstPage) {
transition = "fade";
this.firstPage = false;
}
// make transition by jquery mobile
$.mobile.changePage($(page.el), {changeHash:true, transition: transition});
// page was rendered - trigger event
page.trigger('render');
$.mobile.loading("hide");
},
I tried to use allowSamePageTransition but with no success. Maybe someone could give me a hint. Thanks!
Looks like jQuery Mobile and Backbone's routers are conflicting. Take a look here:
http://coenraets.org/blog/2012/03/using-backbone-js-with-jquery-mobile/
Thats not the reason. I disabled the routing of jquery mobile.
// Prevents all anchor click handling
$.mobile.linkBindingEnabled = false;
// Disabling this will prevent jQuery Mobile from handling hash changes
$.mobile.hashListeningEnabled = false;

Angular test - DOM updating on element click

I have a test and before test I'm doing such stuff.
- Going to some element
- Hovering element and small tooltip with 2 button gonna show
- I'm clicking on one of buttons and modal
var fireEvent = function(element, event) {
if (element.fireEvent) {
element.fireEvent('on' + event);
} else {
var evObj = document.createEvent('Events');
evObj.initEvent(event, true, false);
element.dispatchEvent(evObj);
}
};
beforeEach(function(){
browser().navigateTo('/');
sleep(5);
var promise = function() {
element('.browse-packshot-item').query(function(elements, done) {
var first_element = elements.eq(0);
fireEvent(first_element[0],'mouseover');
sleep(2);
element('#browse-packshots-flyout .browse-packshots-flyout-action').click();
done();
});
};
promise();
});
My question are:
- It seem like lots is going on in before but I have to test if there are some elements in this model window and all this action must take place before.
- Can you guide me for better solution - maybe use angular events on elements rather then looking for elements?
Cheers,

Resources