Isolate scope in reusable angular direcives - angularjs

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>

Related

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

User Input won't duplicate/display in directive template

Why are the user inputted values not duplicating when a user types into an input?
The user input works and duplicates when the HTML is separate from a custom directive template as shown below and in this fiddle: http://jsfiddle.net/Lvc0u55v/7069/ .
<div ng-controller="LeaseTemplateController">
<div class="leasespecial">
<div class="firstsec">
<div class="percNumber">
<h1 id="perId" ng-repeat="bb in percent_id">{{bb.value}}</h1>
</div>
</div>
<h2>Lease Special Template</h2>
<form>
<div class="form-group" ng-repeat="cc in percent_id">
<div class="input-group">
<input class="form-control input" type="text" placeholder="Enter Percent" ng-model="cc.value">
</div>
</div>
</form>
</div>
<script>
var myApp = angular.module('myApp', []);
myApp.controller('LeaseTemplateController', ['$scope', function($scope) {
//Lease Special Template
$scope.percent_id = [{
value: '20'
}];
}]);
</script>
However, instead I'm trying to insert it using two different directive templates as shown in this fiddle: http://jsfiddle.net/Lvc0u55v/7068/
<div lease-text-directive>
</div>
<div lease-input-directive>
</div>
<script>
var myApp = angular.module('myApp', []);
myApp.controller('LeaseTemplateController', ['$scope', function($scope) {
//Lease Special Template
$scope.percent_id = [{
value: '20'
}];
}]);
myApp.directive('leaseTextDirective', function() {
return {
restrict: 'A',
template: '<div class="leasespecial" ng-controller="LeaseTemplateController">\
<div class="firstsec">\
<div class="percNumber">\
<h1 id="perId" ng-repeat="bb in percent_id">{{bb.value}}</h1>\
</div>\
</div>'
};
});
myApp.directive('leaseInputDirective', function() {
return {
restrict: 'A',
template: '<h2>Lease Special Template</h2>\
<form ng-controller="LeaseTemplateController">\
<div class="form-group" ng-repeat="cc in percent_id">\
<div class="input-group">\
<input class="form-control input" type="text" placeholder="Enter Percent" ng-model="cc.value">\
</div>\
</div>\
</form>'
};
});
</script>
Why are the values not duplicating over in the second example and would you suggest a better practice than this?
I believe your experiencing the separation of scopes. Your directives have a different scope than your controller so it knows nothing. Try injecting your rootscope or scope like myApp.directive('leaseInputDirective', function($rootScope, $scope)
got it working now
var myApp = angular.module('myApp', []);
myApp.controller('LeaseTemplateController', function($scope,$rootScope) {
//Lease Special Template
$rootScope.percent_id = [{
value: '20'
}];
});
myApp.directive('leaseTextDirective', function() {
return {
restrict: 'E',
replace: true, // Replace with the template below
transclude: true, // we want to insert custom content inside the directive
template: '<div class="leasespecial" ng-controller="LeaseTemplateController">\
<div class="firstsec">\
<div class="percNumber">\
<h1 id="perId" ng-repeat="bb in percent_id">{{bb.value}}</h1>\
</div>\
</div>'
};
});
myApp.directive('leaseInputDirective', function() {
return {
restrict: 'E',
replace: true, // Replace with the template below
transclude: true, // we want to insert custom content inside the directive
template: '<div><h2>Lease Special Template</h2>\
<form ng-controller="LeaseTemplateController">\
<div class="form-group" ng-repeat="cc in percent_id">\
<div class="input-group">\
<input class="form-control input" type="text" placeholder="Enter Percent" ng-model="cc.value">\
</div>\
</div>\
</form></div>'
};
});
<lease-text-directive>
</lease-text-directive>
<!-- leaseTextDirective -->
<lease-input-directive>
</lease-input-directive>

how to access controller dataset in custom directive

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'
}
];

Angular JS Directive loaded in expression

I'm new to Angular, and have found a ton of resources about directives and nesting, but can't seem to get this simple example to work. So basically I am working on a tabset, I have an HTML template:
tabset.html
<div>
<ul>
<li ng-repeat="tab in tabset.tabs" ng-class="{active:tabset.current()==$index}">
<a href ng-click="tabset.current($index)">{{tab}}</a>
</li>
</ul>
<div>
<div ng-repeat="pane in tabset.panes">
<div ng-show="tabset.current()==$index">
{{pane.contents}}
</div>
</div>
</div>
</div>
And a search form template:
search-form.html
<div>
<form name="ytSearch" ng-submit="YTCtrl.submit()" novalidate>
<label for="search_box">Search For: </label>
<input id="search_box" ng-model="YTCtrl.searchString"/>
<br>
<label for="location">Location: </label>
<input id="location" ng-model="YTCtrl.location"/>
within
<input type="numeric" value="100" ng-model="YTCtrl.locationRadius" />
<select ng-model="YTCtrl.locationUnit">
<option value="ft">Feet</option>
<option value="m">Meters</option>
<option value="mi">Miles</option>
<option value="km">Kilometers</option>
</select>
<br>
<label for="search_order">Sort By: </label>
<select id="search_order" ng-model="YTCtrl.order">
<option value="relevance">Relevance</option>
<option value="date">Date</option>
<option value="rating">Rating</option>
</select>
<br>
<button id="search">
Search
</button>
</form>
</div>
And a simple app file with 2 directives to handle each of the templates:
app.js
(function() {
angular.module("JHYT", [])
.directive("sidebarTabset", function($compile) {
return {
restrict : 'E',
templateUrl : 'tabset.html',
controller : function($scope, $compile, $http) {
this._current = 0;
this.current = function(i) {
if (i != null)
this._current = i;
return this._current;
};
this.tabs = ['Search', 'Favorite'];
this.panes = [{
contents : "<search-form></search-form>"
}, {
contents : "Favorite Pane"
}];
},
controllerAs : 'tabset',
};
}).
directive("searchForm", function() {
return {
restrict : 'E',
templateUrl : 'search-form.html',
controller : function($scope, $compile, $http) {
this.searchString = '';
this.location = '';
this.locationRadius = '';
this.locationUnit = 'mi';
this.order = 'relevance';
this.submit = function() {
console.log("Submit");
};
},
controllerAs : 'YTCtrl',
}
});
})();
So as you can probably tell, the idea is to be able to send a JSON object into the tabset (through a service probably) and have it build out a dynamic tabset, that actually works exactly as I expected it to. What isn't working is that in the first tab, the content, which is <search-form></search-form> is not processed, and the tag is rendered as plain text in the content area.
Since this is a tabset, the "child" doesn't need anything from the "parent", the search form and the tab itself have no scope dependencies. I tried playing with the link and compile functions after seeing some examples of nested structures, but can't seem tog et them to work.
How can I process the content of that variable so that element directives are rendered using their templates?
EDIT:
#sielakos Gave me exactly what I was hoping for, a reusable method for doing this.
I added a directive to my module called compile, which adds a wrapper to allow me to use plain text:
.directive("compile", function($compile){
return {
restrict: 'A',
link: function(scope, element, attr){
attr.$observe("compile", function(str){
var compiled = $compile("<div>"+str+"</div>")(scope);
jQuery(element).replaceWith(compiled);
})
}
}
})
And I changed my tabset to use this directive:
<div>
<ul>
<li ng-repeat="tab in tabset.tabs" ng-class="{active:tabset.current()==$index}">
<a href ng-click="tabset.current($index)">{{tab}}</a>
</li>
</ul>
<div>
<div ng-repeat="pane in tabset.panes">
<div ng-show="tabset.current()==$index">
<div compile="{{pane.contents}}"></div>
</div>
</div>
</div>
</div>
You will need to compile your string using $compile service if you wish to use it as you would use template. Otherwise it will be treated as normal string and displayed as it is.
Here is example how to use it inside directive:
var compiled = $compile(str)(scope);
element.empty();
element.append(compiled);
If you wish you can look at this fiddle for more complex example:
https://jsfiddle.net/x78uuwp2/
Here I created simple compile directive that takes string compiles it and puts as element body with current scope.

Angular JS - template of a directive ng -model does not work within ng -switch

To become more dynamic my directive decided to include the category field that makes the selection of the type of template to be displayed. As it is only a select thought of using the switch - ng instead of several html files. (google translate)
Plunker: http://plnkr.co/edit/fnCJj15XJN1kQvKq1OtZ?p=preview
index.html
<div ng-controller="MainCtrl">
<sg-combo
selected-item="selectedItem" categoria="filtro">
</sg-combo>
{{selectedItem}}
script.js
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.selectedItem = {value: null};
$scope.$watch('selectedItem',function(item){
if (item != null){
alert(item.nome); // does not display when updating
}
})
});
app.directive('sgCombo', function(){
function link(scope, elem, attrs){
scope.dados = [
{'codigo':1, 'nome':'teste1'},
{'codigo':2, 'nome':'teste2'},
{'codigo':3, 'nome':'teste3'}
];
}
return {
restrict: 'E',
scope: {
selectedItem: '=',
categoria: '#'
},
link: link,
templateUrl:"sg-combo.html"
}
})
sg-combo.html
<div ng-switch="categoria">
<div ng-switch-when="filtro" class="col-sm-4 control-label">
<div class="col-sm-4 control-label">
<label>{{label}}</label>
<select ng-model="selectedItem.value" ng-options="item.nome for item in dados" class="form-control"></select>
</div>
</div>
<div ng-switch-when="anexo" class="col-sm-4 control-label">
<div class="col-sm-4 control-label">
<label>{{label}}</label>
<select ng-model="selectedItem.value" ng-options="item.nome for item in dados" class="form-control"></select>
</div>
</div>
</div>
Try to make 'selectedItem' a part of an object.
For instance:
In controller:
$scope.options = {'selectedItem': someItem};
In template:
ng-model="options.selectedItem"
ng-switch adds a scope and you should use '.' in ng-model.

Resources