I am having an MD-dialog controller like below.
var HomeController = function ($scope) {
$scope.demoNonLinear = function () {
var element = document.querySelector('.mdl-stepper#stepper-non-linear');
if (!element) return false;
var stepper = element.MaterialStepper;
var steps = element.querySelectorAll('.mdl-step');
var step;
// Upgrade the component.
if (typeof componentHandler === 'undefined') {
console.log('Missing componentHandler');
} else {
console.log('componentHandler is available');
componentHandler.upgradeAllRegistered();
}
for (var i = 0; i < steps.length; i++) {
step = steps[i];
step.addEventListener('onstepnext', function (e) {
setTimeout(function () {
stepper.next();
}, 4000);
});
}
};
};
The line
var element = document.querySelector('.mdl-stepper#stepper-non-linear');
is not working on the md-dialog html instead it works on the main document. Ho can I make it work on my md-dialog content ?
Plunker link
"https://plnkr.co/edit/ixMI8FKbhyTgL5sYieVa?p=preview"
Try this to select your element in angular.
var element = angular.element( document.querySelector( '#stepper-non-linear' ) );
EDIT FOR GOOD APPROACH
I've wrapped the function in a angular.element(document).ready event which will be executed when document is ready rather than the $timeout approach.
DEMO PLUNKER
Related
How do I accurately count the number of watchers on a page?
I found a few different articles on how to do it, but they all give me different counts, so I'm struggling to find which is the correct way.
There is a StackOverflow here with the following code:
(function () {
var root = angular.element(document.getElementsByTagName('body'));
var watchers = [];
var f = function (element) {
angular.forEach(['$scope', '$isolateScope'], function (scopeProperty) {
if (element.data() && element.data().hasOwnProperty(scopeProperty)) {
angular.forEach(element.data()[scopeProperty].$$watchers, function (watcher) {
watchers.push(watcher);
});
}
});
angular.forEach(element.children(), function (childElement) {
f(angular.element(childElement));
});
};
f(root);
// Remove duplicate watchers
var watchersWithoutDuplicates = [];
angular.forEach(watchers, function(item) {
if(watchersWithoutDuplicates.indexOf(item) < 0) {
watchersWithoutDuplicates.push(item);
}
});
console.log(watchersWithoutDuplicates.length);
})();
When I test it on the Angularjs.org homepage, it gives me 132 watchers.
I then found a Medium post here, with the following code:
function getWatchers(root) {
root = angular.element(root || document.documentElement);
var watcherCount = 0;
function getElemWatchers(element) {
var isolateWatchers = getWatchersFromScope(element.data().$isolateScope);
var scopeWatchers = getWatchersFromScope(element.data().$scope);
var watchers = scopeWatchers.concat(isolateWatchers);
angular.forEach(element.children(), function (childElement) {
watchers = watchers.concat(getElemWatchers(angular.element(childElement)));
});
return watchers;
}
function getWatchersFromScope(scope) {
if (scope) {
return scope.$$watchers || [];
} else {
return [];
}
}
return getElemWatchers(root);
}
getWatchers().length
Testing this on Angularjs.org homepage returns 152 watchers.
Using simply angular.element('body').scope().$$watchersCount returns 80.
I understand that the SO code removes duplicates, is it removing duplicates that are watching the same expression (so there will only be one watcher listed for an expression like !visible), or it's removing duplicate watcher objects?
Update:
I realize the discrepancy is from duplicates. But, do duplicate watchers affect performance?
This script will help you identify how many watchers are there on your
page
just paste the below script on your browser's console and it will tell you the watcher count on your page
function getWatchers(root) {
root = angular.element(root || document.documentElement);
var watcherCount = 0;
function getElemWatchers(element) {
var isolateWatchers = getWatchersFromScope(element.data().$isolateScope);
var scopeWatchers = getWatchersFromScope(element.data().$scope);
var watchers = scopeWatchers.concat(isolateWatchers);
angular.forEach(element.children(), function (childElement) {
watchers = watchers.concat(getElemWatchers(angular.element(childElement)));
});
return watchers;
}
function getWatchersFromScope(scope) {
if (scope) {
return scope.$$watchers || [];
} else {
return [];
}
}
return getElemWatchers(root);
}
getWatchers().length
I instinctly know this is wrong, but how to do this otherwise. It is not example that reflects something more complex what I am trying to do.
First tried to inject $document & $window. But $document did not have access to createElement method.
Nother thought is that I can use this type of logic if I wrap some of it in $scope.apply();
full code plnkr here
app.directive('videoPlayButton', function() {
return {
restrict: 'A',
link: linkFunction
};
function linkFunction(scope, el, att, controller) {
var player; // need this available for button eventlistenter
// Invoke on documet.ready(see bottom)
ready(loadPlayerFunction);
///// Directive Methods
// Inject iframe_ap if not available & configure/reference Iframe player
function loadPlayerFunction() {
if (typeof(YT) == 'undefined' || typeof(YT.Player) == 'undefined') {
// inject youtube iframe_apiscript as 1st script tage in the header
var tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
}
// onYouTubeIframeAPIReady: youtube iframe APi method
// Allows to config & reference your iframe to the api
window.onYouTubeIframeAPIReady = function() {
loadPlayer();
}
// Configure & check if ready(& worked)
function loadPlayer() {
player = new YT.Player('youtube-video', {
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});
}
// Check if everything is functioning
function onPlayerReady() {
console.log("hey Im ready");
//do whatever you want here. Like, player.playVideo();
}
function onPlayerStateChange() {
console.log("my state changed");
}
} // loadPlayerFunction
// Logic for detached basic play/pause button
var playButton = document.querySelector('#playTheVideo');
console.log('playButton', playButton)
playButton.addEventListener('click', function() {
var state = player.getPlayerState();
if(state === 1) {
player.pauseVideo();
} else {
player.playVideo();
}
});
// Non jQuery on document.ready
// Source: http://youmightnotneedjquery.com/#ready
function ready(fn) {
if (document.readyState != 'loading') {
fn();
} else {
document.addEventListener('DOMContentLoaded', fn);
}
}
}
});
Using document.createElement is pretty common in Angular (as is stuff like querySelector when trying to get an element by class name); depending on what you're doing, it often gets wrapped in an angular.element call:
var elem = angular.element(document.createElement('script'));
// which I think is equivalent too...
var elem = angular.element('<script></script>');
So maybe something like this:
var theSource = 'http://something';
var tag = '<script src="' + theSource + '"></script>';
// Or this might go too...
// var tag = document.createElement ( 'script' );
// tag.src = theSource;
var elem = angular.element(tag);
var firstScriptTag = angular.element(document.getElementsByTagName('script')[0]);
firstScriptTag.prepend(elem); // or append, depending
There is something I'm missing here it seems my view is not removing.
// ROUTER //
screensaveroff: function() {
AnimationManager.outAnimation([self.screensaverView], function() {
console.log($(this.screensaverView.el).length); //!! always trigger 1 !!
});
}
// ANIMATION MANAGER outANimation function (trigger outAnimation for the passed view as arg)//
outAnimation : function(animationArray, callback){
var time = [];
window.animationArray = animationArray;
for (var i=0; i<animationArray.length; i++)
{
var view = animationArray[i];
view.outAnimation();
time[i] = animationArray[i].animationTime;
}
var timeoutMax = Math.max.apply(null, time);
setTimeout(function(){callback()},timeoutMax);
}
// screensaverView outANimation function //
outAnimation: function() {
var self = this;
this.$el.slideToX(1920, self.animationTime, function() {
self.clearIntervalAnimation();
self.remove();
});
},
any idea what's wrong with my code ?? thanks a lot
Angular loads new pages just by moving the scroll bar. Not when it reaches the bottom. It even loads new posts when i scroll up.
app.factory('Recipes', function ($http) {
var Recipes = function () {
this.recipes = [];
this.loading = false;
this.page = 1;
};
Recipes.prototype.nextPage = function () {
var url = 'api/recipes?page=' + this.page;
if (this.loading) return;
this.loading = true;
$http.get(url)
.success(function (data) {
console.log(this.page);
for (var i = 0; i < data.data.length; i++) {
this.recipes.push(data.data[i]);
}
this.page++;
this.loading = false;
}.bind(this));
};
return Recipes;
});
app.controller('RecipesCtrl', function ($scope, $http, Recipes) {
$scope.recipes = new Recipes();
});
This is the angular part. This is the laravel part:
Route::group(['prefix' => 'api'], function () {
Route::get('recipes', [
'as' => 'recipe.all',
'uses' => 'RecipeController#recipes'
]);});
And this is the html part:
<div ng-controller="RecipesCtrl">
<div class="recipe row" infinite-scroll="recipes.nextPage()" infinite-scroll-distance="1"
infinite-scroll-disabled='recipes.loading'>
<div ng-repeat="recipe in recipes.recipes | orderBy:sortOrder:sortReverse | limitTo:myLimit">
...
</div>
</div>
</div>
First Problem: Why does infinite-scroll load more content constantly?
infinite-scroll-distance is set to 1 in your code. This means that when the element is within 1000 pixels of the browsers bottom the directive will fetch more data.
If you change this to a value closer to 0, the user will have to scroll further for the trigger to get activated.
Second Problem: How do I prevent the directive from loading more content, when there is no more content to return?
One solution to stopping the directive from continuously loading more data is by setting the recipes.loading = true; when the returned data is empty.
As such:
.success(function (data) {
console.log(this.page);
for (var i = 0; i < data.data.length; i++) {
this.recipes.push(data.data[i]);
}
this.page++;
this.loading = false;
recipes.loading = true; // This should prevent more code from being loaded.
}
I have a simple Carousel example. The example on Plnkr shows what I do in my application. I have to change the slides in my application. When I set the active slide after I change slides it goes to that slide and then slides out into oblivion or it goes to the first slide. How can I solve this problem? So that after making new slides I can go to the right slide?
http://plnkr.co/edit/PJg9U4HZ1k5aSTSvbl3k?p=preview
angular.module('plunker', ['ui.bootstrap', 'ngTouch']);
function CarouselDemoCtrl($scope) {
$scope.genderPerson = "men";
$scope.myInterval = -1;
$scope.slides = [];
$scope.$watch("genderPerson", function( newValue, oldValue ) {
$scope.MakeSlides();
});
$scope.MakeSlides = function() {
var newSlides = [];
for ( var i = 0; i < 10; i++ ) {
newSlides[i] = { image: 'http://api.randomuser.me/portraits/' + $scope.genderPerson + '/' + i + '.jpg' };
}
$scope.slides = newSlides;
if ( $scope.slides[6] ) {
$scope.slides[6].active=true;
}
}
}
Looks like there is a race condition, if I wrap the active slide set in a timeout with a delay it seems to work:
$timeout(function () {
$scope.slides[6].active=true;
}, 100);
I just struggled with this problem. Here's my overly technical hack totally legit solution:
1. Create a new directive that overrides the addSlide method
.directive('onCarouselChange', function ($animate, $parse) {
return {
require: 'carousel',
link: function (scope, element, attrs, carouselCtrl) {
var origAdd = carouselCtrl.addSlide;
carouselCtrl.addSlide = function(slide, element) {
origAdd.apply(this, arguments);
$animate.on('enter', element, function (elem, phase) {
if (phase === 'close') {
scope.$emit('carouselEntered');
}
});
};
}
};
})
This will emit an event when the carousel's ngRepeat has finished parsing its new elements.
2. Add the new directive to the carousel element
<carousel interval="myInterval" on-carousel-change>
3. Set the active slide on an event listener
Add an event listener to the function where you add the elements, and set the active slide on its callback
$scope.MakeSlides = function() {
var newSlides = [];
for ( var i = 0; i < 10; i++ ) {
newSlides[i] = { image: 'http://api.randomuser.me/portraits/' + $scope.genderPerson + '/' + i + '.jpg' };
}
$scope.slides = newSlides;
var dereg = $scope.$on('carouselEntered', function(event, data) {
if ($scope.slides[6]) {
$timeout(function () {
$scope.slides[6].active=true;
});
dereg();
}
});
}
All of this is possible thanks to the magic of $animate's events.