Angular How to pass scope to view from service? - javascript

i have global function in helper service.
this.displayModalWithInput = function ($scope, title, cotroller, functionCallback) {
$scope.modalProperties = {
modalTitle : title,
modalController : cotroller,
modalFunction : functionCallback
};
$('#myModal').modal();
};
In my template i have standard Bootsrap modal window. Problem is if i'm trying to access to scope using:
{{modalProperties.modalTitle}}
Nothing is displayed.
How can in solve it, if i want to use global available function and pass data from this global function into accessible scope?
Thanks for any advice.

first define $scope.modalProperties=[]; in your controller.
Then, when you call displayModalwithInput function from controller, there please pass $scope as one of arguments. e.g. displayModalwithInput($scope, ...)
if it doesn't work, i'd like to see your global service code.

Services (factories) are singletons and its not good practice (I would say you can't) to inject scope to service.
In your case to avoid code duplicate I would create directive instead (isolate scope) where in link function you can write all relevant logic.
This Demo might help you PLUNKER
Directive Example
.directive( 'confirmPopup', function () {
return {
restrict: 'A',
replace: true,
scope: { title: '#', content: '#', placement: '#', animation: '&', isOpen: '&', func: '&' },
templateUrl: 'confirmPopup.html',
link: function ($scope, element, attrs) {
$scope.confirm = function(){
$scope.$parent.deleteThing();
$scope.$parent.tt_isOpen = false;
}
$scope.cancel = function(){
$scope.$parent.tt_isOpen = false;
}
}
};
})

Related

Passing Function Arguments with Isolated and Share scopes with Angular Directives

I have 3 directive with isolate scope and share scope and I want pass a function beteween outermost a innermost directive. The outer and middle has isolate scopes and the middle with inner share the scope. Any suggest ?
Pass the functions of my controller as shown below .
<outer on-edit="helloWorld" ng-model="model" ng-repeat="items in items.objects" ></outer>
In my controller:
$scope.helloWorld = function(){
alert('Hello world');
}
My directive:
angular.module('myApp')
.directive('outer', function () {
return {
restrict: 'E',
replace: true,
scope: {
item: "=ngModel",
onEdit: '&'
},
template: '<div><middle on-edit='onEdit'></middle></div>',
controller : function($scope){
$scope.edit = function(){
$scope.onEdit()();
}
}
};
})
.directive('middle', function () {
return {
restrict: 'E',
replace: true,
scope: {
item : '=ngModel',
onEdit : '&'
},
templateUrl: '<div><inner on-edit='onEdit'></inner></div>'
};
})
.directive('inner', function () {
return {
restrict: 'E',
template: '<div><a ng-click='edit()'>Edit</a></div>'
};
})
And this not work, any ideas?
Thanks
This looks a bad design though, but in the middle directive's template you are using inner directive as follows:
<div><inner on-edit='onEdit'></inner></div>
If you look at it, inner directive has no scope, so the attribute on-edit doesn't make sense there.
If you want to use any method that is present in middle directive can be directly used in inner directive because of shared scope. Think of inner directive as a part of html written in some other html file which will be replaced at run time.
So anything you pass to middle directive is implicitly passed to inner.

ng-click in angular directive - pass function from root scope

Fixed the issue, here is the final fiddle that shows it working:
http://jsfiddle.net/mbaranski/tfLeexdc/
I have a directive:
var StepFormDirective = function ($timeout, $sce, dataFactory, $rootScope) {
return {
replace: false,
restrict: 'AE',
scope: {
context: "=",
title: "="
},
template: '<h3>{{title}}</h3><form id="actionForm" class="step-form"></form><button ng-click="alert()" type="button">Save</button>',
link: function (scope, elem, attrs) {
}
}
}
How do I make the alert() do something from the controller?
Here is a fiddle:
http://jsfiddle.net/mbaranski/tfLeexdc/
Angular can be twitchy, so I've built a whole new fiddle to demonstrate all of the "glue-up" pieces you need to make this work.
First, you weren't passing the properties through to the directive, so I've made that adjustment:
// You have to pass the function in as an attribute
<hello-directive list="osList" func="myFunc()"></hello-directive>
Second, you were using onclick instead of ng-click in your template, which was part of the problem, so I made that switch:
// You need to use "ng-click" instead of "onclick"
template: '<h3>{{list}}</h3><button ng-click="func()" type="button">Button</button>',
And lastly, you need to bind the function in the scope of the directive, and then call it by the bound name:
scope: {
list: "=",
// Bind the function as a function to the attribute from the directive
func: "&"
},
Here's a Working Fiddle
All of this glued up together looks like this:
HTML
<div ng-controller="MyCtrl">
Hello, {{name}}!
<hello-directive list="osList" func="myFunc()"></hello-directive>
</div>
Javascript
var myApp = angular.module('myApp', []);
function MyCtrl($scope) {
$scope.name = 'Angular Directive';
$scope.osList = "Original value";
$scope.stuffFromController = {};
$scope.myFunc = function(){ alert("Function in controller");};
};
var HelloDirective = function() {
return {
scope: {
list: "=",
func: "&"
}, // use a new isolated scope
restrict: 'AE',
replace: false,
template: '<h3>{{list}}</h3><button ng-click="func()" type="button">Button</button>',
link: function(scope, elem, attrs) {
}
};
};
myApp.directive("helloDirective", HelloDirective);
If you'd like to execute a function defined somewhere else, make sure you pass it in by the scope directive attribute.
Here you can do:
scope: {
context: '=',
title: '=',
alert='&' // '&' is for functions
}
In the place where you using the directive, you'll pass the "expression" of the function (meaning not just the function, but the actual invocation of the function you want to happen when the click occurs.
<step-form-directive alert="alert()" title=".." context=".."></step-form-directive>

AngularJS - Same level directives mistaking same name methods

I have two directives: directiveA and directiveB, hanging from the same module in my AngularJS application.
They are called at the same level in the same HTML template, so we could say they are brothers.
<directive-a>
<directive-b>
Both of them have an own method hanging from its scope, like this:
$scope.clickOkey = function () {
... whatever
};
They both have a 'clickOkey' method, but their behaviours are different.
My problem comes out when I try to call the 'clickOkey' of directiveA from directiveA's template. It executes the 'clickOkey' from directiveB.
Inside directiveA's own template:
<label ng-click="clickOkey()">Okey</label>
They are placed at same level so there it shouldn't be way for them to share their $scope or misunderstanding methods.
Also, is important to say that if I change the method's name to 'clickOkeyA', for example, it takes the right method, so the template can access to its scope without problems.
What am I missing?
Thanks for your help!
Edit:
Both directives are isolated and have a controller, and inside each one of them is defined a 'clickOkey' method. There are two methods with the same name.
Both directives are like this:
angular.module('myModule').directive('directiveA', function () {
return {
restrict: 'AE',
templateUrl: '/whatever.html',
controller: function ($scope, $http, $rootScope) {
$scope.clickOkey = function () {
... whatever
};
}
}
});
You should add an isolated scope to your directives :
https://docs.angularjs.org/guide/directive
For your directive it would be :
angular.module('myModule').directive('directiveA', function () {
return {
restrict: 'AE',
templateUrl: '/whatever.html',
scope : {},
controller: function ($scope, $http, $rootScope) {
$scope.clickOkey = function () {
... whatever
};
}
}
});

Directive accessing to parent controller function without $scope

So I have this directive that has its own scope but I want to access to a function inside its parent controller. I can do this if the parent controller exposes the function with a $scope.getElementsList(), although I'm trying to avoid the use of $scope and I have the function exposed with self.getElementsList() and the directive cannot reach it.
Directive:
angular.module('myApp').directive('accountBalance', function() {
return {
scope: {
elementId: '=elementid'
},
transclude: true,
restrict: 'E',
templateUrl: '../views_directives/account-balance.html',
controller: function($scope) {
$scope.removeElement = function(){
//this where I want to access the parent function
console.log($scope.$parent.getElementsList());
console.log("ALSO I WANT TO ACCESS THIS DIRECTIVE elementId WITHOUT USING $scope", $scope.elementId);
}
}
};
});
ParentController:
angular.module('myApp').controller('AppDesignCtrl', function ($scope) {
var self = this;
self.elementsList = [];
self.getElementsList = function(){
return self.elementsList;
}
});
I also want to know what is the best way to access, inside the directive controller, the data passed to the directive's $scope.
scope: {
elementId: '=elementid'
},
UPDATE
<div>
<i class="fa fa-arrows element-drag"></i>
<i class="fa fa-trash-o element-remove" ng-click="removeElement()"></i>
</div>
And what about calling functions from the directive template inside the controller of the directive? Do I need to expose them with something like $scope.removeElement()? How do I use this.removeElement() and be able to access it from the template?
Sorry about the long question. I'm trying to set the best practices to my new project since I've been away from angular for a year+.
Thanks in advance
(Going from bottom to top...)
To call functions in the controller without using the scope in Angular >= 1.2, use the controllerAs syntax:
<div ng-controller="AppDesignCtrl as appDesignCtrl">
...
<i class="fa fa-trash-o element-remove" ng-click="appDesignCtrl.removeElement()"></i>
</div>
And removeElement() must be a method of the controller:
angular.module('myApp').controller('AppDesignCtrl', function ($scope) {
...
this.removeElement = function() {
...
};
});
To access the scope data from the controller in Angular >= 1.3, use the new bindToController: true configuration (this is especially useful when combined with the new controllerAs syntax):
angular.module('myApp').directive('accountBalance', function() {
return {
...
scope: {
elementId: '=elementid'
},
controller: function() {
// now elementId is a member of the controller:
console.log(this.elementId);
}
};
});
Having said these, the answer to how you can call getElementsList from the directive would be:
angular.module('myApp').directive('accountBalance', function() {
return {
...
scope: {
elementId: '=elementid',
getElementList: '&'
},
controller: function() {
...
// invoking the expression that was passed to us
var theElements = this.getElementList();
}
};
});
The correct expression should be passed as:
<div ng-controller="AppDesignCtrl as appDesignCtrl">
<account-balance element-id="xxx"
get-elements-list="appDesignCtrl.getElementsList()"></account-balance>
</div>
It is generally not recommended, because directives are meant to be self-contained. It isn't critical if you don't plan to reuse the directive. And wise usage of isolate scope can solve this.
angular.module('myApp').directive('accountBalance', function() {
return {
scope: {
outerScope: '#'
elementId: '='
},
transclude: true,
restrict: 'E',
templateUrl: '../views_directives/account-balance.html',
controller: function($scope) {
console.log("we can use anything from other controller", $scope.outerScope.elementsList)
$scope.elementId = "and share data with any other scope";
}
};
});
Controller is defined as ng-controller="AppDesignCtrl as appDesign", and directive usage is
<account-balance element-id="sharedParentScopeVar" outer-scope="appDesign">
So there won't be any problem if the directive should be moved to other controller.
I guess 'best practice' may be to set up a service that embraces the data and is used by both app controller and directive, so directive controller operates on data items and not DOM elements.
And what about calling functions from the directive template inside
the controller of the directive? Do I need to expose them with
something like $scope.removeElement()?
You surely don't. If there's a need to use functions from outside, you're doing something wrong. Send a message to respective element to run the function if it is DOM-related. Or put the function into the service if it is data-related.

Angularjs - how do i access directive attribute in controller

I am new to angularjs and i am stuck in accessing directive attributes in controller.
Directive
<rating max-stars="5" url="url.json"></rating>
app.directive('rating', [function () {
return {
restrict: 'E',
scope: {
maxStars: '=',
url: '#'
},
link: function (scope, iElement, iAttrs) {
console.log(iAttrs.url); //works
}
controller
app.controller('ratingController', ['$scope', '$attrs' , '$http','$routeParams',function ($scope,$attrs,$http,$routeParams) {
console.log($attrs.url); //shows undefined
}]);
How do i access the url attribute in controller?
If you want to associate a controller with a directive, you can use the Directive Definition Object's controller property (either specifying the controller as a function or specifying the name of the controller (in which case it can be registered anywhere in your module)).
app.directive('rating', [function () {
return {
restrict: 'E',
scope: {
maxStars: '=',
url: '#'
},
controller: 'ratingController'
};
});
// Meanwhile, in some other file
app.controller('ratingController', function ($scope, $element, $attrs) {
// Access $attrs.url
// Better yet, since you have put those values on the scope,
// access them like this: $scope.url
...
});
When using two-way data-binding via =, the corresponding attribute's value should not be a literal (because you can't have two-way data-binding to a literal), but a string specifying the name of a property in the current scope.
Using <rating max-stars="5"... together with scope: {maxStars: '='... is wrong.
You hould either use <rating max-stars="5"... and scope: {maxStars: '#'...
or <rating max-stars="someProp"... and scope: {maxStars: '='... while the enclosing scope has a property named someProp with a numeric value (e.g. $scope.someProp = 5;).
app.directive('myDirective',function(){
return{
controller: function($scope,$attrs){
console.dir($attrs);
}
}
});
That's it. If you want to access the elements attributes on a controller, you have to set up a controller for the directive.
(You could however, use a shared service to make those attributes available to another controller, if that's want you want to achieve)
http://jsbin.com/xapawoka/1/edit
Took your code and made a jsBin out of it. I can't see any problems whatsoever, so I'm assuming this is a simple typo somewhere in your code (could be the stray [ bracket at the top of your directive definition).
Here's the code:
var app = angular.module('app', []);
app.controller('ratingController',
function ($scope, $element, $attrs) {
console.log('ctrl.scope', $scope.url);
console.log('ctrl.attrs', $attrs.url);
});
app.directive('rating', function () {
return {
restrict: 'E',
scope: {
maxStars: '=',
url: '#'
},
controller: 'ratingController',
link: function (scope, el, attrs) {
console.log('dir.scope', scope.url);
console.log('dir.attrs', attrs.url);
}
};
});
And here's the output:
http://cl.ly/image/031V3W0u2L2w
ratingController is not asociated with your directive. Thus, there is no element which can hold attributes bound to that controller.
If you need to access those attributes, the link function is what you need (as you already mentioned above)
What exactly do you want to achieve?

Categories