Not able to access scope from directive of controller in AngularJS - javascript

I am new to AngularJS, I have used service to get data from back end and received it in controller,now I have to parse those values and dynamically create elements in directive,when I am trying to do so I am getting undefined for values in controller.
app.js:
var app = angular.module('childAid', ["myDirectives"]);
app.controller('headerController', function($scope, headerFactory) {
debugger
headerFactory.getAllChallenges()
.then(function(data) {
$scope.challengeslist = data.header;
});
});
directiveExample.js
var myDirectives = angular.module('myDirectives', []);
myDirectives.directive('headerPicker', function() {
return {
restrict: 'AE',
templateUrl: 'component/views/header.html',
link: function($scope, element, attributes) {
console.log('linking foo ' + $scope.challengeslist);
}
};
});
serviceExample.js:
(function() {
angular .module('childAid').factory('headerFactory', headerFactory);
function headerFactory($http) {
function getAllChallenges() {
debugger
return $http
.get('resources/stubs/details_tree.json')
.then(complete)
.catch(failed);
}
// helper function to handle the resolved promise
function complete(response) {
debugger
return response.data;
}
// helper function to handle the rejected promise
function failed(error) {
console.error(error.statusText);
}
return {
getAllChallenges: getAllChallenges
};
}
headerFactory.$inject = ['$http']; })();
index.html:
<div ng-app="childAid" ng-controller="headerController">
<div class="container">
<h2>Displaying Header</h2>
<header-picker></header-picker>
</div>
I don't know where I am doing wrong,Kindly help I am new to AngularJS.

Have you tried using an isolate scope on your directive. It might work out easier for you if you tried something like this..
DEMO
Obviously I don't have access to your api so I've mocked it using a public dummy api, so you will have to modify this to work with your own server.
The important bits are..
return {
restrict: 'AE',
templateUrl: 'header.html',
scope: {
'challengesList' : '='
}
};
.. in your directive, and
<header-picker challenges-list='challengesList'></header-picker>
.. in your html.
Here's the whole thing for reference. Hope you find it useful. Sorry if you've already tried this.
app.js
var app = angular.module('childAid', ["myDirectives"]);
app.controller('headerController', function($scope, headerFactory) {
debugger
headerFactory.getAllChallenges()
.then(function(response) {
$scope.data = response;
// $scope.challengesList = data.header;
// dummy response has a data.title property,
$scope.challengesList = response.data.title;
});
});
angular
.module('childAid')
.factory('headerFactory', headerFactory);
headerFactory.$inject = ['$http'];
function headerFactory($http) {
function getAllChallenges() {
debugger
// var url = 'resources/stubs/details_tree.json';
// dummy api for demo purposes, obviously delete this
// and uncomment your own url for your own app
var url = 'http://jsonplaceholder.typicode.com/posts/1';
return $http
.get(url)
.catch(failed);
}
// helper function to handle the rejected promise
function failed(error) {
console.error(error.statusText);
}
return {
getAllChallenges: getAllChallenges
};
}
var myDirectives = angular.module('myDirectives', []);
myDirectives.directive('headerPicker', function() {
return {
restrict: 'AE',
templateUrl: 'header.html',
scope: {
'challengesList' : '='
}
};
});
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js#1.4.x" src="https://code.angularjs.org/1.4.2/angular.js" data-semver="1.4.2"></script>
<script src="app.js"></script>
</head>
<body>
<div ng-app="childAid" ng-controller="headerController">
<pre>{{data}}</pre>
<div class="container">
<h2>Displaying Header</h2>
<header-picker challenges-list='challengesList'></header-picker>
</div>
</div>
</body>
</html>
header.html
<p>challengeslist = {{challengesList}}</p>

Related

How to use angular listener when using ng-repeat?

When I click a button, the controller will get data from some $http service callback function and will $broadcast it on $scope. Then, in the directive, I have a listener to get the data and do some logic. But, when I am using ng-repeat on button for several formats, the listener will get fired for all ng-repeat items when I click on each button. How can I make the listener to get fired only for the clicked button? Please see the sample code below.
var app = angular.module('demoApp', []);
app.controller('MyCtrl', function($scope) {
var myCtrl = this;
myCtrl.getFile = function(){
var response = 'some data';
$scope.$broadcast('downloadFile', {'response': response});
}
});
app.directive('fileDownload', function(){
return {
restrict: 'A',
link: function(scope, elem, attrs){
var cancelEvent = scope.$on('downloadFile', function(event, args){
console.log('called', args);
});
scope.$on('$destroy', function(){
cancelEvent();
});
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="demoApp">
<div ng-controller="MyCtrl as myCtrl">
<button ng-repeat="format in ['TXT','PDF','CSV']" ng-click="myCtrl.getFile()" file-download>{{ format }}</button>
</div>
</div>
In this sample you can figure out how can create directive using scope, i create a sample to get all formats get all lists, get checked list, get download format, on directive.
About your codes, actually response is true, because when we use some functions like [$broadcast] or ... on app run all data already sets in our scopes. but remember in this case you don't need to use $broadcast that because our actions are in-time and we can get them when we click on a function.
hope helps you my friend
var app = angular.module('app', []);
app.controller('ctrl', function ($scope) {
var self = this;
self.lists = [
{ name: "A"},
{ name: "B"},
{ name: "C"},
{ name: "D"}
];
self.getFile = function () {
console.log('controller', "done");
}
});
app.directive('fileDownload', function ($filter) {
return {
restrict: 'A',
scope: {
fileDownload: "=",
list: "="
},
link: function (scope, elem) {
elem.on("click", function () {
var filter = $filter("filter")(scope.list, { checked: true });
console.log('from directive, all list', scope.list);
console.log('from directive, checked list', filter);
console.log('from directive, download format', scope.fileDownload);
});
}
}
});
<!DOCTYPE html>
<html ng-app="app" ng-controller="ctrl as self">
<head>
<title></title>
</head>
<body>
<small>list</small>
<ul>
<li ng-repeat="list in self.lists">
<input type="checkbox" ng-model="list.checked"/>
{{list.name}}
</li>
</ul>
<small>download format</small>
<button ng-repeat="format in ['TXT','PDF','CSV']" ng-click="self.getFile()" list="self.lists" file-download="format">{{ format }}</button>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
</body>
</html>
I think it will help you
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="demoApp">
<div ng-controller="MyCtrl as myCtrl">
<span ng-repeat="format in ['TXT','PDF','CSV']">
<span get-file="myCtrl.getFile({data: data})" file-download title="format"></span>
</span>
</div>
</div>
</body>
<script type="text/javascript">
var app = angular.module('demoApp', []);
app.controller('MyCtrl', function($scope) {
var myCtrl = this;
myCtrl.getFile = function(data) {
var response = 'some data';
console.log(response);
console.log(data);
// $scope.$broadcast('downloadFile', {'response': response});
}
});
app.directive('fileDownload', function() {
return {
restrict: 'A',
template: "<button ng-click='callFn()'>{{title}}</button>",
scope: {
title: '=',
getFile: '&'
},
link: function(scope, elem, attrs, ctrl) {
scope.callFn = function(){
scope.getFile({data: scope.title});
}
// var cancelEvent = scope.$on('downloadFile', function(event, args) {
// console.log('called', args);
// });
// scope.$on('$destroy', function() {
// cancelEvent();
// });
}
}
});
</script>
</html>

Angular service overrides itself when called more than once

I have a chat component that have a hash id of that chat. All my api call's (done by the service) have a hash value. When I call my chat component twice, the service hash value of the first chat gets overwritten by the seconds chat.
angular.module('testModule', [])
.controller('testController', function(testService, $scope) {
var vm = this;
vm.init = function() {
vm.hash = vm.hash();
testService.setHash(vm.hash);
}
vm.getServiceHash = function() {
vm.serviceHash = testService.hash;
}
vm.init();
})
.service('testService', function() {
var testService = {
hash: null
};
testService.setHash = function(hash) {
testService.hash = hash;
}
return testService;
})
.directive('test', function() {
return {
restrict: 'E',
template: $("#test\\.html").html(),
controller: 'testController',
controllerAs: 'test',
bindToController: {
hash: '&',
},
scope: {}
}
});
var app = angular.module('myApp', ['testModule']);
app.controller('myController', function($scope) {})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.min.js"></script>
<body>
<div ng-app="myApp" ng-controller="myController">
<test hash="'123'"></test>
<test hash="'321'"></test>
</div>
<script type="text/ng-template" id="test.html">
<p>
<h2> Controller hash: {{test.hash}} </h2>
<button type="button" ng-click="test.getServiceHash()">Get service hash</button>
<h2> Service hash: {{test.serviceHash }} </h2>
</p>
</script>
</body>
As #jjmontes noted in a comment, services are singletons in Angular. As such, they should not maintain any state unless that state applies to all consumers. For example, a common set of data that applies to all consumers can be retrieved once and then used by all rather than making a potentially expensive call again. However, any controller-specific data should be maintained in the controller and passed to the service on demand.
Specifically in your case, instead of setting the hash value on the service, you should pass it as a parameter to the service method that the controller instances call.
.service('testService', function() {
var testService = {
};
testService.callService = function(hash) {
// call an API or whatever, passing the hash
}
return testService;
})

Navigation bar in angular 1.5 app

Hello I'm learning Angular and I'm trying to do a global navigation bar across all angular (1.5) app.
So, to do that navigation bar I've created a directive
navigation.html
<nav class="navbar navbar-default" id="header">
...
non important tags
...
<li ng-if="!session.logged">Enter</li>
<li ng-if="session.logged"><a ng-click="logout()">Logout</a></li>
</nav>
navigation.js
export default function navigation(Auth, Session) {
return {
restrict: "E",
replace: true,
scope: {},
templateUrl: directivesPath + "navigation.html",
link: function(scope, element, attrs) {
scope.session = Session.sessionData();
scope.logout = function() {
Auth.logout();
scope.session = Session.sessionData();
window.location = "#/";
}
}
}
}
I've put than directive in an index.html like this
<!DOCTYPE html>
<html lang="es" ng-app="movieApp">
<head>
...
</head>
<body>
<navigation></navigation>
<div ui-view></div>
</body>
</html>
And I have controllers, for example these two
UsersCtrl.$inject = ["$scope", "$http", "Session"];
export function UsersCtrl($scope, $http, Session) {
$http.get('http://localhost:3000/users').success(function (data) {
console.log(data);
$scope.users = data.data;
$scope.session= Session.sessionData();
});
}
export default angular.module('movieControllers').controller("LoginCtrl", ["$scope", "$rootScope", "Auth", "Session",
function($scope, $rootScope, Auth, Session) {
console.log("User is logged? " + Auth.loggedIn());
if (Auth.loggedIn() === true) {
window.location = "#/users/" + Auth.currentUser.username;
}
const button = document.getElementById('login');
button.addEventListener("click", function() {
const username = document.login.username.value;
const password = document.login.password.value;
Auth.login({username: username, password: password}, function() {
$scope.session= Session.sessionData();
window.location = "#/users/" + username;
// $scope.$digest();
});
});
}]);
The Auth and Session services just make a call to the backend, keep the
user data and retrieve that user data.
The problem is that when I login, the app redirect to the show of the user, but
the nav still showing <li>Enter</li>, but if I refresh the browser the nav show
correctly the <li>Logout</li> ant the <li>Enter</li> is hidding.
If I put the navigation directive inside the template of a controller it
behaves correctly.
What I'm doing incorrectly?
(Sorry for my English, I'm still learning the language too)
Your directive does not watch for session changes. When you assign your session object in your link function you may wrap it in $apply call to inform about the changes, like that:
function navigation(Auth, Session) {
return {
restrict: "E",
replace: true,
scope: {},
templateUrl: directivesPath + "navigation.html",
link: function(scope, element, attrs) {
scope.$apply(function() {
scope.session = Session.sessionData();
});
scope.logout = function() {
Auth.logout();
scope.$apply(function() {
scope.session = Session.sessionData();
});
window.location = "#/";
}
}
}
}
Better yet, look how isolate scope in directives work (https://docs.angularjs.org/guide/directive) and check $watch function as well.

getting data in child directive from parent directive in Angular JS

I have a parent directive in which its controller makes a call through a service to get some data.
sharedService.getData(options).then(function(data){
$scope.data = data;
});
Now i need this data in my child controller.
What i have already tried are the ff:
1) Through $timeout i get the data after sometime but it doesn't seem a good solution impacting performance
2) watchCollection() - i watched if newValue !== oldValue
problem being the data is huge so it takes a toll of performance
Now the issue i'm getting is the child directive gets executed after parent BUT before the data comes back from the service and i'm not able to get that data in my child directive via $scope.data.
Is there any solution to get data from parent directive to child directive when i have to wait for data to come in parent?
You can include your parent directive controller in your child directive by using require.
angular.module('myApp', [])
.directive('dirParent', function() {
return {
restrict: 'E',
scope: {},
controller: ['$scope', function($scope) {
}],
};
})
.directive('dirChild', function() {
return {
require: '^dirParent', // include directive controller
restrict: 'E',
scope: {
},
link: function(scope, element, attrs, paretCtrl) {
var data = paretCtrl.getMyData();
}
};
})
It's always a best to use service for communication and and business logic. Here is an example. Please check. This might solve your problem.
// Code goes here
angular.module('app', [])
.factory('messageService', function() {
return {
message: null
}
})
.directive('parentDir', function() {
return {
scope: {}, //isolate
template: '<input type="text" ng-model="PDirInput"/><button ng-click="send()">Send</button>',
controller: function($scope, messageService) {
$scope.send = function() {
messageService.message = $scope.PDirInput;
}
}
}
})
.directive('childDir', function() {
return {
scope: {}, //isolate
template: '<code>{{CDirInput.message}}</code>',
controller: function($scope, messageService) {
$scope.CDirInput = messageService;
}
}
})
<!DOCTYPE html>
<html ng-app="app">
<head>
<script data-require="angular.js#*" data-semver="2.0.0-alpha.31" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body>
<HR/>Parent Directive
<parent-dir></parent-dir>
<br/>
<HR/>Child Directive
<child-dir></child-dir>
<HR/>
</body>
</html>

Passing method through a directive to a parent directive

edit: Modified the code as per stevuu's suggestion as well as added a plunkr to here
I'm currently attempting to have a child directive call a method(resolve) through another directive all the way up to a parent directive but I'm having difficulties identifying the problem with my approach.
The problem right now seems to be that although resolve() does get called as expected on click, selected remains undefined.
the html:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Angular: directive using & - jsFiddle demo</title>
<script type='text/javascript' src='//code.jquery.com/jquery-1.9.1.js'></script>
<link rel="stylesheet" type="text/css" href="/css/normalize.css">
<link rel="stylesheet" type="text/css" href="/css/result-light.css">
<script type='text/javascript' src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
<style type='text/css'>
</style>
</head>
<body ng-app="myApp">
<div grand-parent>
<span>selected: {{text}}</span>
<div parent resolve="resolve"></div>
</div>
</body>
</html>
And the js:
var myApp = angular.module('myApp',[]);
myApp.directive('grandParent', function() {
return {
scope:{
resolve: "&"
},
link: function($scope, $element) {
$scope.resolve = function(selected){
$scope.text = selected
}
}
};
});
myApp.directive('parent', function(){
return{
scope: {
resolve: "&"
},
template: "<div child resolve='resolve'></div>"
};
});
myApp.directive('child', function() {
return {
scope: {
resolve: "&"
},
template: "<div>Click me!</div>",
link: function($scope, $element) {
$element.on("click", function(){
$scope.$apply(function(){
$scope.resolve({selected: "Yahoo!!"});
});
});
}
};
});
resolve: "&" is a mapping. So here:
myApp.directive('grandParent', function() {
return {
scope:{
resolve: "&"
},
link: function($scope, $element) {
$scope.resolve = function(selected){
$scope.text = selected
}
}
};
});
you are trying to map "resolve" to ... nothing, because "grandParent" doesn't have any attr named "resolve".
If you want to share some staff betweens directives you should do something like that:
view
<div data-grand-parent resolve="resolved()">
<div data-parent ></div>
</div>
Directives
var app = angular.module('test');
app.directive('grandParent', function() {
return {
scope : {
resolve : "&" // in view we defined resolve attr
// with "resolve()" value, so now resolve=resolved()
// in grandParent scope too.
},
controller: function($scope){
this.getResolve = function(){
return $scope.resolve;
};
}
};
});
app.directive('parent', function() {
return {
require: "^grandParent",
link: function(scope, element, attr, grandParentCtrl){
grandParentCtrl.getResolve()();
},
template : ""
};
});
controller
angular.module('desktop')
.controller('MainCtrl', function ($scope, mocks) {
$scope.resolved = function(){
console.log("calling $scope.resolved() ...");
};
});
output
calling $scope.resolved() ...
So, how does it work?
We defined resolved function in our controller, then we sign this function to attr "resolve" in grandParent directive. Thx to resolve : "&" we could mapped that resolved() function to "resolve" property in grandParent scope. At the end we inject grandParent to other directives. That's all.
I recommend you to read angularJs by Brad Green, Shyam Seshadri. it's not the best book but could be worse and it's free. You can find very good tutorial too on http://www.egghead.io/
ps. Sorry for my english ;/

Categories