Mapbox map in ui-bootstrap tab not loading tiles - angularjs

I'm trying to put a mapbox map inside an angular-ui-bootstrap tab, and it seems that some/most of the tiles are not getting loaded upon initialization, and are not being requested as you pan around on the map. Outside of the ui-bootstrap tabset, the maps work just fine.
No errors are being thrown, but looking at the requests for the tiles, many of them are just not being requested for some reason. I'm not even sure how to debug this one.
Any ideas as to what might be going on?
Here is a plunkr showing the issue
And here is an example angular app that will show the problem
var app = angular.module('app', ['ui.bootstrap'])
app.controller('mapCtrl', ['$scope', '$timeout', function($scope, $timeout) {
$scope.val = 123
}]);
app.directive('myMap', function() {
return {
restrict: 'E',
template: "<div id='map_container'></div>",
link: function ($scope, elem, attrs) {
mapDiv = elem.find('#map_container')
L.mapbox.accessToken = 'pk.eyJ1IjoicmVwdGlsaWN1cyIsImEiOiJlSWZtN1hZIn0.FfT3RxbfRYv4LIjBxXG5fw';
var map = L.mapbox.map(mapDiv[0], 'examples.map-i86nkdio')
.setView([40, -74.50], 9);
}
};
});

The map gets initialized when the mapcontainer is not visible, that's why it fails. You're on the right path with calling invalidateSize but you need to do that when the tab becomes visible. I see you've already setup an event which you could hook into in your directive link function:
$scope.$on('tabSelect:map', function (t) {
$timeout(function () {
map.invalidateSize(true);
});
});
It doesn't work without the timeout. It needs some sort of delay so the tab is complete visible before firing invalidateSize. Here's an updated Plunker: http://plnkr.co/edit/gzwx2pZ1GjBZDE8Utxfl?p=preview

Related

AngularJS watcher not binding/ watching

I'm trying to implement the "infinite-scroll" feature in a list using a directive, which should load progressively a new set of "orders" when the scroll of the html element reaches or exceeds 75% of the scrollable height and append it to the existing list.
Unfortunately, the watcher doesn't trigger when i scroll the list.
The directive is located in the right tag and the watcher triggers the listener function only the first time, when the element is rendered by the browser.
The strange thing is that if i change path and then i return to the path where the list is, the watcher start behaving correctly and trigger the listener function everytime i perform a scroll.
<ol orders-loader class="orders-list">...</ol>
angular:
(function () {
angular.
module('myApp')
.directive('ordersLoader', ['$window', '$timeout', 'ordersResource', ordersloaderDirective])
function ordersloaderDirective($window, $timeout, loading, ordersResource) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
scope.orders = ordersResource; /*ordersResource use $resource to api calls
and then stocks the data in a array exposed in the scope*/
$timeout(function () {
scope.$watch(function () { return element[0].scrollTop }, function () {
if (*the scroll exceedes more or less 75% of the total scrollHeight*/) {
/*asking for more orders*/
}
});
}, 0);
}
}
}
I can't figure out where is the problem.
Solved
As yeouuu suggested, there was no digest cycle after the list scroll event, so i added:
element.bind('scroll', function () {
scope.$apply();
});
just before the $timeout function.
Whenever using plugins outside of angularJs that should trigger watcher you need to explicitly apply them. Otherwise Angular won't be aware of these changes/events.
In your case that means adding scope.$apply(); after the event.
Your edited solution:
element.bind('scroll', function () {
scope.$apply();
});
More information can be found here about the scope life cycle: https://docs.angularjs.org/guide/scope#scope-life-cycle

Angular script won't run when first loaded

On page load the console log prints but the toggleClass/click won't work I even use angular.element but it has the same result.I need to change state in order for the toggleClass to work.I dunno what's wrong in my code.
.run(['$rootScope', function ($rootScope) {
console.log('test');//this prints test and it's ok
//this part won't load at the first loading of page.
$('.toggle-mobile').click(function(){
$('.menu-mobile').toggle();
$(this).toggleClass('toggle-click');
});
//....
}])
even doing it this way doesn't work.
$rootScope.$on('$viewContentLoaded', function () {
angular.element('.toggle-mobile').on('click', function (event) {
angular.element(this).toggleClass('toggle-click');
angular.element('.menu-mobile').toggle();
event.preventDefault();
});
});
The Angular way to render items is different from "On DOM Ready" that is why we need to treat these as 2 separate things.
Angular could render items later on even after DOM is ready, this could happen for example if there is an AJAX call($http.get) and that is why a directive may be the recommended approach.
Try something like this:
<body ng-controller="MainCtrl">
<div toggle-Me="" class="toggle-mobile"> Sample <div class="menu-mobile">Sample 2</div>
</div>
<script>
var myApp = angular.module('myApp', []);
myApp.controller('MainCtrl', ['$scope', function ($scope) {}]);
myApp.directive("toggleMe", function() {
return {
restrict: "A", //A - means attribute
link: function(scope, element, attrs, ngModelCtrl) {
$(element).click(function(){
$('.menu-mobile').toggle();
$(this).toggleClass('toggle-click');
});
}
};
});
...
By declaring the directive myApp.directive("toggleMe",... as an attribute toggle-Me="" every time angular generates the input element it will execute the link function in the directive.
Disclaimer: Since the post lacks from a sample html I made up something to give an idea how to implement the solution but of course the suggested html is not part of the solution.

Calling a controller function from a directive with isolated scope without creating a markup attribute

Related to this question but I don't want to create an attribute in the markup to call the function.
I have a sticky problem involving a controller, a directive with isolated scope and a disappearing kendo ui modal dialog. The culprit is of course IE.
When my app loads, it has a menu with an item called 'Open Dialog'. When you click it, it calls a function on a controller called callModal():
// javascript object used to build menu. Used by another directive to build the app's menu & is shown here just for illustration
"quickLinks": [ {"label": "Open Dialog", "action": "callModal()", "actionController": "ModalController"} ]
// the controller
angular.module('myModule').controller('ModalController', ['$scope','modalService', function ($scope, modalService) {
$scope.callModal = function () {
var modal = {
scope: $scope,
title: 'Add a user',
templateUrl: 'modal.html'
};
modalService.openDialog(modal, function (result) {
//service code to open a kendo ui dialog, not relevant to this question
});
};
}]);
When I reduce the browser (IE only, Chrome is fine), the modal dialog disappears from view. I can't even find it on the DOM in IE and I don't know why.
My proposed solution is to hook into the brower's resize event and recreate the dialog by calling the controller function, $scope.callModal().
To do this I have to use scope.$parent.$parent.callModal() and I've been told that this is wrong.
Can anyone suggest a better way of doing this?
This is my directive code:
angular.module('myModule').directive('modalDialog', ['$window', function (myWindow) {
return {
restrict: 'ACE',
replace: true,
transclude: true,
scope: {
// various attributes here
},
template: '<div kendo-window></div>',
compile: function (tElement, tAttrs, transclude) {
return function (scope, element, attrs) {
var viewWindow = angular.element(myWindow); // cast window to angular element
viewWindow.bind('resize', autoResizeHandler); // bind resize
var resizeHandler = function() { // the resize handler function
/*
Try find the dialog. If not found, then call
controller function to recreate
*/
var kendoModal = jQuery('div[kendo-window]');
if (kendoModal.length === 0) {
scope.$parent.$parent.callModal(); // this works but it's ugly!
}
};

How can I hide an element when the page is scrolled?

Ok, I'm a little stumped.
I'm trying to think the angular way coming from a jQuery background.
The problem:
I'd just like to hide a fixed element if the window is not scrolled. If someone scrolls down the page I would like to hide the element.
I've tried creating a custom directive but I couldnt get it to work as the scroll events were not firing. I'm thinking a simple controller like below, but it doesnt even run.
Controller:
.controller('MyCtrl2', function($scope,appLoading, $location, $anchorScroll, $window ) {
angular.element($window).bind("scroll", function(e) {
console.log('scroll')
console.log(e.pageYOffset)
$scope.visible = false;
})
})
VIEW
<a ng-click="gotoTop()" class="scrollTop" ng-show="{{visible}}">TOP</a>
LIVE PREVIEW
http://www.thewinetradition.com.au/new/#/portfolio
Any ideas would be greatly appreciated.
A basic directive would look like this. One key point is you'll need to call scope.$apply() since scroll will run outside of the normal digest cycle.
app = angular.module('myApp', []);
app.directive("scroll", function ($window) {
return function(scope, element, attrs) {
angular.element($window).bind("scroll", function() {
scope.visible = false;
scope.$apply();
});
};
});
I found this jsfiddle which demonstrates it nicely http://jsfiddle.net/88TzF/

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