AngularJS - communication between directives not working properly - javascript

I have the following html:
<div ng-controller="collapseController">
<div><breadcrumb-visualiser breadcrumbs="breadcrumbs" /></div>
<div id="partialViewContainer">
<div id="personContainer" partial-view-loader view="person" parent="" breadcrumbs="breadcrumbs"></div>
</div>
</div>
Template breadcrumb-visualiser
<div style="width: 100%; background-color: #eee">
<div ng-repeat="breadcrumb in breadcrumbs">
<span class="customBreadcrumb"><a ng-href="" ng-click="">{{breadcrumb}}</a></span>
</div>
</div>
partial-view-loader
Loads an MVC partial view into the containing div. The loading partial view will be able to add yet another (new) view to the screen, while hiding the previous screen.
As you may see by the above html, this directive shares the breadcrumbs binding, provided by the collapseController.
The directive adds the latest breadcrumb (associated to the just loaded partial view) to the existing breadcrumb list like so:
$scope.AddBreadCrumb = function (breadcrumb) {
$scope.breadcrumbs.push(breadcrumb);
}
That's a function in the directive controller.
The issue
The collapseController initialises breadcrumbs with this value ['A', 'B'].
So breadcrumbs A and B are displayed right away.
The first load of partial-view-loader will add breadcrumb C, resulting in:
['A', 'B', 'C'].
As I click the button that causes a new view to be added, I will once again trigger partial-view-loader, but now for breadcrumb D.
The problem is that it does not seem to update the breadcrumb. There's no visual change. Internally though, changes have been done but incorrectly.
If I add logging to AddBreadCrumb like so:
$scope.AddBreadCrumb = function (breadcrumb) {
console.log($scope.breadcrumbs);
$scope.breadcrumbs.push(breadcrumb);
console.log($scope.breadcrumbs);
}
I get the following output:
before: ['A', 'B']
- push -
after: ['A', 'B', 'D']
Question
How come the added breadcrumb 'C' isn't preserved, and why is the new breadcrumb list not displayed (even if it's incorrect)?

In response to Divya:
self.AddChildByDirective = function (viewIdentifier, parentViewIdentifier) {
var html = '<div id="' + viewIdentifier + 'Container" fvl-partial-view-loader view="' + viewIdentifier + '" parent="' + parentViewIdentifier + '" breadcrumbs="breadcrumbs" /></div>';
var target = $('#partialViewContainer');
var linkFunc = $compile(html);
var content = linkFunc($scope);
target.append(content);
chainedScreensService.CollapsePartialByIdentifier(parentViewIdentifier);
}
That builds, compiles and appends the directive code for the new screen (view and parent are different).
Extra info:
I've changed the scope of both directives to scope: false. The idea is to make sure I'm using the variables declared by the controller, not something in an isolated scope. Zero difference.
This is the current code of the directives:
breadcrumbVisualiser
angular.module('directives.api').directive("breadcrumbVisualiser", [
function () {
return {
restrict: 'E',
scope: false,
templateUrl: 'Templates/Directives/BreadcrumbVisualiser.html',
controller: function () {
},
link: function (scope, element, attrs) {
}
}
}
]);
partialViewLoader
angular.module('directives.api').directive("partialViewLoader", [
'$compile',
'chainedScreensService',
function (
$compile,
chainedScreensService) {
return {
restrict: 'A',
scope: false,
controller: ['$scope', function ($scope) {
}],
link: function (scope, element, attrs) {
chainedScreensService.GetPartialView(scope.activeView).then(function (viewData) {
$.post(viewData.Url, function(view) {
var linkFunc = $compile(view);
var content = linkFunc(scope);
element.append(content);
scope.AddBreadCrumb(viewData.Subject);
});
});
}
}
}
]);
scope.AddBreadCrumb(viewData.Subject) is a function defined in the controller.

Related

Converting an item from a value within a controller to a directive

I am not sure if I am doing this correctly, but I am trying to convert something was in a controller to a directive because I want to use it multiple times and just change a few values, so instead of making many huge object literals, I will have just one and just change the values passed in. I am trying to bind chartConfig, but it doesn't seem to be working. Am I doing this wrong?
Here is my directive:
app.directive('percentageSquare', function(){
return {
restrict: 'E',
scope: {
bgClass: '#',
percentage: '#',
chartConfig: '='
},
link: function(scope){
var fontSize = 80;
var percentage = scope.percentage || 0;
scope.chartConfig = {
options: {
chart: {
// Chart settings here
}
}
};
},
templateUrl: '/templates/dashboard/charts/PercentageChart.html'
};
});
Here is the template that the directive is using (PercentageChart.html):
<div class="drop-shadow">
<div class="row">
<div class="col-sm-12">
<highchart config="chartConfig" class="{{bgClass||''}}" ng-show="true"></highchart>
</div>
</div>
</div>
Here is how I am calling the directive:
<percentage-square bg-class="bg-orange" percentage="23"></percentage-square>
Now my chartConfig no longer binds to the directive like it used to when it was in a controller. What can be done to fix this?
Edit
I have gotten a little further, this seems to work:
scope.$watch(scope.chartConfig, function(){
scope.chartConfig = {
// Chart Settings
};
});
But it seems to load the chart twice, as I get two animations.
Looks like what I want to do is watch chartConfig. it must also be wrapped in a string in order for it to work properly. Using scope.chartConfig loaded the chart twice, while 'chartConfig' loads the chart once and properly.
scope.$watch('chartConfig', function(){
scope.chartConfig = {
// Chart Settings
};
});

Dynamic injection of directive inside another directive template

Hello I'm new to AngularJS and I think I misunderstood something. Im trying to inject dynamically a directive inside another directive template.
For instance I have 2 directives "a-tag" and "b-tag" and I would like to add one of these 2 directives inside another directive "container".
I have something like this:
<body ng-app="myApp">
<container item="a-tag" a-color="#f00"></container>
</body>
I declared my "container" directive to get the item attribute (a-tag, b-tag, or any other directive) and inject it.
angular.module("Container", []).directive("container", function($compile){
return {
restrict: "EA",
scope: {
item: "#"
},
link: function(scope, element, attrs){
var template = '<div id="container">';
var item = '';
if(scope.item !== undefined){
item = '<' + scope.item;
item += ' ></' + scope.item + '>';
}
template += item + '</div>';
element.html(template);
$compile(element.contents())(scope);
}
};
});
It is working but i dunt know how to broadcast to my child directive (a/b,etc.) his attributes (for instance a-color="#f00" like used in the first piece of code).
My child directives look like this:
angular.module("A", []).directive("aTag", function(){
return {
restrict: "EA",
templateUrl: "template/a.html",
replace: true,
link: function(scope, element, attrs){
element.css("color", attrs.bColor);
}
};
});
It is a simple example. Actually I designed a modal popup (my container) and I would like to use it for several things such as displaying a form, a loader (my directives a-tag, b-tag, etc).
Any idea is welcome.
Ty,
RĂ©mi

Directive's scope not initialized prior to loading the directive template leading to not loading of Google Map

I am working with the Google-maps-angular directive in my app and I am trying to wrap that directive within my own directive. It works fine if I define the map object in the controller. But when I define the map object inside the link function, it doesn't seem to be getting the map object.
I tried defining the map object inside the directive's controller, but that didn't load the map either. The scope doesn't seem to be initialized prior to loading the html which explains why the map won't load, but I haven't been able to figure out why this is the case. Is there any other way I can define the map object inside the directive instead of an outside controller?
Also when looking inside the google-map's scope (using angular.element("google-map >").scope() ), it shows center to be defined so I am unsure why the map isn't loaded.
var app = angular.module('myApp',[
'google-maps',
])
app.directive('myDir', function($http) {
return {
restrict: 'AE',
template: '<google-map center="map.center" zoom="13" draggable="true" pan= "1" control="map.control"></google-map>',
link: function(scope, elem, attrs) {
scope.map = {
center: {
latitude: 40.35,
longitude: -74.6702
},
control:{}
}
},
};
});
I know that you are not asking about ngMap, but I found this way is a lot easier than http://angular-google-maps.org/
Here is the plunker, http://plnkr.co/edit/ZwUARi7OC2vUnJS6UdBD?p=preview
and this is your directive
var app = angular.module('myApp', ['ngMap']);
app.directive('myDir', function() {
return {
restrict: 'AE',
template: '<map center="{{mapOptions.center}}" zoom="13" draggable="true" pan="1"></map>',
link: function(scope, elem, attrs) {
},
};
});
It shouldn't be that hard. btw, I am the creator of ngMap

karma testing for $scope item not working / item not fired by $compile

I created a directive for my AngularJS app which checks the $scope to see if a profile image is present. If it is the directive shows the image by appending a new DIV, if not it shows a default via a css class. The directive looks like so... and it works in the app.
.directive('profileImage',function() {
return {
restrict: 'A',
scope: {
profileImage: '='
},
link: function(scope, element) {
if(!scope.profileImage) {
element.addClass('icon-default-profile-image defaultProfileImage');
} else {
element.append('<img ng-src="profileImage" class="profileImage">');
}
}
};
})
;
now I wish to write a unit test for this (I know I should have done that first but I am just learning about tests). I have written the following:
it('should display a specific image', inject(function($rootScope, $compile) {
//$rootScope.profileImage = "srcOfImage";
$scope = $rootScope;
$scope.profileImage = "srcOfImage";
var element = angular.element('<div profile-image="profileImage"></div>')
element = $compile(element)($scope)
console.log(element);
var imgElem = element.find('img');
expect(imgElem.length).toBe(1);
expect(imgElem.eq(0).attr('src')).toBe('srcOfImage');
}));
Now the test fails in this scenario (other tests where no $scope.profileImage is available has passed). When I out put the element I have created I get the following:
LOG: {0: <div profile-image="profileImage" class="ng-scope ng-isolate-scope"><img ng-src="profileImage" class="profileImage"></div>, length: 1}
So everything works but the profile-image="profileImage" in the var element = angular.element('<div profile-image="profileImage"></div>') is being taken as a literal string. It is not showing the value 'srcOfImage'. What am I doing wrong?
The error looks like so:
PhantomJS 1.9.2 (Mac OS X) Profile Image Directive should display a specific image FAILED
-src="prExpected undefined to be 'srcOfImage'.>, length: 1}
Big thanks for any advice / help / explanations
This is expected behavior because:
element.append('<img ng-src="profileImage" class="profileImage">');
appends a normal DOM image without compilation, so the properties are added as they are.
Try:
element.append('<img ng-src="profileImage" class="profileImage">');
$compile(element.contents())(scope); //adding this line to compile the image.
Remember to declare the $compile service in your directive:
.directive('profileImage',function($compile) {
One more problem is you have to use ng-src with {{}} like this:
ng-src="{{profileImage}}"
Your final directive looks like this:
.directive('profileImage',function($compile) { //declare $compile service.
return {
restrict: 'A',
scope: {
profileImage: '='
},
link: function(scope, element) {
if(!scope.profileImage) {
element.addClass('icon-default-profile-image defaultProfileImage');
} else {
element.append('<img ng-src="{{profileImage}}" class="profileImage">');
$compile(element.contents())(scope); //adding this line to compile the image.
}
}
};
})
;

how to write custom 'row' and 'col' element for angularjs

I'm trying to write a little dsl around the grid elements outlined here: http://foundation.zurb.com/docs/grid.php
basically what I wish to do is to
<row>
<column two mobile-one>{{myText}}</col>
<column four centered mobile-three><input type="text" ng-model="myText"></input></col>
</row>
transform into:
<div class="row">
<div class="columns two mobile-one">{{myText}}</div>
<div class= "columns four centered mobile-three"><input type="text" ng-model="myText"></input></div>
</div>
Ideally, I wish to write something that can take arbitrary nesting: row -> col -> row -> col -> row.....
I am having trouble getting the first step right - nesting the elements because I can't quite figure how how to get the child elements into another template without seriously compromising the compilation process.
var app = angular.module('lapis', []);
app.directive('row', function(){
return {
restrict: 'E',
compile: function(tElement, attrs) {
var content = tElement.children();
tElement.replaceWith(
$('', {class: 'row',}).append(content));
}
}
});
just does not do anything. The failed attempt is shown here - http://jsfiddle.net/ZVuRQ/
Please help!
I was hoping not to use ng-transclude because I found that it added an additional scope.
Here is a directive that does not use ng-transclude:
app.directive('row', function() {
return {
restrict: 'E',
compile: function(tElement, attrs) {
var content = angular.element('<div class="row"></div>')
content.append(tElement.children());
tElement.replaceWith(content);
}
}
});
You may want to use tElement.contents() instead of tElement.children().
You don't need jquery at all in your example (but you need to include it on your page/jsFiddle):
var app = angular.module('lapis', []);
app.directive('row', function(){
return {
restrict: 'E',
template: '<div class="row" ng-transclude></div>',
transclude: true,
replace: true
};
});

Categories

Resources