That's a noob question. I'm looking for the correct way to access the parent scope inside a directive in a nested ng-repeat. This is exactly what i mean:
<div ng-app="myApp" ng-controller="myCtrl">
<div ng-repeat="section in sections">
{{section.Name}}
<div ng-repeat="item in section.Items" ng-init="parent = section">
<span class="menuItem">{{item}}</span>
</div>
</div>
</div>
And the directive:
myApp.directive('menuItem', function () {
return {
restrict: 'C',
link: function (scope, element, attrs) {
console.log(scope.$parent.section.SectionId);
}
}
});
The directive is attached to an item in the inner ng-repeat, and i need to access a property of the parent object. The problem is that i cannot access directly to the parent properties (with scope.$parent), because ng-repeat creates a new scope, and i must append the name of the object i set in the ng-repeat (in this case scope.$parent.section.):
<div ng-repeat="section in sections">
console.log(scope.$parent.section.SectionId);
JsFiddle: http://jsfiddle.net/7Lra7Loy/2/
As i want the directive to be generic, so it can be used inside other ng-repeat blocks, without being forced to use the same names in the ng-repeat, the only way i found is to use an ng-init, that would be the same in all ng-repeat blocks (ng-init="parent = section"):
<div ng-repeat="section in sections">
{{section.Name}}
<div ng-repeat="item in section.Items" ng-init="parent = section">
<span class="menuItem">{{item}}</span>
</div>
</div>
myApp.directive('menuItem', function () {
return {
restrict: 'C',
link: function (scope, element, attrs) {
console.log(scope.parent.SectionId);
}
}
});
JsFiddle: http://jsfiddle.net/7Lra7Loy/1/
Is there a better way to handle this situation? Or am i just missing something? I searched a bit, but i couldn't find anything really useful.
Template:
<div ng-app="myApp" ng-controller="myCtrl">
<div ng-repeat="section in sections">
{{section.Name}}
<div ng-repeat="item in section.Items">
<span class="menuItem" section="{{section}}">{{item}}</span>
</div>
</div>
</div>
And directive:
myApp.directive('menuItem', function () {
return {
restrict: 'C',
scope: {
section: '#' // text-binding
//section: '&' //one-way binding
//section: '=' //two-way binding
},
link: function ($scope, element, attrs) {
console.log($scope.section);
}
}
});
JsFiddle: https://jsfiddle.net/nrkmn/26zhqbjg/
Related
I've built a custom directive for generating and populating a dropdown from JSON Data.
However I want to react to a dropdown selection with a function. Therefore I want to make use of ngChange (and ngModel).
So I try to start by updating the ng-model from the directive.
The link function in the Directive should give me everything I need.
BUT the fourth parameter is undefined therefore I can't use it or even compile.
Directive TS:
module lsw{
lswApp.lswAppModule.directive("guiDropdown",
() => ({
restrict: "E",
require: "ngModel",
scope: {
data: "=",
ctrl: "="
},
link(scope, element, attrs, ctrls) {
element.bind("change", () => {
});
},
templateUrl: "../../App_Scripts/Directives/guiDropdown.html"
}));}
"ctrls:{}"
My Idea was to make use of ctrls.$setViewValue to update ngModel
Directive HTML
<div class="elements">
<select name="{{data.name}}" class="dropdown-box">
<option ng-repeat="option in ctrl" ng-value="option">{{option}}</option>
</select>
Main View HTML
<div ng-repeat="element in HomeController.wrapper" ng-if="HomeController.trigger">
<div ng-switch="element.type">
<div ng-switch-when="dropdown">
<gui-dropdown data="element" ctrl="HomeController.content" ng-model="element" ng-change="HomeController.test()"></gui-dropdown>
</div>
<div ng-switch-when="textfield">
<gui-textfield data="element"></gui-textfield>
</div>
<div ng-switch-when="checkbox">
<gui-checkbox data="element"></gui-checkbox>
</div>
</div>
</div>
I have created a directive for a search box which i want to use with different views. Here is the directive -
angular.module('jobSeekerApp')
.directive('searchBoxDirective', function () {
return {
restrict: 'E',
templateUrl: 'templates/searchbox-template.html',
};
});
template for the directive -
<span class="searchButton"><i class="fa fa-search fa-2x"></i></span>
<input ng-change="search()" ng-model="searchTerm" ng-keydown="deleteTerm($event)" type="text" id="search-box" style="width: 0px; visibility:hidden;"/>
I want to use this directive on two views which look like this -
View 1 -
<div class="panel panel-default companies" ng-repeat="company in companies.companiesList">
<div class="panel-heading text-center"><a ng-href="/companies/{{company.id}}" class="hvr-sink"><h3 class="well">{{company.company_name}}</h3></a></div>
<div class="panel-body text-center flexcontainer">
<div>Location: {{company.location}}</div>
<div>Founded In: {{company.founded_year}}</div>
<div ng-if="company.opening">Opening: Yes</div>
<div ng-if="!company.opening">Opening: No</div>
<div>Number Of Openings: {{company.no_openings}}</div>
</div>
</div>
View 2 -
<div class="panel panel-default jobs" ng-repeat="job in jobs.jobsList">
<div class="panel-heading text-center"><h3 class="well">{{job.job_name}}</h3></div>
<div class="panel-body text-center flexcontainer">
<div>Company: {{job.company_name}}</div>
</div>
</div>
As you can see i am using aliases companies and jobs in my views, due to this my directive is not able to affect the view it is contained in. If i use the companies or jobs in my template , then it works fine. So for example if change the template to -
<span class="searchButton"><i class="fa fa-search fa-2x"></i></span>
<input ng-change="companies.search()" ng-model="companies.searchTerm" ng-keydown="companies.deleteTerm($event)" type="text" id="search-box" style="width: 0px; visibility:hidden;"/>
Then it works with the view associated with companies controller and similarly for jobs.
How can i use the directive with the respective controller instance?
Thank you.
Create a simple view for Search
Create it's own controller
Search record by this controller in service and put data in service variable
this.searchResult = [];
this.search = function(searchText){
// code to search
this.searchResult = Result;
}
Now where ever you want to use this result use the watch on this service variable in current controller, like:
$scope.$watch(function() { return servicename.searchResult ; }, function(newVal, oldval) {
if(newval != oldval){
$scope.data = servicename.searchResult;
/* Do the rest of stuff */
}
}, true);
Since the search function is asynchronous, I recommend avoiding the use of ng-change to invoke it. Instead use ng-click on the search icon.
<span class="searchButton" ng-click="$ctrl.searchFn()">
<i class="fa fa-search fa-2x"></i>
</span>
<input ng-model="$ctrl.searchTerm" type="text" id="search-box">
In the directive, use isolate scope and bindToController.
app.directive('searchBoxDirective', function () {
return {
restrict: 'E',
scope: { 'searchTerm': "=",
'searchFn': "&"
},
controller: function () {},
controllerAs: '$ctrl',
bindToController: true,
templateUrl: 'templates/searchbox-template.html'
};
});
Usage
<search-box-directive search-term="companies.searchTerm"
search-fn="companies.search()" >
</search-box-directive>
<search-box-directive search-term="jobs.searchTerm"
search-fn="jobs.search()" >
</search-box-directive>
The search-term attribute creates a bidirectional binding from parent scope to the directive isolate scope. The search-fn attribute creates an expression binding from parent scope to isolate scope.
Use isolate scope and explicitly pass the search term to your directive. Something like:
angular.module('jobSeekerApp')
.directive('searchBoxDirective', function () {
return {
restrict: 'E',
scope: {
searchTerm: '=' <-- you can use '#' if you don't need two-way binding
},
templateUrl: 'templates/searchbox-template.html',
};
});
You didn't show where you are actually using your directive, but you would pass the scope property through an attribute (this is using your directive on a <div>):
<div search-box-directive search-term="companies.searchTerm"></div>
I have developed a transclude directive and I set it to use angularjs template script everything works fine but I still cannot access the bind data.
My code:
index.html
<div side-element="box" title="Links">
<ul>
<li>Link 1</li>
<li>Link 2</li>
</ul>
</div>
<script id="box" type="text/ng-template">
<div class="side-box">
<div class="content">
<h2 class="header">Box {{ title }}</h2>
<span class="content" ng-transclude></span>
</div>
</div>
</script>
<script id="ad" type="text/ng-template">
<div class="side-ad">
<div class="content">
<h2 class="header">AD {{ title }}</h2>
<span class="content" ng-transclude></span>
</div>
</div>
</script>
app.js:
angular.module('myApp.directives')
.directive('sideElement', function ($templateCache, $log) {
return {
scope: {
title: '#'
},
transclude: 'element',
link: function(scope, element, attrs, ctrl, transclude){
var template = $templateCache.get(attrs.sideElement);
var templateElement = angular.element(template);
$log.info(scope.title);//Output the title i put in the html which is (Links)
transclude(scope, function(clone){
element.after(templateElement.append(clone));
});
}
};
});
the scope inside the link function(....) displaies the correct title but it doesn't work in the html:
Box {{ title }}
Link 1
Link 2
I think I missed one thing but I can't figure it out, I need your help to complete the cycle.
Thanx in advance,
The template element that contains the angular binding expressions must be compiled first, and then linked. The compilation phase prepares the template, while the linking phase sets up your $watchers for your binding expressions.
Demo Here
Here is a compile function that should work:
.directive('sideElement', function ($templateCache, $compile, $log) {
return {
restrict: 'A',
transclude: true,
scope:'#',
compile: function(element, attrs) {
var template = $templateCache.get(attrs.sideElement);
var templateElement = angular.element(template);
element.append(templateElement);
return function(scope, element, attr, ctrl, transclude) {
$log.info(scope.title);
}
}
}
I would like to create a list of items built in Directive and share through controllers.
Here is my example in plunker: Example of my code
Here is my code in js:
var app = angular.module('app', []);
app.controller("BrunchesCtrl", function ($scope, $log) {
$scope.brunches = [];
$scope.addNew = function () {
$scope.brunches.push({ address: "" });
};
$scope.$watch('brunch.address', function(value) {
$scope.address = value;
$log.info($scope.address);
});
$scope.$watch(function() { return $scope.brunches.length; }, function() {
$scope.brunches = $scope.brunches.map(function (brunch) {
return brunch;
});
});
});
app.directive('brunchesView', function ($compile) {
return {
restrict: 'E',
templateUrl: 'brunch.html',
controller: 'BrunchesCtrl',
replace: true,
link: function (scope, element, attrs, controller) {
}
};
});
app.directive('businessSubmit', function ($log, $compile, formManagerService) {
return {
restrict: 'AE',
controller: 'BrunchesCtrl',
link: function (scope, element, attrs, controller) {
formManagerService.init();
var hasError;
element.on('click', function (e) {
e.preventDefault();
$log.info("brunches: \n");
$log.info(scope.brunches);
});
}
};
});
Here is an HTML:
<!DOCTYPE html>
<div class="controls">
<a class="btn btn-danger" id="addBrunch" data-ng-click="addNew()">
<i class="icon-plus-sign"></i>
add new...
</a>
</div>
</div>
<brunches-view class="well" data-ng-repeat="brunch in brunches">{{brunch}}</brunches-view>
<br/>
<p class="well">
JSON: {{brunches | json}}
</p>
<div class="control-group">
<div class="controls">
<a class="btn btn-primary" href="#" data-business-submit>
<i class="icon-save"></i>
Save...
</a>
</div>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0/angular.min.js"></script>
<script src="//netdna.bootstrapcdn.com/bootstrap/3.0.3/js/bootstrap.min.js"></script>
<script src="script.js"></script>
And here is the template:
<div class="fc-border-separate">
<div class="control-group">
<label class="control-label">Address</label>
<div class="controls">
<input type="text" class="span6 m-wrap"
name="Address" data-ng-model="address"
value="{{address}}" />
</div>
</div>
</div>
The final thing I want to save the whole data inside the BusinessSubmit directive.
Help please...
Several issues with your code.
First, your ng-model for the <input> is set to address, but the object you are wanting to bind it to a brunch object that has an address property. Important to realize that ng-repeat will create a child scope for every repeated item
<input data-ng-model="brunch.address"/>
Another issue is you are assigning the parent controller to a directive as well. Important to understand that controllers are singletons so if you use controller more than once , each is a separate instance. Therefore nesting the parent controller in a directive makes no sense.
DEMO- [ng-model] fix
If you want the data shared with other controllers you should set up a service that holds the brunches data by injecting it into whatever controllers will need access
I have the following HTML to make an accordion:
{{isExpandAllOpen}} // Present in the scope of the calling page
<li class="row" ng-repeat="test in AllTests">
<div vh-accordion-group panel-class="panel-info">
<div vh-accordion-header> </div>
<div vh-accordion-body> </div>
</div>
</li>
In vhAccordionHeader.js we have the following code:
home.directive("vhAccordionHeader", ['version', function(version) {
return {
require: '^vhAccordionGroup',
replace: true,
restrict: 'EA',
transclude: 'element',
templateUrl: "JS/HomeModule/Directives/vhAccordion/vhAccordionHeader.html?v=" + version
};
}]);
home.directive("vhAccordionAssignId", function() {
return {
require: '^vhAccordionGroup',
link: function(scope, element, attrs, vhAccordionGroupController) {
scope.isOpen = true;
}
};
});
in AccordionHeader.html
<div class="panel-heading">
<h4 class="panel-title">
<a ng-click="isOpen = !isOpen" data-toggle="collapse" onclick=" return false; " vh-accordion-assign-id>
<i class="pull-left glyphicon" ng-class="{'glyphicon-chevron-up': isOpen, 'glyphicon-chevron-down': !isOpen}" style="margin: -2px 10px 0 0"></i><span ng-transclude></span>
</a>
</h4>
the isOpen variable controls the expand/collapse all functionality.
Since I want to implement a expand/collapse all functionality, using the isExpandAllOpen to be equal to IsOpen, when expanding all button is used.
I cannot find a way to assign isExpandAllOpen to isOpen as it is a diferent directive.
TIA
As you don't specify isolated scope for your vhAccordionAssignId directive you an get access to isExpandAllOpen via scope inheritance. Simply read scope.isExpandAllOpen. Mind that scope inheritance will work for reading only.
Now you want to be notified when it got changed? Put a watcher in your link function
scope.$watch('isExpandAllOpen', function(newVal){
scope.isOpen = newVal;
//do additional stuff if required
});