Creating child scopes in AngularJS - angularjs

I have a savingIndicator directive that will show a spinny, and then success / error messages. It binds to things like saving and message to show information to the user. The directive is pretty dumb and literally just loads a template like so
m.directive('savingIndicator', function () {
return {
restrict: 'E',
templateUrl: '/templates/savingindicator'
};
});
SavingIndicator.html (snippet):
<span ng-show="saving">
<img src="/Content/loader.gif" style="vertical-align: middle;" /> Saving...
</span>
In my current controller I have a number of things that need to save, so I want to separate them like so:
var thingToSave1 = {
saving: false,
message: ""
}
var thingToSave2 = {
saving: false,
message: ""
}
How can I tell savingIndicator not to look on the main scope (the controller) for its variables, but inside one of these objects? In my head it would work something like what is below, but its not currently working
<input text="Save item 1" />
<saving-indicator ng-model="thingToSave1"></saving-indicator>
...
<input text="Save item 2" />
<saving-indicator ng-model="thingToSave2"></saving-indicator>

You need to add another parameter in the directive's definition. The parameter you need is called "scope". Check out Angular's documentation is already explained there. If you want to play a little bit, this is a plunker (from angular docs):
...
return {
restrict: 'E',
scope: {
customerInfo: '=info'
},
templateUrl: 'my-customer-iso.html'
};
http://plnkr.co/edit/XFy6IB0cdBTMglItIgWR.
In that way you are specifying a new scope for the directive instead of using the controller one.

A quick hack would be as follows:
m.directive('savingIndicator', function () {
return {
restrict: 'E',
templateUrl: '/templates/savingindicator',
scope: {
myModel: '='
}
};
});
//---------------
<span ng-show="myModel.saving">
<img src="/Content/loader.gif" style="vertical-align: middle;" />Saving...
</span>
//---------------
<saving-indicator my-model="thingToSave1"></saving-indicator>
You can learn more about directives and isolate scope option in Angular docs.

Related

I want to change the value of the template of directive

I define a directive like this:
module.directive("jump", function(html){
var text = html.getHtml();
return{
scope:{},
restrict:"AE",
template: text,
}
});
And I have a service to store the html, like this:
module.factory('html',function(){
var html = "";
return {
setHtml: function(text){
html = text;
},
getHtml: function(){
return html;
}
}
})
I want to change the template value when I enter article pageļ¼Œbecause the detail of every article are different, and I only can get it from API, it's like
<p><a href="#" ng-click="open(\'http://www.facebook.com\')"facebook</a></p>
I want to define a directive for every article to show the detail of article.
But now, when I enter one article page, the directive's template is defined, and if I want to see other article, the template value is the first article's detail.I want to know how to change it.
"Note: All services in Angular are singletons."
(src: https://docs.angularjs.org/guide/providers)
If you need to have the html be different for each instance of your directive, you can't use a singleton that restricts it to a single instance.
Why not just define the template from within the directive? It's a much cleaner pattern that way. Something like this:
directive in use:
<div ng-repeat="foo in dataFromApiThatYouGetFirst">
<jump url="{{foo.url}}" title="{{foo.title}}"></jump>
</div>
directive:
restrict: 'E',
template: '<p>{{vm.title}}</p>',
replace: true,
scope: {
url: '=?',
title: '=?'
},
controllerAs: 'vm',
bindToController: true,

Issue with dom manipulation inside Directive link function - Angularjs

I set up a directive as follows:
.directive('ogTakeATour', function() {
return {
restrict: 'E',
replace: true,
templateUrl: '../scripts/directives/TakeATourTemplate.html',
scope: {
content: '#',
uid: '#'
},
link: function(scope) {
angular.element(scope.uid).css("top","250px");
}
};
});
Directive template looks like this:
<div id="{{uid}}" class="tourContainer">
{{content}}
</div>
And this is how I call my directive:
<og-take-a-tour content="Content goes here" uid="menuTour"></og-take-a-tour>
However for some reasons this does not apply the css to the applicable div.
angular.element(scope.uid).css("top","250px");
Why is this? Could it be that the directive does not know what the id of my element is at the time the link function is running? How would I get around this if that is the case?
Angular's jQlite does not support search by id or CSS selector. So change your code like this:
angular.element(document.querySelector('#' + scope.uid)).css("top", "250px");

How to delegate ngFocus/ngBlur to directive's template <input> element?

I'm trying to create a custom component (directive) which is composed of an <input> box and a [-] and [+] buttons. Currently, the example below only implements the input box.
So, say I have the following HTML for my directive:
<my-input ng-blur="onBlur($event)" ng-focus="onFocus($event)"></my-input>
And for testing purposes, I use this code:
app.run(function ($rootScope) {
$rootScope.onBlur = function ($event) {
console.log('onBlur', $event);
};
$rootScope.onFocus = function ($event) {
console.log('onFocus', $event);
};
});
Now I want to create my custom <my-input> directive which has an <input> box on the template and I need the ng-blur and ng-focus set on <my-input> to respond to blur/focus events on the input box.
I have the following solution almost working: http://codepen.io/anon/pen/KpELmj
1) I have a feeling that this can be achieved in a much better way, I just can't seem to do it. Thoughts?
2) $event seems to be undefined and I can't understand why. Thoughts?
Ok figured it out. Doron's answer was a good starting point for research, but now I think I have what you are looking for. The key is you have to use & in the link section in order to get it to execute the expression.
.directive('myInput', function($timeout) {
return {
restrict: 'E',
scope: {
data: '=',
blur: '&myBlur' //this is the key line
},
template: '<input ng-blur="blur($event)" ng-model="data">'
}
})
This is how you use it:
<my-input my-blur="runBlurFunc()"></my-input>
If you really want to define the function on the root scope, you can use $scope.$root.onBlur() instead of runBlurFunc()
Hope I got your question right, did you try to use the link function?
app.directive('myInput', function () {
return {
restrict: 'E',
scope: {
ngBlur: '&',
ngFocus: '&'
},
bindToController: true,
controller: controllerFn,
controllerAs: 'ctrl',
link:function(scope){
scope.onBlur = function(ev){
console.log(ev);
}
scope.onFocus = function(ev){
console.log(ev);
}
},
template: '[-]<input ng-blur="onBlur($event)" ng-focus="onFocus($event)"></input>[+]'
}
});

When using two way binding in angular, when are the binded variables actually available?

I'm writing a directive that uses two-way binding.My directive looks like this:
bankSearch.directive('bankSearch',
function() {
return {
restrict: 'E',
scope: {
bankDetail: '='
},
templateUrl: "angular/views/self_signup/bank_search.html",
link: function(scope) {
//Now when link function runs, scope.bankDetail is undefined.
}
}
});
Html of template-url:
`<div class="row-fluid">
<input id="ifsc-code" class="span12" type="text" name="ifscCode"
ng-model="bankDetail.bankBranch.ifscCode"
should-be-ifsc
ng-keypress="onPressEnter($event, bankSearch.ifscCode.$valid)">
</div>`
This is how I'm using the directive:
`<bank-search
bank-detail="bankSearchModel.bankDetail">
</bank-search>`
I was under the impression that link function runs after linking(watchers setup) is done. If I'm correct then why am I getting undefined for scope.bankDetail inside my link function.
I'm new to Angular. Thanks for the help!
Might be bankSearchModel.bankDetail defines after directive is rendered. In case it is loaded from $http or something else. Try
link: function(scope) {
scope.$watch('bankDetail', function(val){
console.log(val);
});
}
And you'll see all changes of that variable.

Call a function in a angular-controller from outside of the controller?

I have a lightbox-dierective and controller that looks like this:
directive('modalDialog', function() {
return {
restrict: 'E',
scope: {
show: '='
},
replace: true, // Replace with the template below
transclude: true, // we want to insert custom content inside the directive
template: '<div class="ng-modal" ng-show="show"><div class="ng-modal-overlay" ng-click="hideModal()"></div><div class="ng-modal-dialog" ng-style="dialogStyle"><div class="ng-modal-dialog-content" ng-transclude><div class="ng-modal-close" ng-click="hideModal()">X</div></div></div></div>'
};
}).controller('Lightbox', function($scope) {
$scope.modalShown = false;
$scope.toggleModal = function() {
$scope.modalShown = !$scope.modalShown;
};
});
Here is the desierd html, what I need is to open the secon ligthbox from withing the first one:
<div ng-controller="Lightbox">
<span ng-mousedown='toggleModal()'>Open lightbox one</span>
<modal-dialog show='modalShown'>
<h2>One lightbox <span ng-mousedown='toggleModal()'>Open lightbox two</span></h2>
</modal-dialog>
</div>
<div ng-controller="Lightbox">
<span ng-mousedown='toggleModal()'>Open lightbox one</span>
<modal-dialog show='modalShown'>
<h2>another lightbox</h2>
</modal-dialog>
</div>
For most cases it works great! I use it in several occations throughout the site, with diffrent lightboxes and different content.
I have now come across a case, where I need to call one of the lightboxes from outside of the controller. Can this be achieved and in that case how do I reference the right lightbox?
I'd extend that setting to an object
var modalSet = {
shown: false,
toggle: function(){ modalSet.shown = !modalSet.shown }
}
Then put it on your main controller (the one with ngApp attribute) and have your entire scope modaleble.
Also, directives do have a controller option, but since only one modal is gonna show up at any given time, you might not want to re-create a controller for every new instance.
Upon re-reading your question: Where is it exactly -> "outside of the controller"?

Resources