I have three tabs that has different html inside ng-include. These tabs are shown using ng-repeat. Only one HTML template contains function call, but it's executed 3 times (once per ng-repeat iteration). What is wrong here and how to fix it?
var app = angular.module('myApp', [])
app.controller('myCtrl', [
'$scope',
function($scope){
$scope.randomFnc = function (i) {
console.log(i);
return "Placeholder text";
}
$scope.tabs = [
"a",
"b",
"c"
];
}
])
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="myCtrl">
<div ng-repeat="tab in tabs">
<div ng-if="$index == 1">
{{$index}}<input type="text" value="" placeholder="{{randomFnc($index)}}"/>
</div>
<div ng-if="$index != 1">{{$index}}</div>
</div>
</div>
</div>
You can use ng-init though it is not highly recommended to achieve this. The reason why your function call is being executed thrice is because angular doesn't know if any $scope value has changed during each digest cycle. So the function will get executed for each digest cycles. In your case, it will get executed when the ng-if conditions become true as well as during the two digest cycles accounting a total of three. This is the reason why it gets executed 3 times with the value 1 regardless of the number of items in the array.
var app = angular.module('myApp', [])
app.controller('myCtrl', [
'$scope',
function($scope) {
$scope.x = {};
$scope.randomFnc = function() {
console.log("once");
$scope.placeholderText = "Placeholder text";
}
$scope.tabs = [
"a",
"b",
"c"
];
}
])
app.directive('trackDigests', function trackDigests($rootScope) {
function link($scope, $element, $attrs) {
var count = 0;
function countDigests() {
count++;
$element[0].innerHTML = '$digests: ' + count;
}
$rootScope.$watch(countDigests);
}
return {
restrict: 'EA',
link: link
};
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="myCtrl">
<div ng-repeat="tab in tabs">
<div ng-if="$index == 1" ng-init="randomFnc()">
{{$index}}<input type="text" value="" placeholder="{{placeholderText}}" />
</div>
<div ng-if="$index != 1">{{$index}}</div>
</div>
</div>
<track-digests></track-digests>
</div>
Call a method for 3 times because placeholder attribute or other attributes like this as class or ... can't define the ng- in angularjs, for solution we can use ng-init to handle it.
when you run the first you have repeat and then binding elements and then your angular attributes runs.
for best solution i refer to use model as object to binding placeholder on it, it's easily.
var app = angular.module('myApp', [])
app.controller('myCtrl', [
'$scope',
function($scope){
$scope.placeholder = "";
$scope.randomFnc = function (tab) {
$scope.placeholder = "Placeholder text";
}
$scope.tabs = [
"a",
"b",
"c"
];
//----2
$scope.randomFnc2 = function (tab) {
tab.placeholder = "Placeholder text";
}
$scope.tabs2 = [
{name: "a"},
{name: "b"},
{name: "c"},
];
}
])
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="myCtrl">
<h1>better if you use model</h1>
<div ng-repeat="tab in tabs2">
<div ng-if="$index == 1" ng-init="randomFnc2(tab)">
{{$index}}
<input type="text" value="" placeholder="{{tab.placeholder}}"/>
</div>
<div ng-if="$index != 1">{{$index}}</div>
</div>
<h1>also you can</h1>
<div ng-repeat="tab in tabs">
<div ng-if="$index == 1" ng-init="randomFnc(tab)">
{{$index}}
<input type="text" value="" placeholder="{{placeholder}}"/>
</div>
<div ng-if="$index != 1">{{$index}}</div>
</div>
</div>
</div>
Related
Below are my list of items :
$scope.items = [
{id=1,name:'code.lmn.1234.Lodashjs'},
{id=2,name:'xyz.Suv.Angularjs'},
{id=3,name:'www.kius.reactjs'}
]
Now I want to display last part of name so expected output is like below :
Lodashjs
Angularjs
reactjs
I don't want to create filter for this as that will impact performance. So is there any way to do this without filter?
var app = angular.module("myApp", []);
app.controller("myController", function ($scope) {
$scope.items = [
{id:1,name:'code.lmn.1234.Lodashjs'},
{id:2,name:'xyz.Suv.Angularjs'},
{id:3,name:'www.kius.react.js'}
]
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myController">
<div ng-repeat="item in items">
<span >{{item.name}}</span>
</div>
</div>
Try this:
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myController">
<div ng-repeat="item in items">
<span >{{ item.name.split('.').reverse()[0] }}</span>
</div>
</div>
Yes it's so simple.
Just copy paste the below code:
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myController">
<div ng-repeat="item in items">
<span >{{(item.name).substr((item.name).lastIndexOf('.') + 1, (item.name).length)}}</span>
</div>
</div>
You can also use directive that will manipulate the name and return string after last "." dot.
<div ng-repeat="item in items" my-directive my-directive-fn="item.name">
app.directive("myDirective", function(){
return {
scope: {
"myDirectiveFn": "="
},
link: function(scope){
scope.myDirectiveFn.match(/([^.]+)$/);
}
}
});
I'm an Angular beginner.
I want to load a json on load, this works well
but if I make a change to an input field, I get an error message.
Error: $rootScope:infdig Infinite $digest Loop
Thanks
My HTML
<body ng-app="myApp" ng-controller="mainCtrl">
<div id="wrapper">
<header style="height:50px;"> </header>
<div class="container">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script>
<script src="js/controller.js"></script>
<section>
<div class="col-md-4">
<label for="words">Wörter</label>
<input ng-model="words" id="words" type="number" name="words" placeholder="Wörter" min="10" max="10000" value="{{words}}" step="10">
</div>
<p>{{words}}</p>
<div id="view" class="col-md-6">
<ul ng-controller="loadContent">
<li ng-repeat="content in contents | orderBy:random">{{content.text}}</li>
</ul>
</div>
</section>
</div>
</div>
My javascript
var app = angular.module('myApp', []);
app.controller('mainCtrl', function ($scope, $http) {
$scope.words = 40;
$scope.letterLimit = 400;
});
app.controller('loadContent', function ($scope, $http) {
$scope.random = function () {
return 0.5 - Math.random();
}
$scope.loadContent = function() {
var def = $http.get('data.json').success(function(data) {
$scope.contents = data;
});
}
$scope.loadContent();
});
My json
[
{"text": "Lorem ipsum1", "date" : true},
{"text": "Lorem ipsum2", "data" : true},
{"text": "Lorem ipsum3", "data" : true}
]
I think that angular is continuously disgesting your controller as soon as you interact with the view, and is therefore executing $scope.loadContent(); at the bottom of your controller repeatedly.
I assume you only wish for this to fire once? If so, remove the function call from your controller and modify your view as below.
<body ng-app="myApp" ng-controller="mainCtrl" ng-init="loadContent">
With this, $scope.loadContent is only called once. if you wish to call it another way, or multiple times, please specify in your question.
[Edited to reflect comments in #shaunhusain answer]
I have a nested ng-repeat construct for which I want to instantiate a new instance of a directive for every inner item, passing to that directive the data from that inner item. In the code below, I've included two nested loops. The one that works uses one-way binding via the {{ }} notation, and the other appears to work as well...until you hit the Refresh button. Even though $scope.frames changes (and the {{ }} binding continues to reflect the changes), the binding for the directive is not triggered.
What am I missing?
var myApp = angular.module('myApp', []);
myApp.directive("myDirective", function () {
return {
restrict: 'E',
scope: {
boundData: '=data'
},
link: function (scope, element) {
angular.element(element.find("div")[0])
.html('')
.append("<p>" + scope.boundData.val + "</p>");
}
}
});
myApp.controller('FooCtrl', ['$scope', function ($scope) {
$scope.clear = function () {
$(".item-list").empty();
};
$scope.getData = function () {
var frames = [];
for (var i = 0; i < 2; i++) {
var items = [];
for (var j = 0; j < 4; j++) {
items.push({ val : Math.floor(Math.random() * 5000) });
};
frames.push(items);
}
$scope.frames = frames;
};
$scope.getData();
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div ng-app="myApp">
<div ng-controller="FooCtrl">
<div>
<button ng-click="getData()">Refresh</button>
<button ng-click="clear()">Clear</button>
</div>
<div style="float: left">
<b>{} binding</b>
<div ng-repeat="frame in frames track by $index">
<div ng-repeat="item in frame track by $index">
{{item.val}}
</div>
</div>
</div>
<div style="margin-left: 120px">
<b>Using directive</b>
<div ng-repeat="frame in frames track by $index">
<div ng-repeat="item in frame track by $index">
<my-directive data="item">
<div class="item-list"></div>
</my-directive>
</div>
</div>
</div>
</div>
</div>
"Thinking in AngularJS" if I have a jQuery background?
var myApp = angular.module('myApp', []);
myApp.directive("myDirective", function () {
return {
restrict: 'E',
scope: {
boundData: '=data'
},
link: function (scope, element) {
angular.element(element.find("div")[0])
.html('')
.append("<p>" + scope.boundData.val + "</p>");
}
}
});
myApp.controller('FooCtrl', ['$scope', function ($scope) {
$scope.clear = function () {
$(".item-list").empty();
};
$scope.getData = function () {
var frames = [];
for (var i = 0; i < 2; i++) {
var items = [];
for (var j = 0; j < 4; j++) {
items.push({ val : Math.floor(Math.random() * 5000) });
};
frames.push(items);
}
$scope.frames = frames;
};
$scope.getData();
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.js"></script>
<div ng-app="myApp">
<div ng-controller="FooCtrl">
<div>
<button ng-click="getData()">Refresh</button>
<button ng-click="clear()">Clear</button>
</div>
<div style="float: left">
<b>{} binding</b>
<div ng-repeat="frame in frames track by $index">
<div ng-repeat="item in frame track by $index">
{{item.val}}
</div>
</div>
</div>
<div style="margin-left: 120px">
<b>Using directive</b>
<div ng-repeat="frame in frames track by $index">
<div ng-repeat="item in frame track by $index">
<my-directive data="item">
<div class="item-list"></div>
</my-directive>
</div>
</div>
</div>
</div>
</div>
Believe your problem is because of using a jquery selector without a specified parent being element. Typically you won't need jQuery when using Angular, good to drop it initially, search on SO for how to think in AngularJS with a jQuery background for a good write up. Will add a sample here shortly.
The flaw here was the use of track by $index in my ng-repeat (specifically the inner one).
I am using two controllers and a factory service to get the data from it. I want to filter the data in the second controller by input 'ng-model'. So i have written input ng-model in both the controllers (check index.html). Its working if i tried to enter the input data in the second controller input field, but its not working if i try filtering from first controller input ng-app.
index.html
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
<script src="scripts/controller.js"></script>
</head>
<body ng-app="app" ng-controller="appCtrl">
<div style="float: left; width: 300px;">
<div ng-controller="checkBoxModuleCtrl">
<ul>
<li ng-repeat="item in chkBoxList" style="list-style: none;">
<input type="checkBox" value="{{item}}" ng-click="checkBoxClickHandler($index, $event)"> {{item}}
</li>
</ul>
<input type="text" ng-model="myText" />
</div>
</div>
<!--<input type="button" ng-click="checkBoxClickHandler()" value="Click Me!"> </input>-->
<div style="float: left; width: 400px; height: 600px; overflow-y: scroll;" >
<div>
<div ng-controller="panelCtrl">
<input type="text" ng-model="myText" />
<ul>
<li ng-repeat="panelItem in panelData|filter:myText" style="list-style: none;">
<div>
<b>Title </b/>: {{panelItem.name }}<br/>
<b>Channel-Type </b>: {{panelItem.type }}<br/>
<b>description </b>: {{panelItem.description }}
</div>
<hr weight="5" />
</li>
</ul>
</div>
</div>
</div>
</body>
</html>
controller.js
var app = angular.module("app", ["checkBoxserviceModule"]);
app.controller('appCtrl', function($scope){
});
app.controller('checkBoxModuleCtrl', function($scope, externals){
$scope.chkBoxList = [];
$scope.init = function(){
$scope.chkBoxList = externals.getCheckBoxList()
};
$scope.init();
$scope.checkBoxClickHandler = function(itemIndex, event){
alert(event.currentTarget.value + "will be handling click listener for check box" + itemIndex)
}
});
app.controller("panelCtrl", function($scope, externals){
$scope.init = function(){
$scope.panelData = externals.getPanelData();
};
$scope.init();
$scope.customFilter = function(panelItem){
return panelItem.toUpperCase;
}
});
var checkBoxserviceModule = angular.module("checkBoxserviceModule", []);
checkBoxserviceModule.factory("externals", function(){
return{
getCheckBoxList : function(){
return [ "sports", "movies", "entertainment", "news" ]
},
getPanelData : function(){
//alert("in services")
return [
{
"name":"cinmax",
"type": "movies",
"description":"Some Tesxt"
},
{
"name":"setmax",
"type": "sports",
"description":"Some Tesxt"
},
{
"name":"mtv",
"type": "entertainment",
"description":"Some Tesxt"
},
{
"name":"ibn news",
"type": "news",
"description":"Some Tesxt"
}
];
}
};
});
Controllers use prototypical inheritance to share scope. So, you need to make sure the object you want inherited is an actual object in order for the reference to stay binded. If try to share a primitive from the scope, they will not be binded correctly and each controller will end up making it's own copy.
This might inherit correctly initially, but the binding will disconnect as soon as you change the value of the child's scope.number.
-ParentController
-scope.number = 4
-ChildController
-scope.number //will not stay binded to parent's scope
This on the otherhand, if you create an object on the scope, will inherit a reference to the object, and will stay binded in the child controller.
-ParentController
-scope.myObject.number = 4
-ChildController
-scope.myObject.number //will stay binded to parent's scope
Here is a better explanation: Why don't the AngularJS docs use a dot in the model directive?
I have put all necessary files into a single file. I want to push item into array when the user clicks on a button. Here is the content:
When I click on the button, nothing happens. Also the data is not repeated/looped and {{}} is shown in angular which means there is a problem.
<script type="text/javascript" src="angular.js" ></script>
<div data-ng-app="App">
<div data-ng-controller="MyController">
<ul data-ng-repeat="one in names">
<li>{{ one.first }}</li>
</ul>
</div>
</div>
<input type="text" data-ng-model="Namer.name"/>
<input type="submit" data-ng-click="AddName()"/>
<script type="text/javascript">
var App = angular.module("App", []);
App.controller("MyController", function($scope){
$scope.names = [
{ first : "Thomas"},
{ first : "Geferson"},
{ first : "Jenny"},
{ first : "Maria"},
];
$scope.AddName = function(){
$scope.names.push({
name : $scope.Namer.name;
});
};
});
</script>
Working DEMO
var App = angular.module("App", []);
App.controller("MyController", function ($scope) {
$scope.names = [{
first: "Thomas"
}, {
first: "Geferson"
}, {
first: "Jenny"
}, {
first: "Maria"
}];
$scope.AddName = function () {
$scope.names.push({
first: $scope.Namer.name
});
};
});
You need to move your data-ng-click inside Controller.Also you had some syntax issues.That is also i fixed (To work with IE ALSO)
<div data-ng-app="App">
<div data-ng-controller="MyController">
<ul data-ng-repeat="one in names">
<li>{{ one.first }}</li>
</ul>
<input type="text" data-ng-model="Namer.name" />
<input type="submit" data-ng-click="AddName()" />
</div>
</div>
Move your inputs to inside your MyController so that it executes code in the scope created by the controller:
<div data-ng-app="App">
<div data-ng-controller="MyController">
<ul data-ng-repeat="one in names">
<li>{{ one.first }}</li>
</ul>
<input type="text" data-ng-model="Namer.name"/>
<input type="submit" data-ng-click="AddName()"/>
</div>
</div>
Another mistake is that you need to change name to first to match with your existing object
$scope.AddName = function(){
$scope.names.push({
first : $scope.Namer.name //change to first and remove the ;
});
};
DEMO