AngularJS - stop ng-click expression evaluation after $event.stopPropagation - javascript

I'm struggling stoping ng-click from evaluating the rest of expression after event cancelation was called.
I've made the following plnkr.
If you click on "Click me" text, you'll see that the value is changing, although the clickMe function is calling all known methods (by me, at least) to stop the event.
Markup:
<body ng-controller="Home" ng-init="model = { value: false }">
<div ng-click="clickMe($event); model.value = true;">Click me!!</div>
{{ model.value }}
</body>
App:
APP.controller('Home', function($scope){
$scope.clickMe = function($event){
$event.stopPropagation();
$event.preventDefault();
return false;
}
})
I know that I can pass arguments to clickMe function and if "isOK" is false, then update what I need, but for many reasons bound to my application, I try not to do that.
Update
In my app I had something like this on ng-click
model.PS.title = value; model.PS.desc = value2; model.PS.isSce = value3
I was blind and I couldn't see that I can make it a lot simpler like this:
model.PS = { title: value, desc: value2, isSce: value3 }
... and in this case i can use the shorthand expression for if statement like #Emmanuel mentioned.
I was thinking that ngClick evaluates the expressions inside it like a function block, so if it meets a return false (or something similar) it stops there.

You could evaluate your expression after you have run your click handler with $scope.$eval.
You can pass your expression that you want to run after the click is valid to your click handler and evaluate the expression depending on your isOK variable.
I've extended the demo a bit to show the functionality of $eval.
Please find the updated plunkr here.
HTML
<body ng-controller="Home" ng-init="model = { value: false }; model2 = {value:true}">
<div ng-click="clickMe($event, 'model.value = true; model2.value = false;');">Click me!!</div>
{{ model.value }}
{{ model2.value }}
<br/>
check to enable click me!
<input type="checkbox" ng-model='isOK'/>
</body>
JS
var APP = angular.module('myApp', []);
APP.controller('Home', function($scope){
//var isOk = true;
$scope.isOK = false;
$scope.clickMe = function($event, args){
if($scope.isOK){
console.log(args);
$scope.$eval(args);
return false;
} else {
console.log('isOk false:', args);
}
}
});

How about
<body ng-controller="Home" ng-init="model = { value: false }">
<div ng-click="model.value = clickMe($event) ? true : model.value;">Click me!!</div>
{{ model.value }}
</body>
Leave App as is
APP.controller('Home', function($scope){
$scope.clickMe = function($event){
$event.stopPropagation();
$event.preventDefault();
return false;
}
})

Related

AngularJS ng-if directive using a function not returning a value

I'm still pretty new to AngularJS. Following multiple tutorials online, I used a javascript function inside a ng-if directive to validate whether a group name already exists in an array. If it does, the ng-if block is skipped for the next ng-repeat iteration. If it doesn't, add the group name to an array and create the ng-if block. This is what the HTML code looks like in the partial:
HTML
<span ng-if="checkGroups(service.group.name)">
<!--Make nested list-->
</span>
This is a simplified version of the javascript:
JAVASCRIPT
(function () {
'use strict';
MainApp.controller('MainController', [
'$scope',
'Filters',
'helper',
'$timeout',
'$filter',function(
$scope,
Filters,
helper,
$timeout,
$filter) {
var MainCtrl = this;
//Function to check group array variable
$scope.usedGroups = [];
$scope.checkGroups = function(name) {
var isValid = true;
for(var i = 0; i < $scope.usedGroups.length; i++) {
if($scope.usedGroups[i] == name){
isValid = false;
break;
}
}
if(isValid == true){
$scope.usedGroups.push(name);
console.log($scope.usedGroups);
}
return isValid;
}
}
]);
})();
I've used console.log() to return the values and I do get an array with the group names inside as well as a true or false value being returned. The issue is the ng-if function seems to only return false. If I switch the directive function to "checkGroups(service.group.name) == false", it will keep creating the HTML block regardless. Any ideas what I can do to fix this?
I've replaced your service with just a simple array of objects in the controller, since I don't want to create it. But below should do the trick, notice that I flipped the logic around. This will display it only once and will not display it/insert if the object exists already.
var app = angular.module("MyApp", []);
var MyController = function($scope) {
// Somewhere in your service
$scope.service = {
group: [{
name: "Foo"
}
]
}
//Function to check group array variable
$scope.usedGroups = [];
$scope.checkGroups = function(name) {
for (var i = 0; i < $scope.usedGroups.length; i++) {
if ($scope.usedGroups[i] == name) {
console.log(name + " exists already!", $scope.usedGroups);
return true;
}
}
$scope.usedGroups.push(name);
console.log(name + " doesn't exist!\n", $scope.usedGroups);
return false;
}
}
app.controller(MyController, "[$scope, MyController]");
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
</head>
<body ng-app="MyApp">
<div ng-controller="MyController">
<div ng-repeat="item in service.group"> <!-- This is ran three times, just like calling it with three different names, but I put it in the controller to make it accessible -->
<span ng-if="checkGroups(item.name)">
{{ item.name }}
<!--Make nested list-->
</span>
</div>
</div>
</body>
</html>
Use the controller reference:
MyController.checkGroups(service.group.name)

Confuse about angular expressions

Here is what I want achieve: when user input some text, I will find the character a and using <em> tag to emphasize it in another <label>, here is my html markup:
<div ng-controller="demoController">
<input type="text" ng-model="input" />
<label>{{emphasize()}}</label>
</div>
As showing above, I'm using method emphasize in demoController to do emphasize job:
myapp.controller('demoController', function ($scope) {
$scope.input = 'Hello';
$scope.emphasize = function () {
return $scope.input.replace(/a/g, '<em>a</em>');
}
});
But the result is, the angular escape the <em> tag. For instance , if I input apple, then the label would show <em>a</em>pple, not I want: apple.
So why does this happened? Is there a way I can prevent this happen or another way to do it?
To do so a simple ng-bind-hmtl will do the trick :
<span ng-bind-html="emphasize()"></span>
But this is not really safe so it's always better to add this on your controller :
myapp.controller('demoController', function ($scope, $sce) {
$scope.input = 'angularJS';
$scope.emphasize = function () {
var res = $scope.input.replace(/a/g, '<em>a</em>');
return $sce.trustAsHtml(res);
}
});
add a filter to your module:
myapp.filter('unsafe', ['$sce', function ($sce) {
return function (a) { return $sce.trustAsHtml(a) };
}]);
and in your view:
<span ng-bind-html="emphasize() | unsafe"></span

One time binding not working inside custom AngularJS Directive

I'm trying to wrap my head around the reason that the one-time bound value (obj.value) inside the directive in this code example is being updated?
Updating the first field will update the bound value inside the directive only once, as expected. Afterwards, inside the directive, when clicking "edit", it will also update the one-time bound value AND also update the parent scope. Updating the first field again will not change the value inside the directive.
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular.min.js"></script>
</head>
<body ng-app="myApp" ng-controller="myCtrl" ng-model-options="{updateOn: 'blur'}">
Enter value here first, then press edit:<br>
<input type="text" ng-model="t.value"><br>
<br>
Press edit, change the value and press copy:
<my-directive obj="t"></my-directive><br><br>
<script>
var myApp = angular.module('myApp', []);
myApp.directive('myDirective', function() {
var directive = {};
directive.restrict = 'E';
directive.template = '<div ng-switch="edit">\
<div ng-switch-default>[{{ ::obj.value }}]<button ng-click="toggle()">edit</button></div>\
<div ng-switch-when="true">\
<input type="text" ng-model="clone.value">\
<button ng-click="copy()">copy</button>\
</div>\
</div>';
directive.scope = {
obj: '='
};
directive.controller = function($scope) {
$scope.edit = false;
$scope.toggle = function() {
$scope.edit = true;
$scope.clone = angular.copy($scope.obj);
}
$scope.copy = function() {
$scope.obj = angular.copy($scope.clone);
$scope.edit = false;
}
}
return directive;
});
myApp.controller('myCtrl', function(){
});
</script>
</body>
http://plnkr.co/edit/tbC3Ji6122gdqt4XbZpI?p=preview
In 1.3 they added a new syntax for helping with one-way binding, "::". So you just need to change your directive implementation to obj="::t".
Here's an update to your plnkr: http://plnkr.co/edit/7lsiX1ItPiQoVpJcQ6iW?p=preview
Here's a nice article that explains a bit more
It is because of ng-switch. Every time it's expression is recalculated the directive is 'redrawn'. And every time is does that the one time expression is also recalculated.
If you change your template to:
directive.template = '{{::obj | json}}<div ng-switch="edit">
etc...
you will see it won't change because it is outside of the ng-switch.

Angular ng-class not updating after ng-change is fired

EDIT: It seems my issue is more complex than the simple typo in the code below. I have 3rd party components interacting and raising change events on the inputs which angular is picking up when I don't want it to. The problem is somewhere in there. I will try to find a simple fiddle and update the question if I manage it.
I have a pair of inputs, which have an ng-model and share an ng-change function. The ng-change sets a boolean value in the controller which is supposed to update the class(es) on the inputs through an ng-class directive. However the first of the two inputs never seems to get any updates to it's class. Here is a simplified version:
View:
<div ng-controller='TestCtrl'>
<input type="text" ng-class="{ 'invalid': firstInvalid }" ng-model="firstValue" ng-change="doOnChange()"></input>
<input type="text" ng-class="{ 'invalid': secondInvalid }" ng-model="secondValue" ng-change="doOnChange()"></input>
</div>
Controller:
function TestCtrl($scope) {
$scope.firstInvalid = false;
$scope.secondInvalid = false;
$scope.firstValue = '';
$scope.secondValue = '';
$scope.doOnChange = function () {
console.log('change fired');
$scope.firstInValid = !$scope.firstInvalid;
$scope.secondInvalid = !$scope.secondInvalid;
};
};
Codepen:
http://codepen.io/Samih/pen/ZGXQPJ
Notice how typing in either input, the second input updates with the class just as I would expect, however the first never gets the 'invalid' class.
Thanks in advance for your help.
Check your code for typos:
This line
$scope.firstInValid = !$scope.firstInvalid;
should be
$scope.firstInvalid = !$scope.firstInvalid;
It should be $scope.firstInvalid, not $scope.firstInValid.
It seems to work for me :
Just edited 'firstInvalid'.
View:
<div ng-controller='TestCtrl'>
<input type="text" ng-class="{ 'invalid': firstInvalid }" ng-model="firstValue" ng-change="doOnChange()"></input>
<input type="text" ng-class="{ 'invalid': secondInvalid }" ng-model="secondValue" ng-change="doOnChange()"></input>
</div>
Controller:
function TestCtrl($scope) {
$scope.firstInvalid = false;
$scope.secondInvalid = false;
$scope.firstValue = '';
$scope.secondValue = '';
$scope.doOnChange = function () {
console.log('change fired');
$scope.firstInvalid = !$scope.firstInvalid;
$scope.secondInvalid = !$scope.secondInvalid;
};
};
Codepen: http://codepen.io/vikashverma/pen/BNwKmQ

How to display output of function from ng-init in div?

Trivial to most
Ok so I have this div that I want to bind some values into AFTER the ng-if has been set to true and AFTER the ng-init has been called.. at this point my ng-init is getting called but the message isnt binding. I might have the wrong tags on but you get what I mean.. i want the function to be called after my statement becomes true.
<div ng-repeat="field in model.fieldData">
<button class="btn-xs" ng-show="!isLocked(field.Id)" ng-click="openField(field)">
Edit
</button>
<div ng-if="isLocked(field.Id)" ng-init="msg = getLockMessage(field.Id)" ng-bind="msg">
</div>
</div>
Look this plunker:
plunker
Is a little example based on your html, and works. Make a little changes the element msg change inside the element parent, because if exist a lot of fieldData then the same variable is replaced.
The JS:
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.model = {
fieldData: [{
Id: 123,
locked: false,
}]
};
$scope.isLocked = function(element) {
return element.locked;
}
$scope.openField = function(element) {
element.locked = true;
}
$scope.getLockMessage = function(element) {
return "message from " + element.Id
}
});
And the html:
<div ng-repeat="field in model.fieldData">
<button class="btn-xs" ng-show="!isLocked(field)" ng-click="openField(field)">
Edit
</button>
<div ng-if="isLocked(field)" ng-init="field.msg = getLockMessage(field)" ng- bind="field.msg">
</div>
</div>

Categories

Resources