Access to scope in angular directive with two data bindigs - javascript

good morning i have a small problem i have directive:
return {
restrict:'E',
scope: {
user: "="
},
template: '<b>{{userLogin}}</b>',
link:function(scope,elem,attrs){
},
controller: function ($scope) {
console.log($scope.user)//always undefindet
$scope.userLogin = $scope.user;
},
};
and i want to show my parameter "user" with scope in template i must use controller because i need download some data from server
I think problem is somewhere here(in directive):
scope: {
user: "=" //when i have this response "undefined"
user: "#" //when i have this response not show id only text
},
my HTML
<get-user-login user="{{post.user_id}}"></get-user-login>
i always getting: empty value or undefined in console.
How to fix that.

When using # you have to use interpolation as:
<get-user-login user="{{post.user_id}}"></get-user-login>
Note # is ONLY for text values
WHEREAS
When using = you do not need interpolation
<get-user-login user="post.user_id"></get-user-login>
Note = is only for passing objects (two way binded)

I built this demo for you and it works just fine :
<!DOCTYPE html>
<html lang="en" ng-app="app">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.2/angular.min.js"></script>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body ng-controller="ctrl">
<get-user-login user="post.user_id"></get-user-login >
<script>
angular.module("app",[])
angular.module("app").
controller("ctrl",function($scope){
$scope.post = {user_id:565};
}).
directive('getUserLogin', function() {
return {
restrict:'E',
scope: {
user: "="
},
template: '<b>{{userLogin}}</b>',
link:function(scope,elem,attrs){
},
controller: function ($scope) {
console.log($scope.user)//always undefindet
$scope.userLogin = $scope.user;
},
};
});
</script>
</body>
</html>

Are you sure that you're giving correct value to directive? Because in getPost you're binding it to $scope.onePost, so maybe correct way is:
<get-user-login user="{{onePost.user_id}}"></get-user-login>

Related

Angular directive attributes cause an error

I am trying write an angular directive to make a substring of an attribute that is passed in. Below is my code:
HTML:
<body ng-controller="MainCtrl">
<div><substring message="This is a test."></substring></div>
<div><substring message="So is this." ></substring></div>
</body>
Angular/Javascript:
var app = angular.module('myapp', []);
app.controller('MainCtrl', function($scope) {
});
app.directive('substring', function () {
return {
restrict: 'AE',
replace: true,
scope: { text: '=message' },
link: function (scope, elem, attrs) {
//alert(attrs.message);
var str = attrs.message;
scope.text = str.substring(1, 4);
},
template: '<H1>{{text}}</H1>'
};
});
When I try running this I get the following error:
HTML1300: Navigation occurred.
File: directive.html
Error: [$parse:syntax] Syntax Error: Token 'is' is an unexpected token at column 6 of the expression [This is a test.] starting at [is a test.].
Also, I have tried changing
'=message' to '#message'
but that just causes the substring stuff I am doing in the link function to get ignored.
Why the error? Is Angular not seeing that stuff in the quotation marks as a string and instead trying to parse out some command? Most importantly, how do I fix this?
Thanks
Simply use # for text binding and the rest of your code works perfectly.
Working example:
var app = angular.module('myapp', []);
app.controller('MainCtrl', function($scope) {
});
app.directive('substring', function() {
return {
restrict: 'AE',
replace: true,
scope: {
message: '#'
},
link: function(scope, elem, attrs) {
//alert(attrs.message);
var str = attrs.message;
scope.text = str.substring(1, 4);
},
template: '<H1>{{text}}</H1>'
};
});
<!DOCTYPE html>
<html ng-app="myapp">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.min.js"></script>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body ng-controller="MainCtrl">
<div>
<substring message="This is a test."></substring>
</div>
<div>
<substring message="So is this."></substring>
</div>
</body>
</html>

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>

Angular translate not working between 2 controllers

I would like to use i18n and i10n in my Angular app.
I read that Angular-translate can help with this, however, it doesn't work for me.
In my index.html:
<!DOCTYPE html>
<html ng-app="eApp">
<head>
<meta charset="utf-8" />
<link rel="stylesheet" type="text/css" href="../common/css/bootstrap.css" />
<link rel="stylesheet" type="text/css" href="../common/css/style.css" />
<title></title>
</head>
<body ng-controller="AppCtrl">
<div id="container" ng-view></div>
<!--- Libs Js files --->
<script type="text/javascript" src="../vendor/angularjs/angular.min.js"></script>
<script type="text/javascript" src="../vendor/angularjs/angular-route.min.js"></script>
<script type="text/javascript" src="../vendor/angularjs/angular-translate.min.js"></script>
</body>
</html>
In my eApp.js:
var eApp = angular.module('elbitApp', ['ngRoute', 'ui.bootstrap', 'config', 'pascalprecht.translate']);
// configure our routes
eApp.config(["$routeProvider",
function ($routeProvider) {
$routeProvider
// route for the home page
.when('/c', {
templateUrl: 'c/partials/c.html',
controller: 'CController'
})
// route for the about page
.when('/de', {
templateUrl: 'd/partials/dE.html',
controller: 'DEController',
resolve: {
data: function (DDataService) {
return DDataService.loadData().then(function (response) {
return response.data;
});
}
}
})
// route for the contact page
.when('/di', {
templateUrl: 'd/partials/di.html',
controller: 'DIController',
resolve: {
data: function (DDataService) {
return DDataService.loadData().then(function (response) {
return response.data;
});
}
}
}).otherwise({
redirectTo: '/di'
});
}]).config(["$httpProvider",
function ($httpProvider) {
// Configure the $httpProvider to parse date with date transformer
$httpProvider.defaults.transformResponse.push(function (responseData) {
convertDateStringsToDates(responseData);
return responseData;
});
}]).config(["$translateProvider",
function ($translateProvider) {
$translateProvider.translations('en', {
"TITLE": 'Hello',
"FOO": 'This is a paragraph.',
"BUTTON_LANG_EN": 'english',
"BUTTON_LANG_DE": 'german'
});
$translateProvider.translations('de', {
"TITLE": 'Hallo',
"FOO": 'Dies ist ein Paragraph.',
"BUTTON_LANG_EN": 'englisch',
"BUTTON_LANG_DE": 'deutsch'
});
$translateProvider.preferredLanguage('en');
}]);
// main controller that catches resolving issues of the other controllers
eApp.controller('AppCtrl', function ($rootScope) {
$rootScope.$on("$routeChangeError", function (event, current, previous, rejection) {
alert("Cant resolve the request of the controller "); //TODO: add URL + additional data.
})
});
In this file I defined my app and added the $translateProvider and two dictionaries.
Afterwards I got to my deController.js:
eApp.controller('DispatcherEventsController', ['$scope', '$route', '$translate',
function($scope, $route, $translate){
var data = $route.current.locals.data;
$scope.title = $translate.instant("FOO");
$scope.switchLanguage = function(languageKey){
$translate.use(languageKey);
};
}]);
In de.html I added a h1 tag with FOO and in a click I would like to change to German:
<h1>{{title |translate}}</h1>
<h1 translate="{{title}}"></h1>
<button type="button" id="searchButton" class="btn btn-default" ng-click="switchLanguage('de')">German</button>
I don't get any problem, but nothing happens. I expected that the English title will be converted to German title.
What can I do to make this work?
It works well for me. Here is a jsFiddle DEMO.
In this case, you want to bind $scope.title with translation key "FOO".
You should change the value of $scope.title dynamically in the switchLanguage function. Then view will be updated accordingly.
//default value
$scope.title = $translate.instant("HEADLINE");
$scope.switchLanguage = function(key){
$translate.use(key);
$scope.title = $translate.instant("HEADLINE");
}
In my opinion, maybe use translation key is a better way than scope data binding. You don't have to maitain the value of key manually.
<h1>{{'FOO' | translate}}</h1>
According to the error msg you provided, maybe you could check if there is any typo syntax in your controller.
Should be
$translate.use(languageKey)
Not
$translate.uses(languageKey)
Though not directly related to your question - you must remember to set the preferred language in the translations.js file, or whatever its name is that you define your key-value translations. Otherwise it will just default to whatever the key is.
...
GREETING: 'Hello World!',
...
$translateProvider.preferredLanguage('en');
Without this line, when doing this:
<h2>{{'GREETING'|translate}}</h2>
Would appear as just GREETING instead of the 'Hello World.'

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 ;/

Supplying data to an Angular directive (from file or database)

I'm trying to determine the right way or the Angular way to provide data to directives for construction of the DOM. As my example, I'm trying to get JSON data from a file to construct a tree with jsTree. The problem is constructing the tree when the data has arrived and not beforehand.
I've solved the problem by $watching treeData in my controller. If I don't $watch it, the tree will be constructed with an empty array as input, and later, Angular will update the reference to populate the data. But I feel like this is the wrong way to do it since the data isn't going to change after the tree is initially constructed.
Here is my code
app.js:
var app = angular.module('jsTreeApp', ['ngResource']);
var TestCtrl = function($scope, Data) {
$scope.treeData = Data.getTreeData();
}
app.directive('jstree', function() {
return function(scope, element) {
scope.$watch('treeData.data', function() {
$(element).jstree({
"json_data" : scope.treeData,
"plugins" : [ "themes", "json_data" ]
});
})
}
});
app.factory('Data', function($resource) {
return $resource('/data/treeData.json', {}, {
getTreeData: { method: 'GET', isArray: false }
})
})
index.html:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>jsTreeAngular</title>
</head>
<body>
<div ng-controller="TestCtrl" ng-app="jsTreeApp">
<div jstree></div>
</div>
<script src="lib/angular/angular.js"></script>
<script src="lib/angular/angular-resource.js"></script>
<script src="js/app.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
<script src="lib/jstree/jquery.jstree.js"></script>
</body>
</html>
Any suggestions for the correct way to do this?
I don't see a problem with using the $watch, because you bind it, you need some way to see if the data is populated or not and to act upon it.
The problem that I see here is that it not obvious where the "treeData" is coming from, and that might confuse other developers.
What I would do instead is to define an isolated scope and to pass an attribute with the treeData, that way you can keep it loosely coupled and don't have to worry about changing the name of "treeData" in the controller.
you can define isolated scope like this:
app.directive('jstree', function() {
return {
scope: {
treeData: "="
},
link: function(scope, element) {
scope.$watch('treeData.data', function() {
$(element).jstree({
"json_data" : scope.treeData,
"plugins" : [ "themes", "json_data" ]
});
})
}
}
});

Categories