Getting the value of a class after it's populated on load - javascript

I need to get the value/text of a a class(.item-title) but the class isn't available right away, it's loaded via angular. I tried to use window.load to have the page load before trying to get the value but this isn't working, it comes up empty because the class doesn't have a value yet. Once the page loads then the class does have a value. Once the page loads I can use the Chrome console to output .item-title just fine. Is there a way to get this function to run once the class(.item-title) is populated? The item has the same value for an ID so that is an option as well. THANK YOU!!!
Called by a simple JS reference in :
/App/lib/angulartics/angulartics-mixpanel.js"
Full contents of angulartics-mixpanel.js:
(function(angular) {
'use strict';
angular.module('angulartics.mixpanel', ['angulartics'])
.config(['$analyticsProvider', function ($analyticsProvider) {
angulartics.waitForVendorApi('mixpanel', 500, '__loaded', function (mixpanel) {
$analyticsProvider.registerPageTrack(function (path) {
var properties = {
'Page': $('.item-title').text()
};
mixpanel.track("Page Viewed", properties);
}));
});
}]);
})(angular);

You could also use $timeout, either with no delay value since that waits till just about everything else is finished before firing, or with a time delay if need be. Don't forget to inject $timeout into your controller first!
I've modified this post to include the (I think) correct implementation for your module pattern. (More on Dependency Injections here)
(function ( angular ) {
'use strict';
angular.module( 'angulartics.mixpanel', ['angulartics'] ).config( ['$analyticsProvider', '$timeout', function ( $analyticsProvider, $timeout ) {
angulartics.waitForVendorApi( 'mixpanel', 500, '__loaded', function ( mixpanel ) {
$analyticsProvider.registerPageTrack( function ( path ) {
$timeout( function () {
var properties = {
'Page': $( '.item-title' ).text()
};
mixpanel.track( "Page Viewed", properties );
},1000 );
} );
} );
}] );
})( angular );

Related

Why is 'this' losing context in new Angular 1.5 components?

Got a very odd issue coming up here with the new components. When we had a 1.4 directive we had the following code...
(function () {
'use strict';
angular.module('app.board').directive('dcCb', dcClipboardCopy);
function dcCb() {
return {
link : function(scope, elem) {
var clipboard = new Clipboard(elem[0]);
elem.on('$destroy', function() {
clipboard.destroy();
});
}
};
}
})();
Inside the clipboard.destroy() function is the following...
Clipboard.prototype.destroy = function(){
this.listeners.destroy();
}
In 1.4 this is the same as the element so...
<button class="btn btn-sm btn-menu-outline copy-button" ...
So this worked fine as the button element seemed to have the listeners property which could be invoked.
However after the upgrade to 1.5 and now we have a component like this....
(function() {
'use strict';
angular.module('app.board').component('dcCb', {
...
controller: [ '$element','$scope',function($element,$scope) {
var self = this;
self.$postLink = postLink;
function postLink(){
var clipboard = new Clipboard($element[0]);
...
$element.on('$destroy', clipboard.destroy);
}
}]
});
})();
this (when inside the destroy function of the Clipboard) is now the controller object. So trying to call this.listeners throws an error.
First Question :
I understand that this in new components is the component scope but in 1.4 it was the button element. Surely in both the button element should be $element? Were we doing something wrong in 1.4?
Second Question :
Shouldn't var clipboard = new Clipboard($element[0]) force the context of this inside the clipboard to always be the clipboard itself (due to the new keyword)?
You're handing a function, which is arbitrarily defined on a class, off to the window and event listeners to be executed in a different context than the instance of Clipboard:
$element.on('$destroy', clipboard.destroy);
This is a fundamental concept of execution context in javascript, and I'd recommend reading up on it. But you can easily solve your current problem by simply binding the context of the function you are passing:
$element.on('$destroy', clipboard.destroy.bind(clipboard));

Force template refresh ember.js

I'm using I18n localization package to take care of the switching language part of my app. It uses a global variable to set the language wanted and a json file to store the translations.
As the switching of a language is just a change in a global variable ember doesn't detect it and doesn't render the templates automatically. I forced it via an action in the application controller :
Extranet.ApplicationController = Ember.ObjectController.extend(
{
actions:
{
localToFr: function()
{
this.localisation('fr'); // this changes the global variable
this.get('target.router').refresh(); // this is what refresh the template
},
localToEn: function()
{
this.localisation('en');
this.get('target.router').refresh();
}
},
localisation: function(lg)
{
I18n.locale = lg;
}
})
I have two problems with that solution :
1) The application template isn't rerendered via my
this.get('target.router').refresh();
2) And my other problem, it doesn't work on templates which don't request a server access ( e.g. : the nest of routes 'authSession' )
Extranet.Router.map(function()
{
this.resource(
'parkings', {path:'/'}, function ()
{
this.route('parking', {path:'/parking/:parking_id'});
this.route('historique', {path:'/parking/:parking_id/historique'});
this.route('sessAct', {path:'/parking/:parking_id/sessAct'});
this.route('rapport', {path:'/parking/:parking_id/rapport'});
}
);
this.resource(
'authSession', function ()
{
this.route('login');
this.route('logout');
}
);
}
);
I was having a similar issue. I just went with View.rerender() on the main view, which was a form in my case.

calling Angular js file with partial doesn't work for first time

I am trying to load following js with partial, using $http . my problem is when i run code firest time get controller undefined type error,
where as on second click it works fine.
JAVASCRIPT::
$scope.loadPage = function(testurl) {
$scope.testpage = testurl;
var element = angular.element($("#data_template"));
$http.get(testurl).success(function(data) {
element.html(data);
$compile(element.contents())($rootScope);
$('.div1').slideUp('slow');
$('#data_template').slideDown('slow');
});
};
HTML::
<div class="movie-listing movie-listing-step"
ng-controller="ProductAddController">
<!-- all DOm PArt -->
</div>
mt js comes from s3 bucket, so may be js taking long time to come and $compile starts before js load . how to solve this problem ??
Here I have used $viewContentLoaded
app.controller('ProductAddController', function ($scope, $timeout) {
$scope.loadPage = function(testurl) {
$scope.testpage = testurl;
var element = angular.element($("#data_template"));
$http.get(testurl).success(function (data) {
element.html(data);
$compile(element.contents())($rootScope);
$('.div1').slideUp('slow');
$('#data_template').slideDown('slow');
});
}
$scope.$on('$viewContentLoaded', function(event) {
$timeout(function() {
$scope.loadPage(testurl);
},0);
});

Protractor: how to wait for full load of bootstrapped AngularJS

I have a bootstrapped Angular (1.2.6) app. This means it doesn't have an explicit ng-app. As such, I've run into all sorts of problems getting Protractor framework'd tests to work (using SauceLabs and grunt-protractor-runner).
The errors vary based on what I try, but generally:
Error: Angular could not be found on the page http://xxx:9000/ :
angular never provided resumeBootstrap
Or...
Error: Error while waiting for Protractor to sync with the page: {}
I've found a few proposed solutions which I've tried. Including those found in this rich thread, as well as here, too. Nothing I do, though, gets things working.
I've tried to use angular.resumeBootstrap in the bootstrapping like so (note I tried multiple variations of this to no avail, including trying to set an ng-app programatically on the document body):
angular.element( document ).ready( function() {
window.name = 'NG_DEFER_BOOTSTRAP!'
angular.bootstrap( document, [ 'app' ] );
setTimeout( angular.resumeBootstrap, 0 );
});
The error for this, as others have found, is weird:
UnknownError: unknown error: [ng:btstrpd] App Already Bootstrapped with this Element
'<body ng-app="" ng-controller="ApplicationController" class=" ng-scope pace-done">'
What's weird/annoying is that, at least looking in Sauce Labs session, it appears that this test is working... it's just weirdly thinking that it's been bootstrapped twice.
I've also tried using various combinations of waitForAngular, wait, and others in the test itself. Here's one variation I've tried:
it( 'should load the home page', function() {
ptor = protractor.getInstance();
ptor.waitForAngular();
ptor.driver.get( 'http://xxx:9000/' );
ptor.driver.wait( function() {
return ptor.getCurrentUrl().then( function() {
return ptor.isElementPresent( by.id( 'signIn' ) ).then( function() {
console.log('we are here!');
return true;
});
});
})
.then( function() {
expect( ptor.isElementPresent( by.id( 'signIn' ) ) ).toBe( true );
});
});
This results in errors like the following:
1) e2e: home should load the home page
Message: timeout: timed out after 20000 msec waiting for spec to complete
Stacktrace: undefined
I've also tried increasing various timeouts in the config file to no avail.
Any help would be much appreciated!
You should separate the test in two 'it'-steps. Like this:
it( 'should load angular', function() {
ptor = protractor.getInstance();
ptor.waitForAngular();
})
it( 'should load the home page', function() {
ptor.driver.get( 'http://xxx:9000/' );
ptor.driver.wait( function() {
return ptor.getCurrentUrl().then( function() {
return ptor.isElementPresent( by.id( 'signIn' ) ).then( function() {
console.log('we are here!');
return true;
});
});
})
.then( function() {
expect( ptor.isElementPresent( by.id( 'signIn' ) ) ).toBe( true );
});
});
The problem with protractor is that every command runs without waiting for the prior step to complete. So, ptor.waitForAngular() and ptor.driver.get( 'http://xxx:9000/' ) are running at almost the same time. If you separate these into two steps, protractor moves on after the first 'it'-step is done.

JavaScript ignores bool

i try to do something like a status-check to enable/ disable settings.
// file 1
function settings( valStatus ){
var status = valStatus;
this.getStatus = function(){
return status;
}
this.setStatus = function( valStatus ){
status = valStatus;
}
}
calling this function here:
// file 2
$settings = new settings( false );
$(document).ready(function() {
$( '#openSettings' ).on('click', function() {
$settings.setStatus( true );
enableSettings();
});
$('#save').on('click', function(){
$settings.setStatus( false );
closeSettings();
});
});
// file 1
enableSettings = function() {
if( $settings.getStatus() === true ){
//toggle emptyLink
$('.myButton').on('click', function(){
alert($settings.getStatus());
});
}
}
So as startup while clicking on "myButton" nothing happens.
After Clicking on "openSettings" and then on "myButton" i get the alert "true";
After clicking on "save" and then on "myButton" again, i get the alert "false", but it does not even trigger, because i checked it befere.... can somebody help me please?
Where is my mistake?
I think you probably want to put the check for status inside the .myButton click handler instead of outside. That way you only need to apply the handler once and it will either work or not depending on the value of status.
$(document).ready(function() {
var $settings = new settings( false ); // keep out of global scope
$( '#openSettings' ).on('click', function() {
$settings.setStatus( true );
});
$('#save').on('click', function(){
$settings.setStatus( false );
});
$('.myButton').on('click', function(){
if ($settings.getStatus()) {
alert($settings.getStatus());
}
});
});
First of all, you can write JavaScript with less code than e.g. Java. You don't need the getter and setter methods since there is no package visibility. Since you do nothing with the setters you can access your fields directly. This is less code to read and less code where you can have errors. So get rid of useless code (remember this is not the request do code one-liners). Search the internet for "clean code".
Since you are in JavaScript you can do better than that. A smaller approach to store your information.
var mySetting = {};
mySetting.status = true
console.log( mySetting.status );
mySetting.status = false;
console.log( mySetting.status );
Remember to keep your global space clean! Search the internet for "javascript global scope pollution". So do this within your scope.
Your main problem is, that you are using closures. You probably don't want to use it in your case. Search the internet for "javascript closure tutorial". There are a lot of good ones out there.
Since you are using the Jquery, you can use the .data() function to store your information.
See http://api.jquery.com/jquery.data/
$(function(){
$( '#openSettings' ).on('click', function() {
$('#settings').data( "status", true );
enableSettings();
});
$('#save').on('click', function(){
$('#settings').data( "status", false );
closeSettings();
});
$('.myButton').on('click', function(){
alert($('#settings').data());
});
});
Or within the HTML itself. See http://api.jquery.com/attr/
$('#settings').attr( "status", true );
console.log( $('#settings').attr( "status" ) );
Or as switches.
$('#settings').addClass( "settingsEnabled" );
$('#settings').removeClass( "settingsEnabled" );
console.log($('#settings').hasClass('settingsEnabled'));
Use .data() if you want to store object references and HTML for simple information like switches etc. The benefit is, that you can reach that information even with CSS.
And please get rid of the $ prefix in your own code since it has no meaning. If you use frameworks like angular it will help you to identify the origin or like the $$ the ("don't") use of it.

Categories