Scope issue with multiple directives on same page - angularjs

I have a directive that is used multiple times on the same page. The documentation has lead me to the following code:
<my-directive info="one"></my-directive>
<my-directive info="two"></my-directive>
.directive('myDirective', function() {
return {
restrict: 'E',
scope: {
itemInfo: '=info'
},
templateUrl: 'my_directive.html'
};
});
my_directive.html:
<div ng-repeat="item in itemInfo.items">
<div>{{item.name}}</div>
<div>{{item.value}}</div>
</div>
I can't get it to work, and I suspect it has to do with how I specify the scope in my directive and try to apply it in the ng-repeat. What am I missing?

Related

isolate controller to only controller specific scopes

So I have a directive and inside the directive view (html) I put a controller however its affecting the rest of the viewModel (vm). What's the best way to isolate a controller to only control specific viewModel?
That's the structure of the view model and directive, I thought ng-controller="ctrl as vm" would only find vm within the class of "controller" but instead its finding every vm on the page.
Directive:
var directive = {
templateUrl: '/Content/app/core/scaffolding/views/popup.html',
restrict: 'A',
link: function (scope, element, attributes) {
console.log('something')
}
};
view:
<div class="directive">
<div class="moreVm">
</div>
<div class="controller" ng-controller="ctrl as vm">
<button ng-click="vm.find()"></button>
</div>
</div>
I tried making "ctrl as jvm" but still the same haha, its just a guess.
<div class="controller" ng-controller="ctrl as jvm">
<button ng-click="jvm.find()"></button>
</div>
Try this.
var directive = {
restrict: "A",
scope: true,
bindToController: {},
controller: "ctrl as vm",
templateUrl: "/Content/app/core/scaffolding/views/popup.html"
};
I've come up with an example using directives which may be of some help - Plunker
As you can see clicking the button in directive2 does not set the value of $scope.aValue in directive1.
JS
var app = angular.module('plunker', [])
.directive("directive1", function accountDir() {
return {
restrict: "EA",
templateUrl: "directive1.html",
scope: {},
controller: function ($scope) {
$scope.$watch("aValue", function(newValue) {
console.log(newValue);
})
}
};
}
)
.directive("directive2", function accountDir() {
return {
restrict: "EA",
templateUrl: "directive2.html",
scope: {},
controller: function ($scope) {
$scope.setAValue = function () {
$scope.aValue = 42;
console.log($scope.aValue);
}
}
};
}
);
Markup
<body>
<directive1></directive1>
</body>
directive1.html
<directive2></directive2>
directive2.html
Directive2
<br>
<button ng-click="setAValue()">Set a value</button>
If I not guess wrong,you want do that when ctrl as different names, the directive console.log different value? or in vm but the value within directive is different with out of the directive?
if you want first ,you just make two controller and then set different value;
controller('ctrl1',function(){ this.name});
controller('ctrl2',function(){ this.name});
else want two
directive('myDir',function(){ return {restrict:'AE',scope:{},controller:function(){this.name='haha'}}})
and now the value is isolate with outer

Angular - understanding directive isolated scope method

take a look at the following code:
html:
<body ng-app="myApp">
<div ng-controller="MainController">
<input type="button" ng-click="talk()" value="outside directive" />
<div my-element>
<input type="button" ng-click="talk()" value="inside directive" />
</div>
</div>
</body>
js:
var app = angular.module('myApp', []);
app.controller('MainController', function($scope){
$scope.talk = function() {
alert('HELLO1');
}
});
app.directive('myElement', function(){
return {
restrict: 'EA',
scope: {},
controller: function($scope) {
$scope.talk = function() {
alert('HELLO2');
}
}
};
});
as you can see, there's a controller, which nests a directive.
there are 2 buttons, one in controller level (outside of directive), and one is inside the directive my-element.
the main controller defines a scope method talk, the nested directive controller also defines a method - talk - but keep in mind that directive has isolated scope (i'd expect that talk method won't be inherited into directive's scope).
both buttons result an alert of 'HELLO 1', while i expected the second button (inside directive) to alert 'HELLO 2' as defined in directive controller, but it doesn't - WHY?
what am i missing here? how could i get a result when the second button will alert 'HELLO 2' but with the same method name ("talk") ?
thanks all
If you want the inner content to use the directive scope, you need to use manual transclusion:
app.directive('myElement', function(){
return {
restrict: 'EA',
scope: {},
transclude: true,
link: function(scope, element, attr, ctrl, transclude) {
transclude(scope, function(clone, scope) {
element.append(clone);
});
},
controller: function($scope) {
$scope.talk = function() {
alert('HELLO2');
}
}
};
});
By default, transcluded content uses a sibling of the directive scope. I actually don't know how angular handles DOM content for directives that don't use transclude (which is what makes this an interesting question), but I would assume from the behavior you are seeing that those elements use the directive's parent scope by default.
This will work for you
<body ng-app="myApp">
<div ng-controller="MainController">
<input type="button" ng-click="talk()" value="outside directive" />
<div my-element></div>
</div>
</body>
app.directive('myElement', function(){
return {
restrict: 'A',
template: '<input type="button" ng-click="talk()" value="inside directive">',
replace: true,
scope: {},
controller: function($scope) {
$scope.talk = function() {
alert('HELLO2');
}
}
};
});

Angularjs Custom Directive ng-click not working

i've created a custom directive in angularjs:
directives.directive('myTop',function($compile) {
return {
restrict: 'E',
templateUrl: 'views/header.html',
}
})
Directive's code:
<div class="my-header">
<button ng-click="alert('x')" class="fa fa-chevron-left"></button>
<h1>SpeakZ</h1>
</div>
for some reason, ng-click doesen't trigger.
I searched over the internet and found that compile / link is the solution for this problem,
but I can't seem to reach a working solution.
I am not using jquery..
You'll need to add a link function to the directive definition for this to work. So basically,
var app = angular.module("myApp", [])
app.directive('myTop',function() {
return {
restrict: 'E',
template: '<button ng-click="clickFunc()">CLICK</button>',
link: function (scope) {
scope.clickFunc = function () {
alert('Hello, world!');
};
}
}
})
And the html:
<div ng-app="myApp">
<my-top></my-top>
</div>
And here's the fiddle: http://jsfiddle.net/4otpd8ah/
Either use link as answered by #Ashesh or just simply add scope. If you set scope false you will not have isolated scope and click will work on directive.
directives.directive('myTop',function($compile) {
return {
restrict: 'EA',
scope: false,
templateUrl: 'views/header.html',
}
})

Directive with isolated scope and added properties, not available to inner directives

I'd like to have a directive with an isolated scope, and to set properties to this scope from within the directive. That is to create some environment variables, which would be displayed by other directives inside it, like so:
HTML:
<div environment> <!-- this directive set properties to the scope it creates-->
{{ env.value }} <!-- which would be available -->
<div display1 data="env"></div> <!-- to be displayed by other directives (graphs, -->
<div display2 data="env"></div> <!-- charts...) -->
</div>
JS:
angular.module("test", [])
.directive("environment", function() {
return {
restrict: 'A',
scope: {},
link: function(scope) {
scope.env = {
value: "property set from inside the directive"
};
}
};
})
.directive("display1", function() {
return {
restrict: 'A',
require: '^environment'
scope: {
data: '='
},
link: function(scope, elt, attr, envController) {
scope.$watch('data', function(oldV, newV) {
console.log("display data");
});
}
};
})
.directive("display2", function() {
return {/* ... */};
});
But it doesn't work. Here is a Plunker.
If I remove the isolation, it works ok though. What do I do wrong ? Is it a problem of transclusion ? It seems to work if I use a template in the 'environment' directive, but this is not what I want.
Thanks for your help.
Edit: I see this same problem answered here. The proposed solution would be to use a controller instead of a directive. The reason I wanted to use a directive is the possibility to use 'require' in the inner directives, thing that can't be done with ngController I think.
By introducing external templates, I managed to find a working solution to your problem.
I'm quite certain the way you have it set up has worked at some point but I can't be certain about when. The last time I built a directive not reliant on an external markup file, I don't even know.
In any case, the following should work, if you are willing to introduce separate templates for your directives:
app.directive('environment', function () {
return {
restrict: 'A',
templateUrl: 'env.html',
replace: true,
scope: {},
link: function (scope, el, attrs) {
scope.env = {
value: "property set from inside the directive"
};
}
};
});
app.directive('display1', function () {
return {
restrict: 'A',
scope: {
data: '='
},
templateUrl: 'display1.html',
replace: false,
link: function(scope) {
// console.log(scope.data);
}
};
});
And then for your markup (these wouldn't sit in <script> tags realistically, you would more than likely have an external template but this is simply taken from the fiddle I set up).
<script type="text/ng-template" id="display1.html">
<span>Display1 is: {{data}}</span>
</script>
<script type="text/ng-template" id="env.html">
<div>
<h1>env.value is: {{env.value}}</h1>
<span display1 data="env.value"></span>
</div>
</script>
<div>
<div environment></div>
</div>
Fiddle link: http://jsfiddle.net/ADukg/5421/
Edit: After reading that you do not want to use templates (should've done that first..), here's another solution to get it working. Unfortunately, the only one you can go with (aside from a few others, link coming below) and in my opinion it is not a good looking one...
app.directive('environment', function () {
return {
restrict: 'A',
template: function (element, attrs) {
return element.html();
},
scope: {},
link: function (scope, el, attrs) {
scope.env = {
value: "property set from inside the directive"
};
}
};
});
And the markup:
<div environment> {{env.value}} </div>
Fiddle: http://jsfiddle.net/7K6KK/1/
Say what you will about it, but it does do the trick.
Here's a thread off of the Angular Github Repo, outlining your issue and why it is not 'supported'.
I did a small edit to your Plunker
When you create a variable on scope of directive other directives can access it two ways (presented in plunker) either directly or by two-way data binding
HTML:
<body ng-app="test">
<div environment>
{{ env.value }}
<div display1 data="env"></div>
<div display2 data="env"></div>
</div>
</body>
<input type="text" ng-model="env.value"> #added to show two-way data binding work
<div display1 info="env"></div> #changed name of attribute where variable is passed, it's then displayed inside directive template
<div display2>{{env.value}}</div> #env.value comes from environment directive not from display2
</div>
JS
angular.module("test", [])
.directive("environment", function() {
return {
restrict: 'A',
scope: true, #changed from {} to true, each environment directive will have isolated scope
link: function(scope) {
scope.env = {
value: "property set from inside the directive"
};
}
};
})
.directive("display1", function() {
return {
restrict: 'A',
template: '<span ng-bind="info.value"></span>', #added template for directive which uses passed variable, NOTE: dot in ng-bind, if you try a two-way databinding and you don't have a dot you are doing something wrong (Misko Hevry words)
scope: {
info: '=' #set two-way data binding for variable from environment directive passed in 'info' attribute
}, #removed unnecessary watch for variable
};
})
.directive("display2", function() {
return {/* ... */};
});

Angular directive with ng-repeat in template

I'm experimenting with angular 1.2 directives. Mine has a template w/ ng-repeat. The variable passed in param does not seem to be seen by the directive. Here is the code:
Fiddle: http://jsfiddle.net/supercobra/vmH3v/
Controller:
angular.module('myApp', [])
.controller('Ctrl', ['$scope', function($scope) {
$scope.labels=
[{name:"abc", color:'blue'},
{name:"xxx", color:'red'}];
}])
.directive('prettyTag', function() {
return {
restrict: 'E',
scope: {labelsArray: '#'},
template: '<h2>Label list:{{labelsArray}}:</h2><div class="label label-warning" ng-repeat="label in labelsArray">{{label.name}}</div>',
restrict: 'E',
};
});
HTML:
<div ng-app="myApp" ng-controller="Ctrl">
label Array: {{labels}}
<hr>
<pretty-tag labelsArray='{{labels}}'></pretty-tag>
<hr>
</div>
There are a few things that need to be changed for the directive to see the labels array.
First, change the pretty-tag HTML to this:
<pretty-tag labels-array='labels'></pretty-tag>
Note that labelsArray was changed to labels-array (directive & attribute names should follow this dashed convention) and {{labels}} was simply changed to labels (so that a bi-directional binding can be established on the array).
Next, inside your directive, the labelsArray scope should be '=' so that the local scope property can reference the parent scope property:
scope: {labelsArray: '='},
Fiddle:
http://jsfiddle.net/Hmcj8/

Resources