ng-click not working when element compiled in controller - angularjs

How do I use $compile to get ng-click working on a block of code? My code currently displays a suggestion box when the parameters for a particular event are met. However, I want to let the user hide the suggestion box by clicking on the close button.
View
<textarea class="form-control suggest"
ng-keyup="vm.suggestActivate($event.keyCode)"
ng-mouseenter="vm.suggestActivate(32)"
rows="3"
ng-model="vm.summaryData"></textarea>
Controller
var vm = this;
var suggestionElement = document.createElement('div');
vm.suggestActivate = function(keyCode) {
if(keyCode === 32) {
if(vm.summaryData) {
var words = vm.words;
var suggestions = null;
suggestions = '<div style="padding-bottom: 20px"><strong>Suggested next word:</strong></div>'
for(var i = 0; i < 5; i++) {
suggestions += '<div style="padding-bottom: 20px">' + words[Math.floor(Math.random() * words.length)] + '</div>';
}
suggestions += '<div class="btn btn-default" ng-click="vm.suggestDeactivate()">Close</div>'
suggestionElement.innerHTML = suggestions;
suggestionElement.setAttribute('style', 'background: #B0B0B0; padding: 20px; position: relative; top: -3.9em; width: 25%');
suggestionElement.style.display = 'block';
var targetElement = event.srcElement;
targetElement.parentNode.appendChild(suggestionElement);
}
}
else {
suggestionElement.style.display = 'none';
}
};

try with $compile
targetElement.parentNode.appendChild($compile(suggestionElement)($scope));
mention that you have to inject $compile first.
ADD:
use angular.element to add new elements to DOM.
refer below demo:
angular.module("app", [])
.controller("myCtrl", function($scope, $compile) {
$scope.add = function() {
var strNewElement = document.createElement('div');
strNewElement.innerHTML = '<button ng-click="test()">Test</button>';
angular.element(event.srcElement.parentNode).append($compile(strNewElement)($scope));
};
$scope.test = function() {
alert('I am new element.');
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<div ng-app="app" ng-controller="myCtrl">
<textarea ng-keypress="add()" rows="3"></textarea>
</div>

Related

How do I integrate Stripe credit card field with `ng-disabled` attribute of a `button`?

I have a form in my page with AngularJS and Stripe JS.
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"> <script type="text/javascript">
var app= angular.module("app",[]);
app.config(function($interpolateProvider){
$interpolateProvider.startSymbol("[[[");
$interpolateProvider.endSymbol("]]]");
});
app.controller("Ctrl",function(stripeService, $scope, $rootScope){
$scope.name= "World";
$scope.stripeCompleted= false;
stripeService.start();
$rootScope.on("stripedone", function(e,stripeEvent){
$scope.stripeCompleted= stripeEvent.complete;
$scope.$apply();
});
});
app.service("stripeService", function($window,$rootScope){
function start(){
var btn= document.querySelectorAll("#test")[0];
var displayError= document.getElementById('card-errors');
var stripe= Stripe("{{ stripe_key }}");
var elements= stripe.elements();
var style= {
base: {
fontSize: "1.1875rem",
fontSmoothing: "always",
fontWeight: "600"
}
};
var card= elements.create("card", {style:style});
card.mount("#card-element");
card.addEventListener('change', function(event) {
if (event.error) {
displayError.textContent = event.error.message;
} else {
displayError.textContent = '';
}
if (event.complete) {
$rootScope.$broadcast("stripedone",event);
} else {
$rootScope.$broadcast("stripedone",event);
}
});
var formID= "register-form";
var form= document.getElementById(formID);
form.addEventListener("submit",function(event){
event.preventDefault();
stripe.createToken(card).then(function(result){
if(result.error) {
displayError.textContent= result.error.message;
} else {
stripeTokenHandler(result.token, formID);
}
});
});
return {"start":start};
}
});
// tut https://stripe.com/docs/stripe-js/elements/quickstart#create-form
function stripeTokenHandler(token, formID) {
// Insert the token ID into the form so it gets submitted to the server
var form = document.getElementById(formID);
var hiddenInput = document.createElement('input');
hiddenInput.setAttribute('type', 'hidden');
hiddenInput.setAttribute('name', 'stripeToken');
hiddenInput.setAttribute('value', token.id);
form.appendChild(hiddenInput);
// Submit the form
form.submit();
}
</script> <form id="register-form" name="regForm" method="post>
<input ng-model="reg.email" type="email" name="username">
<div id="stripe-wrapper">
<div id="card-element"></div>
</div>
<small id="card-errors" class="text-danger" role="alert">{{ ccErrMsg }}</small>
<br>
<button type="submit" ng-model="reg.btn" ng-disabled="!regForm.username.$valid>Register</button>
</form>
I want for my button to be un-clickable unless the user fills out the Stipe credit card section correctly. How do I make it so my button is disabled unless the the Stripe credit card fields are filled out correctly?
Update: Following karthick's answer gives me a new error:
angular.js:13642 TypeError: stripeService.start is not a function
at Object.<anonymous> ((index):135)
at Object.invoke (angular.js:4708)
at P.instance (angular.js:10177)
at n (angular.js:9096)
at g (angular.js:8459)
at angular.js:8339
at angular.js:1782
at m.$eval (angular.js:17378)
at m.$apply (angular.js:17478)
at angular.js:1780
You should use a AngularJS directive in this case. This is nothing to put in a controller, service, factory or component. Once you use a directive your code will be much smarter and become fully supported by AngularJS diggest cycles and DOM bindings. This is how the documentation introduces directives:
What are Directives? At a high level, directives are markers on a DOM element (such as an attribute, element name, comment or CSS class) that tell AngularJS's HTML compiler ($compile) to attach a specified behavior to that DOM element (e.g. via event listeners), or even to transform the DOM element and its children.
> Demo fiddle
Your solution could be smart like this one by using a nice directive:
View
<body ng-app="angularjs-starter">
<script src="https://js.stripe.com/v3/"></script>
<div ng-controller="MainCtrl">
<form name="regForm" id="register-form">
<label>Mail</label>
<input ng-model="reg.email" type="email" name="username">
<div stripe-validator
stripe-complete="stripeCompleted"
stripe-form-id="register-form"></div>
<br>
<button ng-model="reg.btn" ng-disabled="stripeCompleted === false || !regForm.username.$valid">Register</button>
</form>
</div>
</body>
AngularJS Application / Stripe.js card validation directive
var app = angular.module('angularjs-starter', []);
app.controller('MainCtrl', function($scope, $rootScope) {
//Init stripe state via controller
$scope.stripeCompleted = false;
});
app.directive('stripeValidator', function() {
return {
restrict: 'A',
template: `
<div id="stripe-wrapper">
<div id="card-element"></div>
</div>
<small id="card-errors" class="text-danger" role="alert">{{ ccErrMsg }}</small>
<input type="hidden" name="stripeToken" ng-value="stripeToken" />`,
scope: {
"stripeComplete": '=',
"stripeFormId": '#',
"stripeError": '=',
"stripeToken": '=',
},
link: function(scope, element, attrs) {
//Init
var stripe = Stripe("pk_test_6pRNASCoBOKtIshFeQd4XMUh");
var elements = stripe.elements();
var card = elements.create("card");
var form = document.getElementById(scope.stripeFormId);
//mount card element https://stripe.com/docs/stripe-js/reference#element-mount
card.mount("#card-element");
//add event listener
card.addEventListener('change', function(event) {
//check for errors
if (event.error) {
scope.ccErrMsg = event.error.message;
} else {
scope.ccErrMsg = '';
}
//check for complete
scope.stripeComplete = event.complete ? true : false;
//apply scope
scope.$apply();
});
//inject form submit event
form.addEventListener("submit", function(event) {
//prevent form submit
event.preventDefault();
//handle token, handle error and form submit forward
stripe.createToken(card).then(function(result) {
if (result.error) {
scope.ccErrMsg = event.error.message;
scope.stripeToken = '';
} else {
scope.ccErrMsg = '';
scope.stripeToken = result.token;
}
//apply scope
scope.$apply();
//forward submit
form.submit();
})
});
}
}
});

Why I cannot change a value of angular property in html?

I have a tooltip which popups on focus using angular popovers. What I need to do is when I click a button, to change it to popup on blur. It changes but doesn't change the tooltip behavior.
Here is the plunkr: http://plnkr.co/edit/L1oeZQrQdF0AdJMKVsG6?p=preview
Below is the code:
<input type="text"
ng-model="value"
value="{{value}}"
uib-popover-template="htmlPopover"
popover-trigger="{{triggerOn}}"
popover-popup-close-delay="1000"
class="form-control">
<script type="text/ng-template" id="myPopoverTemplate.html">
<div>
<button ng-click="test()"><b style="color: red">Add message</b></button>
</div>
</script>
controller
function ($scope) {
$scope.value = "Click me!";
$scope.dynamicPopover = {
content: 'Hello, World!',
templateUrl: 'myPopoverTemplate.html',
title: 'Title'
};
$scope.message = 'Trigger: none';
$scope.triggerOn = "focus";
$scope.changeTrigger = function(){
$scope.triggerOn = "blur";
$scope.message = "Should trigger on blur";
}
$scope.test = function(){
$scope.value = "test me click";
}
$scope.htmlPopover = 'myPopoverTemplate.html';
});
Looking at the source code for the uib-popover directive, one can that the triggers are set when the directive is linked.
compile: function(tElem, tAttrs) {
var tooltipLinker = $compile(template);
return function link(scope, element, attrs, tooltipCtrl) {
var tooltip;
var tooltipLinkedScope;
var transitionTimeout;
var showTimeout;
var hideTimeout;
var positionTimeout;
var appendToBody = angular.isDefined(options.appendToBody) ? options.appendToBody : false;
//TRIGGERS SET AT LINK TIME
var triggers = getTriggers(undefined);
var hasEnableExp = angular.isDefined(attrs[prefix + 'Enable']);
var ttScope = scope.$new(true);
var repositionScheduled = false;
var isOpenParse = angular.isDefined(attrs[prefix + 'IsOpen']) ? $parse(attrs[prefix + 'IsOpen']) : false;
var contentParse = options.useContentExp ? $parse(attrs[ttType]) : false;
var observers = [];
var lastPlacement;
If one wants to change how the directive triggers, the directive needs to be re-compiled with the $compile service on each change to the triggers.

Angular Modal triggering an ng-hide/ng-show

I'm building out a modal that takes 10 lines of input and when clicked to close randomizes them and "should" display out side the modal. Through other research I have found that I must use a service to make everything work right. However this service is not properly changing over my var for changing over true/false for my ng-hide.
Some code below
Main HTML
<body ng-controller="ModalDemoCtrl">
<my-modal-content></my-modal-content>
<button class="btn btn-small"
ng click="open(lg,'ModalInstanceCtrl')">Add</button>
<ul ng-controller="MainCtrl" >
<button class="btn" ng-click="test()">Test</button>
<li ng-hide="toggle" ng-repeat="random in randomTeams">{{random.team}}</li>
</ul>
<script src="js/vendor/angular.js"></script>
<script src="js/vendor/angular-animate.js"></script>
<script src="js/vendor/angular-ui.js"></script>
<script src="app.js"></script>
<script src="js/Modal.js"></script>
<script src="js/setTeams.js"></script>
<script src="js/randomizeTeamservice.js"></script>
</body>
My-modal-content
<div class="modal-header">
<h3 class="modal-title">Set Team Names</h3>
</div>
<div class="modal-body" ng-controller="MainCtrl">
<div class="input-append" ng-repeat="team in teams | limitTo: 10">
<input class="form-control" type="text" ng-model="team.team"
placeholder="Team {{$index + 1}}" value="{{$index}}">
</div>
<div class="modal-footer">
<button class="btn btn-primary" type="button" ng-click="ok();
add(this)">OK</button>
<button class="btn btn-warning" type="button" ng-
click="cancel()">Cancel</button>
</div>
</div>
Modal Controller
app.controller('ModalDemoCtrl', function ($scope, $uibModal) {
$scope.animationsEnabled = true;
$scope.open = function (size,controller) {
$uibModal.open({
animation: $scope.animationsEnabled,
templateUrl: 'myModalContent.html',
controller: controller ,
size: size,
});
};
});
// Please note that $uibModalInstance represents a modal window (instance)
dependency.
// It is not the same as the $uibModal service used above.
app.controller('ModalInstanceCtrl', function ($scope, $uibModalInstance, randomService) {
$scope.toggle = randomService.showTeams();
$scope.ok = function () {
$uibModalInstance.close({
}
);
$scope.toggle = false;
console.log($scope.toggle);
};
$scope.cancel = function () {
$uibModalInstance.dismiss('cancel');
};
});
Finally the Service
app.factory("randomService", function(){
var teams = [{},{},{},{},{},{},{},{},{},{}];
var randomTeams = teams.slice(0);
var showTeams = true;
return{
randomTeams: function(){
return randomTeams;
},
teams: function(){
return teams;
},
showTeams: function(){
return showTeams;
}
}
});
So why won't the ng-hide work?
Problem solved. Moved some things around but here it is.
Moved the randomize function to the controller and added a getter so a function will retrieve the toggle (true/false) value.
app.controller('MainCtrl', function($scope, randomService) {
$scope.teams = randomService.teams();
$scope.randomTeams = randomService.randomTeams();
$scope.toggle = function(){
return randomService.getTeams();
}
$scope.add = function(team, show) {
for( var i = 0; i < 10; i++){
$scope.teams.splice(i, 1, team.teams[i]);
}
randomService.shuffle($scope.randomTeams);
$scope.toggle();
};
});
In the service factory in addition to adding the shuffle function and a getter function. I added a line of code that will change the showTeams from true to false.
app.factory("randomService", function(){
var teams = [{},{},{},{},{},{},{},{},{},{}];
var randomTeams = teams.slice(0);
var showTeams = true;
return{
randomTeams: function(){
return randomTeams;
},
teams: function(){
return teams;
},
shuffle: function shuffle(array) {
showTeams = false;
var currentIndex = array.length, temporaryValue, randomIndex;
// While there remain elements to shuffle...
while (0 !== currentIndex) {
// Pick a remaining element...
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
// And swap it with the current element.
temporaryValue = array[currentIndex];
array[currentIndex] = array[randomIndex];
array[randomIndex] = temporaryValue;
}
return array;
},
getTeams: function(){
return showTeams;
},
}
});
lastly ng-hide="toggle: changed to ng-hide="toggle()"

How to write directive to hide div clicking on it or anywhere on page?

I am writing directive for first time.What i am trying to do is when user click on div it open that div, if div is opened and user click anywhere on page to close that div and if div is closed and user click anywhere on page that div stay closed. My html looks like this:
<div id="loggedIn" close-logged-in class="fade-show-hide" ng-show="loggedInOpened" default-display='block' ng-cloak>
#Html.Partial("~/Views/Shared/_LoggedInPartial.cshtml")
</div>
My angular:
$scope.toggleLoggedIn = function () {
$scope.loggedInOpened = !$scope.loggedInOpened;
$scope.languagesOpened = false;
$scope.loginOpened = false;
};
And my directive looks like this:
'use strict';
angular.module("mainModule").directive('closeLoggedIn',['$window', function ($window) {
return {
// bind a local scope (i.e. on link function scope) property
// to the value of default-display attribute in our target <div>.
scope: {
defaultDisplay: '#'
},
restrict: 'A',
link: function (scope, element, attrs) {
var el = element[0];
el.style.display = scope.defaultDisplay || 'block';
angular.element($window).bind('click', function(){
if(scope.defaultDisplay == 'block')
el.style.display = 'none';
});
}
};
}]);
Any suggestion?
<div ng-controller="MyCtrl" ng-show="userclick==1" class="sample">
<div class="test" ng-hide="hideDiv" > hideDiv </div>
</div>
Use ng-show
modify the value of var userclick to zero on whatever condition you mentioned and the div will be hidden till the userclick value is not 1.
Be sure the the value is getting applied updated.
print the updated value to console.
use
$scope.$apply(function () {
$scope.userclick = 0;
});
As my understanding I have created a sample structure it might be help full for you please check..
HTML:
<div ng-controller="MyCtrl" ng-click="testing() "class="sample">
<div class="test" ng-hide="hideDiv" > hideDiv </div>
</div>
JS:
var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
$scope.hideDiv = false;
$scope.testing = function () {
if ($scope.hideDiv) {
$scope.hideDiv = false;
} else {
$scope.hideDiv = true;
}
}
}
CSS:
.sample{
background-color: red;
height:100px;
width: 100%;
}

why the ng-class not changed?

Examples of the problem:
http://jsfiddle.net/paloalto/DTXC2/
HTML:
<div ng-app="app">
<div id="wrapper" ng-controller="AppController" ng-class="showChatPanel">
<div id="tabBar" class="ui vertical icon menu inverted" ng-controller="TabBarController">
<a class="item switchChatBtn" data-tab="showChatWraper">Open Chat Panel</a>
</div>
<div id="chatWraper" class="ui segment">Chat Panel Opend!!</div>
</div>
</div>
Javascript:
angular.module('app', ['app.controllers']);
var controllers = angular.module('app.controllers', []);
controllers.controller('AppController', function AppController($scope, $log, $http) {
$scope.showChatPanel = '';
$scope.$on("switchChatPanel", function (event, msg) {
console.log(msg);
$scope.showChatPanel = msg;
console.log($scope.showChatPanel);
// $scope.$broadcast("switchChatPanel-done", msg);
});
$scope.$watch('showChatPanel', function(newVal, oldVal) {
if(newVal){
console.log('yeah! It is a newVal !!!');
} else {
console.log('still oldVal ');
}
});
});
controllers.controller('TabBarController', function TabBarController($scope, $log, $http) {
var tabBarItem =$('#tabBar > .item');
tabBarItem.click(function(){
var tabClass = $(this).data('tab');
console.log(tabClass);
$scope.$emit("switchChatPanel", tabClass);
});
});
CSS:
#chatWraper {
display:none;
}
.showChatWraper #chatWraper{
display:block;
}
=====
I finally solved it using jQuery, but I still wonder why angular not success.
controllers.controller('TabBarController',function TabBarController ($scope,$log,$http) {
var tabBarItem =$('#tabBar > .item');
var chatPanelOpen = false;
tabBarItem.click(function(){
var tabClass = $(this).data('tab');
if(!chatPanelOpen){
$('#wrapper').addClass(tabClass);
chatPanelOpen = true;
} else{
$('#wrapper').removeClass(tabClass);
chatPanelOpen = false;
}
})
})
https://gist.github.com/naoyeye/7695067
========
UPDATE
http://jsfiddle.net/paloalto/DTXC2/17/
You shouldn't be doing DOM manipulation like that in the controller. The correct way to do this is like this
<div ng-controller="TabBarController">
<div ng-click="toggleChatPanel()" ng-class="{tabClass: isChatPanelOpen}">
</div>
controllers.controller('TabBarController', function ($scope) {
$scope.isChatPanelOpen = false;
$scope.toggleChatPanel = function () {
$scope.isChatPanelOpen = !$scope.isChatPanelOpen;
};
});

Resources