how to access controller dataset in custom directive - angularjs

I was playing with angular js scope of custom directive and tried to use controller dataset in custom directive with different attribute name but did not succeed. Kindly see my code and suggest me what is the proper way to access the data and set in ng-repeat in custom directive.
Do I need to set ng-repeat on custom directive or inside the directive template ? I mean
like this
<movie-list ng-repeat="entry in movieData" > </movie-list>
or inside directive template
<movie-list movieArray = "movieData" ></movie-list>
and then
<div>
<ul >
<li ng-repeat="entry in movieArray"> {{...}} </li>
</ul>
</div>
here is my data
app.js
angular
.module('app')
.controller('homeController', function($scope) {
$scope.movieData = [{
name : 'PK',
star : 'Aamir Khan',
releaseYear : '2015'
},
{
name : 'PiKu',
star : 'Irrfan Khan',
releaseYear : '2015'
}
];
});
// custom directive
angular
.module('app')
.directive('movieList', function(){
// Runs during compile
return {
scope: { movieArray : '=movieArray' },
controller: function($scope, $element, $attrs, $transclude) {
},
require: '?ngModel',
restrict: 'E',
templateUrl: 'movie.html',
replace: true,
link: function($scope, element, attr, controller) {
// console.log($scope.$parent);
}
};
});
index.html
<div ng-controller="homeController" >
<div class="col-lg-6">
<movie-list movieArray="movieData"></movie-list>
</div>
movie.html
<div>
<ul class="list-group" >
<li ng-repeat="entry in movieArray" class="list-group-item" >
<h3 class="list-group-item-heading">{{ entry.name }}</h3>
<p class="list-group-item-text">
{{ entry.star }} - Release in {{ entry.releaseYear }}
</p>
</li>
</ul>
</div>
scope: { movieArray : '=movieArray' } >> not working
scope: { movieArray : '=movieData' } >> not working
even I changed the attribute
<movie-list movieArray="movieArray"></movie-list>
but not working

Update attribute name:
<movie-list movieArray="movieData"></movie-list>
To:
<movie-list movie-array="movieData"></movie-list>
Note: Directive or attribute name should be lower case.

If you want to skip scope (delete scope: { movieArray : '=movieArray' }, from your directive ) in your directive you can do that by below code in Index.html:
<div ng-controller="homeController" >
<div class="col-lg-6">
< div movie-list></div>
</div>
And in your controller you need to replace movieData by movieArray:
.controller('homeController', function($scope) {
$scope.movieArray= [{
name : 'PK',
star : 'Aamir Khan',
releaseYear : '2015'
},
{
name : 'PiKu',
star : 'Irrfan Khan',
releaseYear : '2015'
}
];

Related

Get data from directive and change image according to data value

I want to get the value of current.flag from my template property in random.js file and use it after in my HTML file. According to the value of flag I will receive, I would like an image to be changed. I would like some help for that, because I'm beginner to Angular.
Thanks in advance
<section class="has-header">
<div class="container">
<div class="row">
<div class="col-md-4 text-center">
<img ng-src="{{current.flag === 'gr' ? 'images/image_1.png' : 'images/image_2.png'}}" />
</div>
</div>
</div>
</section>
random.js
angular.module('app')
.directive('language', function () {
return {
restrict: 'E',
replace: true,
scope: {},
template: '<div class="languages-wrap pull-right">\
<div class="languages f32">\
<div ng-click="poped=!poped" class="current flag {{current.flag}}"></div></div>\
<div class="language-selector" ng-show="poped">\
<ul class="languages-list">\
<li ng-repeat="l in langs">\
<a ng-click="changeLang(l)" class="languages f32">\
<div class="flag {{l.flag}}"></div>\
</a>\
</li>\
</ul>\
</div>\
</div>',
controller: ['$scope', '$cookies', '$translate', '$route', '$location',
function ($scope, $cookies, $translate, $route, $location) {
$scope.poped = false;
$scope.langs = [
{
iso: 'el',
flag: 'gr'
},
{
iso: 'en',
flag: 'us'
}
];
$scope.current = $cookies.getObject('lang') ? $cookies.getObject('lang') : $scope.langs[0];
$scope.changeLang = function (lang) {
$cookies.putObject('lang', lang, {expires: new Date(2020, 10, 10)});
$translate.use(lang.iso);
$scope.current = lang;
$scope.poped = false;
$location.search('lang', lang.iso);
$route.reload();
};
}
]
};
});
u can do a controller to the html file and add
$scope.current = $cookies.getObject('lang') ? $cookies.getObject('lang') : $scope.langs[0];
to the controller
if i understand correctly , u want to use the $scope.current value from a directive in another html file, which is not connected to the directive , if yes , what u can do is add a controller to this html and add the
$scope.current = $cookies.getObject('lang') ? $cookies.getObject('lang') : $scope.langs[0];
to it How to bind an AngularJS controller to dynamically added HTML?
make controller file
add it to index.html,
add this inside it
var app = angular.module('myApp', []);
app.controller('exampleCtrl', function($scope, $cookies) {
$scope.current = $cookies.getObject('lang') ? $cookies.getObject('lang') : $scope.langs[0];
});
in the html add controller to the section tag
<section class="has-header" ng-controller="exampleCtrl">

losing scope of ng-model when using ng-include (AngularJS)

I am creating android app using ionic and angularjs. In app i used ng-include for include html content in my page.
checkbox.html:
<ul>
<li ng-repeat="opt in $parent.checkboxOptions">
<h4>
<label><input type="checkbox" name="checkbox" ng-model="$parent.checkboxAnswer" value="{{opt.option_value}}">{{opt.option_value}}</label>
</h4>
</li>
</ul>
surveyCtrl.js
$scope.checkboxOptions = params.QuestAnswers;
$scope.next = function(){
console.log($scope.checkboxAnswer);
}
its showing undefined and another thing when i am click on one checkbox its selecting all checkbox's also.
surveyCtrls.js
.directive('question', function ($compile) {
return {
restrict: 'A',
replace: true,
link: function (scope, ele, attrs) {
scope.$watch(attrs.question, function(QuesHtml) {
ele.html(QuesHtml);
$compile(ele.contents())(scope);
});
}
};
})
.directive('description', function ($compile) {
return {
restrict: 'A',
replace: true,
link: function (scope, ele, attrs) {
scope.$watch(attrs.description, function(DescHtml) {
ele.html(DescHtml);
$compile(ele.contents())(scope);
});
}
};
})
.directive('answers', function ($compile) {
return {
restrict: 'A',
replace: true,
link: function (scope, ele, attrs) {
scope.$watch(attrs.answers, function(AnswerHtml) {
ele.html(AnswerHtml);
$compile(ele.contents())(scope);
});
}
};
})
.controller('surveyLoad', function($scope){
var QuestType = SurveyData[QuestionIndex].question_type;
var DrawHTML = {
'QuestionText': 'Some Text',
'QuestionDesc': 'Some Desc',
'QuestAnswers': [{
option_value: 'Red',
}, {
option_value: 'Blue',
}];,
'scope' : $scope
};
checkbox(DrawHTML);
}
})
.controller('nextQuest', function($scope){
$scope.QuestNext = function(){
console.log($scope);
}
});
function checkbox(params){
var $scope = params.scope;
$scope.QuesHtml = "<p>"+params.QuestionText+"</p>";
$scope.DescHtml = "<p>"+params.QuestionDesc+"</p>";
$scope.checkboxOptions = params.QuestAnswers;
$scope.AnswerHtml = "<div ng-include src=\"'surveyTemplate/checkbox.html'\"></div>";
}
survey.html
<div class="row">
<div class="col question_div">
<div class="qus_head">
<p>Question: 1/10</p>
</div>
<h4 class="para"><span question="QuesHtml"></span> </h4>
<div class="qus_footer">
<p>Maxime quis.</p>
</div>
</div>
</div>
<div answers="AnswerHtml">
</div>
<div class="row">
<div class="col button_div">
<ul>
<li><img src="../img/next.png" style="width:70px;float:right" alt="next" ng-controller="nextQuest" ng-click="QuestNext()"></li>
<!-- <center><li><button style="align:center">Stop</button></li></center> -->
<li><img src="../img/pre.png" style="width:70px;float:left" alt="previous" ></li>
</ul>
</div>
</div>
is there any way to get the value of checked checkboxes and prevent to check all other checkboxe's ?
Using ng-includes create it's own scope.
By using the controller as syntax you can overcome this issue.
https://docs.angularjs.org/api/ng/directive/ngController
<div id="ctrl-as-exmpl" ng-controller="Controller as ctrl">
...
<li ng-repeat="opt in ctrl.checkboxOptions">
<h4>
<label><input type="checkbox" name="checkbox" ng-model="ctrl.checkboxAnswer" value="{{opt.option_value}}">{{opt.option_value}}</label>
</h4>
</li>
...
</div>
and in your controller :
$scope.checkboxOptions = params.QuestAnswers;
becomes
this.checkboxOptions = params.QuestAnswers;
and so on.
AngularJS plunker about this syntax :
https://plnkr.co/edit/DB1CpoWLUxQ9U8y558m1?p=preview
Regards,
Eric
You need some correction in the html.
<label><input type="checkbox" name="checkbox" ng-model="opt.option_value">{{opt.option_value}}</label>
With ng-model="$parent.checkboxAnswer" ,all checkbox will share common model.
So,it will get updated at the same time.
So,when you check/uncheck,it will check/uncheck everyone.
And ,you don;t need to use value,angular is used ng-model property for its value evaluation and not value
Yupii...i found the solution..
here is solution:
<ul>
<label ng-repeat="opt in $parent.checkboxOptions">
<li>
<h4>
<label><input type="checkbox" name="checkbox" ng-model="$parent.$parent.selected[opt.option_value]" >{{opt.option_value}}</label>
</h4>
</li>
</label>
</ul>
(function(angular) {
'use strict';
angular.module('MyApp', [])
.controller('AppCtrl', SettingsController1);
function SettingsController1($scope) {
$scope.options = {
'checkboxAnswer': ''
};
$scope.checkboxOptions= [{
type: 'phone',
value: '917xxxxxxxx'
}, {
type: 'email',
value: 'sajankumarv#example.org'
}];
}
})(window.angular);
radio box template file #radiobox.html
<ul>
<li ng-repeat="opt in checkboxOptions">
<h4>
<label><input type="checkbox" ng-model="options.checkboxAnswer" ng-true-value="'{{opt.value}}'" ng-false-value="''">{{opt.type}}</label>
</h4>
</li>
<h1>{{options.checkboxAnswer}}</h1>
</ul>
//in main index.html you simply include this radiobox.html using ng-include directive
<body ng-app="MyApp">
<div ng-controller="AppCtrl">
<div ng-include='"radiobox.html"'></div>
</div>
</body>
I think you are not in isolate scope so you don't have to use $parent for your case.I made a simple working demo for you to understand kindly refer the below link.
https://plnkr.co/edit/aQMZBln5t0Fipwn1qloN?p=preview

Controller function not called from directive template in AngularJS

Directive template (items.html)
<li ng-repeat="item in itemCart">
{{item.title}} <br>
{{item.category}} &nbsp
{{ formatCurrencyFunction({cost: item.price}) }}
</li>
This custom directive is used in Second.html
<h1>
This is Second.
{{header}}
</h1>
<hr>
<ul>
<items-list item-cart="items" format-currency-function="formatPrice(cost)"></items-list>
</ul>
The code for Controller is:
myApp.directive("itemsList", function(){
return{
templateUrl:"contents/Views/Directives/Items.html",
replace: true,
scope:{
itemCart: "=",
formatCurrencyFunction: "&"
},
restrict:"EACM" // E-Element A-Attribute C-Class M-Comments
}
})
myApp.controller('secondController', ['$scope', '$log', '$http','$routeParams', function($scope, $log, $http, $routeParams) {
$scope.formatPrice = function(price){
return "₹ "+parseFloat(price).toFixed(2);
};
$scope.header = 'Second ' + ($routeParams.num || "");
$scope.testSecond = "Second";
$http.get("/items")
.success(function(data){
$scope.items = data;
});
}]);
The function formatPrice is not called from the directive Items.html
What has to be corrected in my code to get it working?
Inside the directive's link function, you'll want to call the method like so:
scope.someFn({arg: someValue});
myApp.directive("itemsList", function(){
return{
templateUrl:"contents/Views/Directives/Items.html",
replace: true,
scope:{
itemCart: "=",
formatCurrencyFunction: "&formatCurrencyFunction"
},
link:function(scope, element, attrs){
scope.formatPrice({arg: 35}); //scope.someFn({arg: 35});
},
restrict:"EACM" // E-Element A-Attribute C-Class M-Comments
}
})
Got this working by using ng-repeat in the Second.html page instead of the directive.
Directive template (items.html)
<li>
{{item.title}} <br>
{{item.category}} &nbsp
{{ formatCurrencyFunction({cost: item.price}) }}
</li>
This custom directive is used in Second.html
<h1>
This is Second.
{{header}}
</h1>
<hr>
<ul>
<items-list item="item" format-currency-function="formatPrice(cost)" ng-repeat="item in items"></items-list>
</ul>
Code in JS file
myApp.directive("itemsList", function(){
return{
templateUrl:"contents/Views/Directives/Items.html",
replace: true,
scope:{
item: "=",
formatCurrencyFunction: "&"
},
restrict:"EACM" // E-Element A-Attribute C-Class M-Comments
}
})
Inside the directive's link function, you need to call the method by applying some event to that attribute
Controller function 'formatCurrencyFunction()' can't be called by just placing it in an expression.
To use the function you need to call it based on an even, in your case ng-init will work.
<li ng-repeat="item in itemCart" ng-init="formatCurrencyFunction({cost: item.price})">
{{item.title}} <br>
{{item.category}}
</li>

Isolate scope in reusable angular direcives

I have a custom directive : myContent
'use strict';
angular.module('myModule').directive('myContent', function() {
return {
restrict: 'E',
templateUrl: 'myContent.html',
controller: function($scope){
$scope.selectedContents = {
priceOrTransactionOption : null,
yearlyOrMonthly : 'Yearly',
contentIndicator : null
};
$scope.comboContent = {
priceOrTransactionOption : ['By Price Range', 'By Transactions'],
yearlyOrMonthly : ['Yearly', 'Monthly'],
contentIndicator : ['Active configuration', 'Next Configuration']
};
},
controllerAs: 'myContentCtrl'
};
});
And I'm using this same directive in multiple places :
<div class="tab-content col-lg-10">
<my-content></my-content>
</div>
<div class="tab-content col-lg-10">
<my-content></my-content>
</div>
<div class="tab-content col-lg-10">
<my-content></my-content>
</div>
And my html page for the directive (myContent.html) is having some data with :
<div class="row no-left-padding">
<div class="col-lg-3 no-left-padding">
<select class="form-control" ng-model="selectedContent.priceOrTransactionOption"
ng-options="keyOption as keyOption for keyOption in comboContent.priceOrTransactionOption">
</select>
</div>
<div class="col-lg-3 no-left-padding">
<select class="form-control" ng-model="selectedContent.yearlyOrMonthly" ng-disabled = "true"
ng-options="interval as interval for interval in comboContent.yearlyOrMonthly">
</select>
</div>
<div class="col-lg-3 no-left-padding">
<select class="form-control" ng-model="selectedContent.contentIndicator"
ng-options="indicator as indicator for indicator in comboContent.contentIndicator">
</select>
</div>
</div>
But my problem is, when ever I'm changing the model in one directive, it reflects in each directives.
How can I use the same directive, and map each with different models?
I had tried with wadding one attribute to my custom directive:
<div class="tab-content col-lg-10">
<my-content category="type1"></my-content>
</div>
<div class="tab-content col-lg-10">
<my-content category="type2"></my-content>
</div>
<div class="tab-content col-lg-10">
<my-content category="type3"></my-content>
</div>
But still I'm confused with where should I map the category for getting isolated object.
You need to add Isolated scope to your directive. This effectively allows it to have its own set of properties:
angular.module('myModule').directive('myContent', function() {
return {
restrict: 'E',
templateUrl: 'myContent.html',
scope: {
category:'='
},
controller: function($scope){
$scope.selectedContents = {
priceOrTransactionOption : null,
yearlyOrMonthly : 'Yearly',
contentIndicator : null
};
$scope.comboContent = {
priceOrTransactionOption : ['By Price Range', 'By Transactions'],
yearlyOrMonthly : ['Yearly', 'Monthly'],
contentIndicator : ['Active configuration', 'Next Configuration']
};
},
controllerAs: 'myContentCtrl'
};
});
You can then use your example above:
<div class="tab-content col-lg-10">
<my-content category="type1"></my-content>
</div>
And each one will work individually.
Take note though, when you add your properties of the isolated scope binding '='. There are a number of different types, '#', '=' and '&' as well as optional arguments. The naming of your scope property uses snake case. Rather than me giving you a full explanation, read the Angular developers guide on isolated scope
Try adding this in your directive definition:
restrict: 'E',
scope: {},
It appears what you want is to return selectedContents for each instance of the directive. To do this, you would use angular two way binding to create a link between your directive and the page content.
angular.module('myModule').directive('myContent', function() {
return {
restrict: 'E',
templateUrl: 'myContent.html',
scope: {
selectedContents: '='
},
controller: function($scope){
$scope.selectedContents = {
priceOrTransactionOption : null,
yearlyOrMonthly : 'Yearly',
contentIndicator : null
};
....
now, you can refer to the selectedContents as a parameter on the directive.
<div class="tab-content col-lg-10">
<my-content selectedContents="someObject.selectedContents"></my-content>
</div>
<div class="tab-content col-lg-10">
<my-content selectedContents="someOtherObject.selectedContents"></my-content>
</div>

How to evaluate stored expression in AngularJS $scope

Please help me in this issue. Following is the the code.
HTML:
<div ng-controller="ctrl">click here</div>
JS:
app.controller('ctrl', function($scope) {
$scope.true_link = "http://google.com";
$scope.link = "{{ true_link }}";
});
Result:
<div ng-controller="ctrl">click here</div>
Expectation:
<div ng-controller="ctrl">click here</div>
Replace {{ link }} by {{ true_link }} in HTML will solve this problem. But I have to use this way. How can I evaluate expression in $scope.link content again? Please help me. Thanks.
Update
Look like facebook, I have two wall pages: User page and Actor page. They have same template structure and process (append, remove element etc...) after bussiness function such as changeAvatar(), changeCover(), post() etc... So I create 'homepage' based directive:
JS
app.directive('homepage', function() {
return {
restrict: 'A',
templateUrl: 'homepage.html',
controller: 'homepageCtrl'
};
});
app.controller('homepageCtrl', function($scope) {
$scope.changeAvatar() = ...;
$scope.post() = ...;
});
and two extend controllers:
app.controller('userCtrl', function($scope, $http) {
$http.({...}).success((data){ $scope.username = data.username })
$scope.menu = [
{
title: "foo-user"
link: "/u/{{ username }}/foo-user"
}
{
title: "bar-user"
link: "/u/{{ username }}/bar-user"
}
]
});
app.controller('actorCtrl', function($scope) {
$http.({...}).success((data){ $scope.actorname = data.actorname })
$scope.menu = [
{
title: "foo-actor"
link: "/u/{{ actorname }}/foo-actor"
}
{
title: "bar-actor"
link: "/u/{{ actorname }}/bar-actor"
}
]
});
HTML
homepage.html
<section>
<header>
<ul class="menu">
<li ng-repeat="_menu in menu">
<a href="{{ _menu.link }}">
{{ _menu.title }}
</a>
</li>
</ul>
</header>
<main>
content...
</main>
</section>
User page:
<div homepage ng-controller="userCtrl"></div>
Actor page:
<div homepage ng-controller="actorCtrl"></div>
Two pages menu has same HTML structure & effect, but differ in items. I wanna define menu item in extended controller (userCtrl, actorCtrl) and print them by ng-repeat. The problem is evaluate $scope.menu.link content.
Solution
I found solution: using $scope.$eval (https://docs.angularjs.org/guide/expression).
In userCtrl, the $scope.menu[i].link is dynamic content because included username - received from ajax call. I can update $scope.menu[i].link in $http.success() by using foreach. But I think using $scope.$eval help me auto update wherever I want easier.
So, the code is:
JS
app.controller('userCtrl', function($scope, $http) {
$http.({...}).success((data){ $scope.username = data.username })
$scope.menu = [
{
title: "foo-user"
link: "'/u/' + username + '/foo-user'"
show: 'true'
}
{
title: "bar-user"
link: "'/u/' + {{ username }} + '/bar-user'"
show: 'username == "lorem"'
}
]
});
HTML
homepage.html
<section>
<header>
<ul class="menu">
<li
ng-repeat="_menu in menu"
>
<a
ng-href="{{$parent.$eval(_menu.link)}}"
ng-show="$parent.$eval(_menu.show)"
>
{{_menu.title}}
</a>
</li>
</ul>
</header>
<main>
content...
</main>
</section>
Create a directive, and use $eval to parse the expression:
app.directive('a', function (){
return {
restrict : 'E',
link: function(scope, element, attr){
element.attr('href',scope.$eval(attr.href));
}
}
});
app.controller('ctrl', function($scope) {
$scope.true_link = "http://google.com";
$scope.link = $scope.true_link; // you need a copy of `$scope.true_link` here
});
<div ng-controller="ctrl"><a ng-href="link">click here</a></div>
$scope.link = "{{ true_link }}"; this is just a string, nothing else, so it will render as such.
Don't be confused by the curly brackets.
You can complicate things by using $compile, eval, etc... or you can simply assign the true_link value to the link variable.
UPDATE
You have a problem in your ng-repeat directive. This:
<li ng-repeat="_menu in menu">
{{ menu.title }}
</li>
Should be this (note _menu):
<li ng-repeat="_menu in menu">
{{ _menu.title }}
</li>
Also, there's no need to use templating in your controller, so this:
$scope.menu = [
{
title: "foo-user"
link: "/u/{{ username }}/foo-user"
}
{
title: "bar-user"
link: "/u/{{ username }}/bar-user"
}
]
can be this:
$scope.menu = [
{
title: "foo-user"
link: "/u/" + $scope.username + "/foo-user"
}
{
title: "bar-user"
link: "/u/" + $scope.username + "/bar-user"
}
]
Why do you want to use {{link}} instead of {{true_link}}? What exactly the issue you've run into that "you have to use this way"? If you can explain this in details, may we can figure out a better solution.

Resources