AngularJS ng-click doesn't work in digest cycle - angularjs

Html:
<a href id="link "ng-click="print(arg)"> print </a>
Angularjs controller:
$scope.return_promise =function(arg){
return $http.post('\path');
)};
$scope.print = function(arg){
url ="other/path/"
$scope.return_promise(arg).then(function(r){
if(r){
$('#link').attr('href', url);
});
};
Problem: I checked with chrome debugger, the href actually updated, but the event doesn't trigger (i.e. not go to the url). If I click it again, it works.
If I add a statement document.getElementById('#link').click() at the end of if clause, it will prompt an error "digest cycle is in progress"
How can i solve this.

Not sure if I get your question. First, check if the code you paste is the code you wanted add here, because it has numerous errors. If you would like to replace dynamically href attribute do it like so:
<div ng-controller="SomeCtrl as ctrl">
print
</div>
(function () {
'use strict';
angular
.module('module')
.controller('SomeCtrl', SomeCtrl);
SomeCtrl.$inject = ['$scope'];
function SomeCtrl($scope) {
var vm = this;
vm.url = "#";
vm.return_promise = function (arg) {
return $http.post('/path');
};
vm.print = function (arg) {
var url = "other/path/";
vm.return_promise(arg).then(function (r) {
if (r) {
vm.url = url;
}
});
};
}
}());

Related

Confused in Calling function in angularjs using anchortag

i am working on one angularjs project.i have one dropdown in which new email,new contact,new calendar function is there.but these function is working on perticular page only like newmessage function works only on Email page.
i want to call the newMessage function, newAddressbook and new calendar functon from anywhere...
can you help me.
this is my code for that functions.:-
<a ng-href="" ng-click="mailbox.newMessage($event)">Email</a>
Contact
<a href="" ng-click="app.newCalendar()" >Calendar</a>
This should work for you.
In your app.js write following code
app.run(['$rootScope', function($rootScope) {
$rootScope.newMessage = function($event) {
// do something
}
$rootScope.newAddressbook = function($event) {
// do something
}
$rootScope.newCalendar = function($event) {
// do something
}
}]);
In Safari and IE "javascript:void(0)" is treated as unsafe so include following code into your app config
app.config(['$compileProvider',function($compileProvider) {
$compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|javascript|chrome-extension):/);
}])
// html code
<a ng-href="javascript:void(0)" ng-click="$root.newMessage($event)">Email</a>
Contact
<a href="javascript:void(0)" ng-click="$root.newCalendar($event)" >Calendar</a>
There may be few details which you might have missed , try embedding controller to the respective div which you are using .
Or you can declare the scope as "root" ('$rootScope') into your model .
Hope this will help .
var app = angular.module('SOGo.Common')
app.controller('navController', navController);
app.run(['$rootScope', function($rootScope) {
$rootScope.newMessage = function($event) {
// do something
}
$rootScope.newAddressbook = function($event) {
// do something
}
$rootScope.newCalendar = function($event) {
// do something
}
}]);
});

Using ngReact with ASP .NET MVC

I have an MVC test project that I am trying to use React components inside Angular directives. It will render the directive, but any props I pass in are undefined. Here are my files:
~/Scripts/Directives/HelloDirective.js:
var Hello = React.createClass({
render: function () {
return React.DOM.span(null,
'Hello ' + this.props.email
);
}
});
angular.module('App').value('Hello', Hello);
angular.module('App')
.directive( 'hello', function( reactDirective ) {
return reactDirective( Hello, ['email'] );
});
My folder structure for my views goes like this:
~/Views/Dashboard/Index.cshtml - this is my main view that has ng-view
~/Views/Dashboard/Dash.cshtml - this gets put inside the main view
Inside Dash.cshtml I have
<div ng-controller="DashboardController" class="btn-group">
<hello email="{{name}}"></hello>
</div>
Here is my DashboardController.js file:
angular.module("App")
.controller("DashboardController", ['$scope', 'Welcome', '$cookies', function ($scope, Welcome, $cookies) {
Welcome.name().success(function (data) {
$scope.name = data.name;
});
$scope.ChangeName = function (val) {
Welcome.EditName(val).success(function (data) {
});
};
/*
$scope.isOff() = function () {
var value = $cookies.get('Off');
return value === null;
}
$scope.Off = function () {
$cookies.put('Off', 'This turns the green button off');
}
$scope.On = function () {
$cookies.remove('Off');
}
*/
}]);
Once the page has finished loading up all that is rendered is Hello undefined.
For some reason the email prop inside the hello tag is not being read or recognized or something.
I have already tested to make sure that my WelcomeFactory returns data by just displaying {{name}} on the screen and it works just fine.
Can someone tell my where I went wrong?
Thanks!
So the answer to this question was annoyingly simply. It really came down to syntax. What I mean by that was the React Component I had looked like this:
<hello email="{{name}}"></hello>
I kept getting undefined for the email, as it turns out the issue was the curly braces. Once I took those out the React Component looked like this:
<hello email="name"></hello>
Then I started to get a value output on the screen.
Hope this helps someone else looking into this issue!

How to Change Color of background in Angularjs Dynamically

I have a Poller that I have setup that has 2 files which are being queried. When new data has been found I am trying to set the color of my text background in the view but its just not happening.
If someone can solve this issue that would be great I am also welcome to suggestions to improving the structure of the code.
Service:
function Poller($http, $timeout) {
var projectcache = { response: [], calls: 0 };
var msgcache = { response: [], calls: 0 };
var newdata = false;
var msgdata = false;
var msgcolor = {};
var projectcolor = {};
var poller = function () {
$timeout(poller, 10000);
console.log("Begin Poller!");
$http.get('http://localhost/app/controllers/php/getProjects.php')
.then(function(r) {
if (r.data.projects.length > projectcache.response.length) {
newdata = true;
projectcolor = 'green';
} else {
newdata = false;
projectcolor = 'green';
};
angular.copy(r.data.projects, projectcache.response);
console.log("New Data Found: " + newdata);
});
$http.get('http://localhost/app/controllers/php/getMessages.php')
.then(function(m) {
if (m.data.messages.length > msgcache.response.length) {
msgdata = true;
msgcolor = 'green';
} else {
msgdata = false;
msgcolor = 'green';
};
angular.copy(m.data.messages, msgcache.response);
console.log("New Msg Found: " + msgdata);
});
};
poller();
return {
projects: projectcache.response,
messages: msgcache.response,
newdata: newdata,
msgdata: msgdata,
msgcolor: msgcolor,
projectcolor: projectcolor
};
};
View:
<li ng-class="{active: selectTab=='inbox'}" style="background-color:{{msgcolor}};" ng-click="selectTab='inbox'">Inbox</li>
<li ng-class="{active: selectTab=='projects'}" style="background-color:{{projectcolor}};" ng-click="selectTab='projects'">Projects</li>
Controller:
app.controller("taskbarController", ['$scope', 'authData', '$location', 'projectsModal', 'sendMessageModal', 'Poller',
function ($scope, authData, $location, projectsModal, sendMessageModal, Poller) {
$scope.msgcolor = Poller.msgcolor;
$scope.projectcolor = Poller.projectcolor;
}]);
My first thought is to use ng-class for this. I see you already have ng-class handling the display of your 'active' class.
If you'd like to try this approach out, I would:
1. Create css clases for each state/color you want to change to. (Can do this in external css file or between tags you create at the beginning of your page.
.successBackground {
background-color:green;
}
.errorBackground {
background-color:red;
}
Modify your ng-class attributes. Here I am assuming that success means that msgdata=true and error means that msgdata=false
Current html:
<li ng-class="{active: selectTab=='inbox'}" style="background-color:{{msgcolor}};" ng-click="selectTab='inbox'">Inbox</li>
<li ng-class="{active: selectTab=='projects'}" style="background-color:{{projectcolor}};" ng-click="selectTab='projects'">Projects</li>
Updated html:
<li ng-class="{active: selectTab=='inbox', successBackground:msgdata===true, errorBackground:msgdata===false}" ng-click="selectTab='inbox'">Inbox</li>
<li ng-class="{active: selectTab=='projects',successBackground:msgdata===true, errorBackground:msgdata===false}" ng-click="selectTab='projects'">Projects</li>
Now when your msgdata is updated, the successBackground and errorBackground are automatically updated based on the latest msgdata value.
Hope this helps!
#Elevant, the comment option didn't allow me to format my code snippets, so I am replying to your latest comment in this answer post.
I'm not sure if the watcher can listen to just the Poller object or if it'll need to listen to each attribute separately (msgColor, projectColor). In my code snippet here, I'll assume we cannot and we'll need to listen to each individually.
Current code:
$scope.msgcolor = Poller.msgcolor;
$scope.projectcolor = Poller.projectcolor;
Updated with watchers:
$scope.$watch('Poller.msgcolor', function(newValue,oldValue) {
$scope.msgcolor = Poller.msgcolor;
});
$scope.$watch('Poller.projectcolor', function(newValue,oldValue) {
$scope.projectcolor = Poller.projectcolor;
);
Though if you still wanted to look into the option to move $timeout, I would make the following changes (not sure if this matches what you had tried).
In the Poller service definition remove $timeout. Updated snippet:
function Poller($http)
Still in Poller, remove this line:
$timeout(poller, 10000);
In the Controller add $timeout - updated snippet:
app.controller("taskbarController", ['$scope', 'authData', '$location', 'projectsModal', 'sendMessageModal', 'Poller','$timeout'
function ($scope, authData, $location, projectsModal, sendMessageModal, Poller,$timeout)
Then in the controller, you would add:
$timeout(function(Poller) {
Poller.poller();
$scope.msgcolor = Poller.msgcolor;
$scope.projectcolor = Poller.projectcolor;
}, 10000);
I hope this helps, I haven't had a chance to test the code, so you may have to tinker around with it a bit. Let me know how it goes!

Why will my twitter widget not render if i change the view in angularjs?

Hi and thanks for reading.
I have a angular app im making and ive stumbled on a problem. set up as so
index.html-
<html ng-app="myApp">
...
<div ng-view></div>
<div ng-include="'footer.html'"></div>
...
</html>
I wont bother putting my routes its pretty simple /home is shows the /home/index.html and so on...
/home/index.html (default view when you come to the site)
<div class="responsive-block1">
<div class="tweet-me">
<h1> tweet me </h1>
</div>
<div class="twitter-box">
<twitter-timeline></twitter-timeline>
</div>
twitter timeline directive
directives.directive("twitterTimeline", function() {
return {
restrict: 'E',
template: '<a class="twitter-timeline" href="https://twitter.com/NAME" data-widget-id="XXXXXXXXXXXXXX">Tweets by #NAME</a>',
link: function(scope, element, attrs) {
function run(){
(!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+"://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs"));
console.log('run script');
};
run();
}
};
});
So I have just created a basic twitter directive using the tag from twitter. But when I change the view example to /blog then go back to /home the twitter widget no longer renders at all.
Im also using an $anchorScroll and if i jump to anyway on the page with this the widget also disappears. Any info would be great thanks.
See this post: https://dev.twitter.com/discussions/890
I think that you may be able to get the widget to re-render by calling
twttr.widgets.load().
If you find that this does not work, you will need to wrap this code into $timeout in your controller:
controller('MyCtrl1', ['$scope', '$timeout', function ($scope, $timeout) {
$timeout = twttr.widgets.load();
}])
To build on Sir l33tname's answer:
In services declaration:
angular.module('app.services', []).
service('tweetWidgets', function() {
this.loadAllWidgets = function() {
/* widgets loader code you get when
* declaring you widget with Twitter
* this code is the same for all widgets
* so calling it once will reference whatever
* widgets are active in the current ng-view */
!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+"://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");
};
this.destroyAllWidgets = function() {
var $ = function (id) { return document.getElementById(id); };
var twitter = $('twitter-wjs');
if (twitter != null)
twitter.remove();
};
});
Then in controller declarations:
angular.module('app.controllers', []).
controller('view_1_Controller', tweetWidgets) {
// load them all
tweetWidgets.loadAllWidgets();
}).
controller('view_2_Controller', tweetWidgets) {
// now destroy them :>
tweetWidgets.destroyAllWidgets();
});
Now whenever you leave view #1 to go to view #2, your controller for view #2 will remove the widgets associated with view #1 and when you return to view #1 the widgets will be re-instatiated.
The problem is because when Angular switches views the script tag that was originally inserted is not removed from the document. I fixed this on my own website by removing the Twitter script element whenever my Twitter timeline directive is not in the view. See the code below with comments.
function (scope, el, attrs) {
el.bind('$destroy', function() {
var twitterScriptEl = angular.element('#twitter-wjs');
twitterScriptEl.remove();
});
// function provided by Twitter that's been formatted for easier reading
function (d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0], p = /^http:/.test(d.location) ? 'http' : 'https';
// If the Twitter script element is already on the document this will not get called. On a regular webpage that gets reloaded this isn't a problem. Angular views are loaded dynamically.
if (!d.getElementById(id)) {
js = d.createElement(s);
js.id = id;
js.src = p + "://platform.twitter.com/widgets.js";
js.parentNode.insertBefore(js, fjs);
}
}(document, "script", "twitter-wjs");
}
Basically it's what Loc Nguyen say.
So every time you recreate it you must remove it first.
var $ = function (id) { return document.getElementById(id); };
function loadTwitter() {!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+"://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");}
var twitter = $('twitter-wjs');
twitter.remove();
loadTwitter();
Answer by #b1r3k works without problems :
put this in your controller:
$timeout(function () { twttr.widgets.load(); }, 500);
For those trying to load twttr.widgets.load() inside their controller, you will most likely get an error that twttr is not defined AT SOME POINT in your UX, because the async call to load the twitter script may not be completed by the time you controller instantiates and references twttr.
So I created this TwitterService
.factory('TwitterService', ['$timeout', function ($timeout) {
return {
load: function () {
if (typeof twttr === 'undefined') {
(function() {
!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');
})();
} else {
$timeout = twttr.widgets.load();
};
}
}
}])
and then call TwitterService.load() inside the controllers that require your widgets. This worked pretty well. It basically just checks if the twttw object exists and if it does, just reload the script... otherwise just reload the script.
Not sure if this is the best implementation, but it seems like all other solutions have edge cases where it will throw an error. I have yet to find one with this alternative.

Making body class the url of the page without the first forward slash

I am having trouble trying to get Angular to spit out the url of the page in to the page class each time the navigation changes the page. This is what I have so far:
$scope.currentPage = getCurrentPage;
var getCurrentPage = function () {
var login = 'login';
var url = $location.url().substring(1);
if (url = null || url = undefined) {
return login;
} else {
return url;
}
};
I have this on the content wrapping <div>:
<div class="" ng-controller="AppCtrl" ng-class="currentPage + 'Page'"></div>
Any hardcore Angulars out there? Please help.
JP
A few things to do differently:
I don't think the function is actually getting called, so:
$scope.currentPage = getCurrentPage();
$scope.currentPage += "Page"; // just keeping the concatenation out of the markup
EDIT: I think I'm wrong about the above statement. I'm still fuzzy on when to call() vs reference the function in situations like this...
Also, this line needs some retouching of the operators:
if (url == null || url === undefined);
And, I am not sure ng-class is entirely necessary in this case. It is usually used to evaluate an expression. I would simply use:
<div class="" ng-controller="AppCtrl" class="{{currentPage}}"></div>
Hopefully someone else can verify me on all of these points, as I am fairly new to JS and AngularJS.
For anyone who comes across this and would like a more verbose answer, here you go!
In view:
<div class="" ng-controller="AppCtrl" class="currentPage"></div>
In controller:
$scope.$watch(function () {return $location.url();}, function () {
var getCurrentPage = function () {
var login = 'loginPage';
var currentUrl = $location.url().substring(1);
if (currentUrl === '' || currentUrl === undefined) {
return login;
} else {
return currentUrl + 'Page';
}
};
$scope.currentPage = getCurrentPage();
console.log($scope.currentPage);
});

Resources