Is a variable enclosed in {{ }} checked for changes on my page? - angularjs

I thought there was a way that I can just display something on the page and not have AngularJS check it for changes.
Can someone tell me how to do this? Is it just if I have a label like this:
{{ abc }}

You may use binding like this {{::abc}} so you app will not watch for changes after first render of the data. See one-time-binding doc

It is a scope variable. Means your controller has an scope object as $scope if you define any variable like $scope.abc = "string". then a new property called abc will be created in your controller scope.
In AngularJS scope object was always watched and once any change in that object made it will reflect in the view.

Thankfully, Angular 1.3 and up versions has put a lot of effort into performance with the feature One-time binding using {{ ::abc }} works:
angular
.module('MyApp', [])
.controller('MyController', function($scope, $timeout) {
$scope.abc = 'Some text'
$timeout(function() {
$scope.abc = 'new value';
console.log('Changed but not reflected in the view:', $scope.abc);
}, 1000);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.0/angular.js"></script>
<div ng-app="MyApp" ng-controller="MyController">
<p>{{ ::abc }}</p>
</div>

As there is a two-way data binding, angular will watch for any changes on the variable $scope.abc and update the DOM with the changes. However if you want to make sure it does not watch for any changes you can go for the one-way binding, where any change made to the variable will not be watched upon by angular. You can do this using by {{::abc}} or ng-bind="::abc".
For example refer - https://jsfiddle.net/m8L2pogg/

Related

AngularJS. Prevent updating ALL data in $scope

I am novice in Angular and I have a question.
I noticed that angular updates all scope data on view (am I right?), even if it has been changed only one variable (that renders on view). Is it normal ? What if I have large data on view and I want to update it only when this is data being changed.
Code for example (every time when scope.word is being modified function func is executing):
<div ng-app="myApp" ng-controller="myCtrl">
Word: <input ng-model="word">
{{func()}}
</div>
<script>
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
$scope.word = "John Doe";
$scope.func = function(){
alert("Who dared to disturb me !? >(");
};
});
</script>
Is it normal? - You bet it's normal, this is the whole idea.
What you're doing is not a good practice at all. However, because when you bind a function as an expression in the view, Angular doesn't "know" when it should update the expression in the view, so it updates it on every digest cycle that happens a lot! Almost every time the user interacts with the view (Click, scroll) or if anything is changed on the controller side, so you might find yourself ending up with this error.
You should bind properties to the view, not functions. Example:
angular.module('app',[]).controller('ctrl', function($scope) {
$scope.welcomeMessage = "Hi, welcome to AngularJS!";
$scope.updateMessage = function(message) {
$scope.welcomeMessage = message;
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<strong>{{ welcomeMessage }}</strong>
<hr>
<input type="text" ng-model="msg">
<button ng-click="updateMessage(msg)">Update Message</button>
</div>
Note that if you know that you need to bind a property in the view only once, then you can use one time binding:
<strong>{{ ::welcomeMessage }}</strong>
Or
<strong ng-bind="::welcomeMessage"></strong>
By adding :: to the expression you prevent angular from tracking this expression after it is bound to the view the first time, and will not update it again, even if it was changed on the controller. Which is good for the performances of your app and can dramatically improve them.
Here is a working example of one-time binding: https://jsfiddle.net/hu9zcbwh/2/ (I couldn't create stack-snippet because it doesn't have angular 1.3 where this feature was first introduced)
I'm editing this with #MaximShoustin comment, that should help make this more clear and summarizes better the differences between the normal binding and one time binding:
ng-bind or {{}} generates one watcher and it will be fired after each digest cycle. On the other hand, :: expression creates watcher and cancels it once the value is not undefined
Sorry, not a native English speaker :(

Directive with ng-model Attribute Not Resolving Using $http

Trying to make a rating directive but I'm stuck at getting rating2 to work. The first rating worked because the rating1 is hardcoded within the controller. But normally I have to get the saved rating from the db, which I'm trying to do with rating2, as u can see the value is fetched but the directive is not appearing.
https://codepen.io/eldyvoon/pen/MbBNLP
<div star-rating ng-model="rating.rating1" max="10" on-rating-select="rating.rateFunction(rating)"></div>
<br>but rating2 is actually there:
{{rating.rating2}}
<star-rating ng-model="rating.rating2" readonly="rating.isReadonly"></star-rating>
Need expert of directive to help.
Initiate rating2 :
function RatingController($http) {
this.rating1 = 5;
this.rating2 = 0; //ADD THIS LINE
var self = this;
it works for me
check here
First of all, I'm not a directive expert but i'm trying to help. I think that when html is first load, the values from db not finish execute and bind into html. The best way is not using directive instead using controller to fetch data from db.
You pass a model without rating2 into your directive and the changes from the parent controller won't affect it, because variable is created afterwards. Adding a watcher in your linker on parent scope will solve the problem;
scope.$parent.$watch('', function(rating){
updateStars();
});
Other solution would be to define a starting value in your controller.
this.rating2 = 1;
Notice that it is bad design to have a scope variable for each rating. It is cleaner to have an array of ratings and you actually do not need the watcher by doing so.
https://codepen.io/hoschnok/pen/LbJPqL
angular controller
function RatingController($http) {
this.ratings = [4];
var self = this;
$http.get('https://api.myjson.com/bins/o0r69').then(function(res){
self.ratings.push(res.data.rating2);
});
}
HTML
<div ng-app="app" ng-controller="RatingController as rating" class="container">
<div ng-repeat="r in rating.ratings">
<div star-rating ng-model="r" max="10" on-rating-select="rating.rateFunction(rating)"></div>
</div>
</div>
The watcher change handler function has parameters reversed:
//INCORRECT parameters
//scope.$watch('ratingValue', function(oldValue, newValue) {
//CORRECT parameters
scope.$watch('ratingValue', function(newValue, oldValue) {
if (newValue) {
updateStars();
}
});
The first argument of the listening function should be newValue.
The DEMO on CodePen
ALSO
The ng- prefix is reserved for core directives. See AngularJS Wiki -- Best Practices
JS
scope: {
//Avoid using ng- prefix
//ratingValue: '=ngModel',
ratingValue: '=myModel',
max: '=?', // optional (default is 5)
onRatingSelect: '&?',
readonly: '=?'
},
HTML
<!-- AVOID using the ng- prefix
<star-rating ng-if='rating' ng-model="rating.rating2"
max="10" on-rating-select="rating.rateFunction(rating)">
</star-rating>
-->
<!-- INSTEAD -->
<star-rating ng-if='rating' my-model="rating.rating2"
max="10" on-rating-select="rating.rateFunction(rating)">
</star-rating>
When a custom directve uses the name ng-model for an attribute, the AngularJS framework instantiates an ngModelController. If the directive doesn't use the services of that controller, it is best not to instantiate it.

angularjs, display a variable containing '{{' '}}'

I got a variable in my angularjs scope like this:
function MyCtrl($scope) {
$scope.myvar = "{{'i want to translate this' |translate}}" + "{{' and this' |translate}}" + " but not this" ;
}
The translate is a custom filter which translates to french.
In the html:
{{myvar}}
I want to display "myvar" in the html but it displays the '{{' & '}}'.
I made a jsfiddle here
As per your jsfiddle code:
<div ng-controller="MyCtrl">
{{myvar}}
</div>
You've not used ng-app="myApp" directive anywhere. So angular know that which part of the HTML it need to bootstrap as angular app.
Another thing, you must avoid using global functions as controllers. Instead use
angular.module("myApp", [])
.controller("MyCtrl", MyCtrl);
Still you can't have
{{'i want to translate this' |translate}}" + "{{' and this' |translate}}
in your controller. instead you must use $filter and do the filtering in controller and just return the string.
$scope.myVar = $filer("translate")("i want to translate this") + $filer("translate")(" and this");
Inject $filter to your controller.
So, to start I have added some code to your jsfiddle and got it working. It renders your myvar.
var myApp = angular.module('myApp',['controllers']);
var controllers = angular.module('controllers', []);
controllers.controller('MyCtrl', ['$scope', function ($scope) {
$scope.myvar = "{{'i want to translate this' |translate}}" + "{{' and this' |translate}}" ;
}]);
Also see jsfiddle.
You can find a "working" fiddle based on the good advice from #mohamedrias here. By "working" I mean ng-app is properly declared in the html and your bindings are working. I agree with the advice already shared. Apply your filter logic within the controller. Then you could set the result to something like vm.myTranslatedVar and bind to that in your html with {{ vm.myTranslatedVar }}. vm simply stands for "view model" and sets your controller's scope rather than using $scope.

Rendering dynamic HTML(angularjs content) content after ajax call in AngularJS

I am new to Angular getting stuck after making ajax call. How do I render/compile the html content once you inject in DOM so that I can still use the AngularJs functions.
Due to the way my backend is set up I have to get content via ajax ($http). And I am making the app without jQuery. I tried $compile and $apply but didn't work. What am I missing here.
I have the code set up at http://jsfiddle.net/rexonms/RB7FQ/3/ . I want the second div content to have the same properties as the first div.
HTML
<div ng-controller="MyCtrl" class="section">
<input ng-model="contentA">
<div>
And the input is: {{contentA}}
</div>
</div>
<div ng-controller="MyAjax" class="section">
<div id="dumpAjax">
{{ajaxData}}
</div>
<button ng-click=getajax()> Get Ajax</button>
</div>
SCRIPT
var myApp = angular.module('myApp',[]);
//myApp.directive('myDirective', function() {});
//myApp.factory('myService', function() {});
function MyCtrl($scope) {
}
function MyAjax($scope){
var data = '<input ng-model="contentB">{{contentB}}';
$scope.getajax = function(){
$scope.ajaxData = data;
}
}
Thanks in advance.
ng-bind-html-unsafe is not available 1.2 and later verison of angular...
so you should use ng-bind-html which creates a binding that will innerHTML the result of evaluating the expression into the current element in a secure way.
using $scope variable in your string make it unsafe, so you should use $sce.trustAsHtml but this time variables in your string cannot be bind because they will be not compiled...
basically you should compile your string in order to bind your variables. Here comes custom directives you can create a directive which can replace with ng-html-bind...
Writing a custom directive which extends ng-bind-html with some extra functions can be a solution...
here is my PLUNKER
and here is your updated JSFIDDLE with my solution...
Instead of {{ajaxData}}, you should use something like:
<div ng-bind-html-unsafe="ajaxData"></div>
However, you'd still need to set the proper model to bind the contentB and get it working.

Sharing data between directives using attributes instead of services

I wanted to make a directive that would essentially act like a specialized input field. After some logic & user input, the 'value' attribute would be populated with a string of comma separated timeslots (hh:mm).
<time-slot value=""></time-slot>
becomes
<time-slot value="01:00,02:00,03:00"></time-slot>
I'd like to provide the flexibility for anyone to place a scope reference in the 'value' attribute tag -- whenever the attribute value is updated, so is the scope reference. (In the code below, myModel.times would be in the MyController scope).
<div ng-controller="MyController">
<time-slot value="{{ myModel.times }}"></time-slot>
</div>
I have had no problems accessing the 'value' attribute in the directive. However, I have not achieved two-way binding -- myModel.times never captures the changed value, even though the contents of the attribute have been changed when I inspect the element during runtime. I am using $attrs.$set to alter the value attribute.
I'm not sure if I'm missing something conceptually, or just missing some extra syntax. To keep this directive modular and shareable, I don't want to use a service to share data between the controller and directive, nor do I want to use a cascading scope. I think it would be optimal if the value attribute can simply be referenced by a scope variable and used as desired, much like a simple input tag:
<input ng-model="model.someText"></input>
An example with two-way data binding: See plunkr.
angular.module('myApp', [])
.directive('timeSlots', function() {
return {
scope: { value: '=' },
link: function($scope, $elem, $attrs) {
// you can access $scope.value here (after it has been interpolated)
}
};
})
.controller('MainCtrl', ['$scope', function($scope) {
$scope.value = 42;
}]);
In HTML:
<div ng-controller="MainCtrl">
<time-slots value="value"></time-slots>
</div>

Resources