Angularjs : accessing service methods from directive's link function - angularjs

i have a directive, and in its link function i want to access methods from a service. My code for directive is
AppDirectives.directive('feed',['FeedService',function() {
return {
restrict : 'AE',
scope : {
feedLike: '&',
feedItem : '=',
feedDislike :'&',
feedsArray :'=',
},
templateUrl :'resources/views/templates/feedTemplate.html',
link : function(scope,element,feedService){
console.debug("linking now");
scope.likeComment = function(commentUid){
console.debug("comment liked :"+commentUid);
};
scope.addComment = function(referenceFeedUid){
console.debug("commentText : "+scope.commentText);
var comment = {
user : "guest",
feedText : scope.commentText
};
feedService.addComment(comment,referenceFeedUid).then(function(response){
console.debug("response ; "+response);
// $scope.feeds.unshift(response);
});
};
},
replace : true,
};
}]);
and my service code is
.factory('FeedService',function($http){
return {
postFeed : function (feed){
/*$http.post('/feed/add',feed).success(function(response){
console.debug("added "+response);
}).error(function(){
console.debug("error adding feed");
});*/
return $http.post('/feed/add',feed).then(function(response){
return response.data;
});
},
getFeeds : function(){
return $http.get('/feed/get');
},
likeFeed : function(feedUid){
return $http.get('/feed/'.concat(feedUid).concat('/like')).then(function(response){
return response.data;
});
},
dislikeFeed : function(feedUid){
return $http.get('/feed/'.concat(feedUid).concat('/dislike')).then(function(response){
return response.data;
});
},
addComment : function (comment,referenceUid){
var targetUrl = '/feed/'.concat(referenceUid).concat('/comment');
return $http.post(targetUrl,comment).then(function(response){
return response.data;
});
},
};
});
when i call the add comment from directive's link, i am getting following error on firebug console.
TypeError: Object #<Object> has no method 'addComment'
at Object.scope.addComment (http://localhost:8080/feed/resources/js/directives.js:53:21)
at http://localhost:8080/feed/resources/js/lib/angular/angular.js:6193:19
at http://localhost:8080/feed/resources/js/lib/angular/angular.js:12684:13
at Object.Scope.$eval (http://localhost:8080/feed/resources/js/lib/angular/angular.js:7840:28)
at Object.Scope.$apply (http://localhost:8080/feed/resources/js/lib/angular/angular.js:7920:23)
at HTMLButtonElement.<anonymous> (http://localhost:8080/feed/resources/js/lib/angular/angular.js:12683:17)
at http://localhost:8080/feed/resources/js/lib/angular/angular.js:1926:10
at Array.forEach (native)
at forEach (http://localhost:8080/feed/resources/js/lib/angular/angular.js:110:11)
at HTMLButtonElement.eventHandler (http://localhost:8080/feed/resources/js/lib/angular/angular.js:1925:5)
here is my directive template
<ul class="media-list">
<li class="media">
<a class="pull-left" href="#"><img class="media-object" src="resources/images/holder.png" style="height:64px; width:64px;" alt="img"></a>
<div class="media-body">
<span><h4 class="media-heading">{{feedItem.userUid}}</h4>{{ feedItem.time | date:medium }}</span>
<h5>{{feedItem.feedText}}</h5><h3></h3>
<p> <a ng-click="feedLike(feedItem.feedLike)">Like </a> {{feedItem.like}}
<a ng-click="feedDislike(feedItem.feeddisLike)">Dislike</a> {{feedItem.dislike}}
</p>
<div ng-repeat = "comment in (feedsArray | filter:{referenceGroupId:feedItem.uid})">
<div class="media">
<a class="pull-left" href="#"><img class="media-object" src="resources/images/holder.png" style="height:64px; width:64px;" alt="img"></a>
<div class="media-body">
<span><h4 class="media-heading">{{comment.userUid}}</h4>{{ comment.time | date:medium }}</span>
<h5>{{comment.feedText}}</h5><h3></h3>
<p> <a ng-click="likeComment(comment.uid)">Like </a> {{comment.like}}
<a ng-click="commentDislike(comment.uid)">Dislike</a>{{comment.dislike}}
</p>
</div>
</div><br/>
</div>
<div>
<input type="text" id="commentBox" ng-model="commentText"/>
<button class="btn btn-success" ng-click="addComment(feedUid)">Comment</button>
</div>
</div>
</li>
</ul>
what i am trying to do is that i want to access the addCommennt method from service. how can i fix it or is there any way that i can acess the service methods from inside the directive link function.
Thanks in advance.
regards,

Instead of declaring the service in the link function, declare it when you are defining the directive:
So here:
AppDirectives.directive('feed',['FeedService',function(feedService) {
Then the feedService will be available to call inside the link function. The link function parameters are specifically defined as scope, element, attrs, ctrl so there is no straight dependency injection happening there (AFAIK).

Related

Setting $location and then filtering with AngularJS

For the purposes of navigation, I'm trying to have a button that triggers a function.
This function must first:
-Redirect to #/
And then:
-Filter an ng-repeat that exists in this view.
This is what I've tried but no luck.
<li><a ng-click="shopsingle()">Shop</a></li>
And JS:
$scope.shopsingle = function(){
$location.path("/");
setTimeout(function () {
$scope.$apply(function(){
$scope.filterExpr = {"shop" : true};
console.log($scope.filterExpr);
});
}, 300);
};
It does redirect to the desired path, where a full ng-repeat list appears, but it won't apply the filter, and console also comes empty.
About the views and routing, I'm using the same template for 2 different views, and showing/hidding parts of if while watching the $routeParams:
.config(function($routeProvider) {
$routeProvider
.when("/", {
templateUrl : "home.htm",
controller : "mainCtrl"
})
.when('/:name',
{
templateUrl:"home.htm",
controller:"mainCtrl"
})
.otherwise({redirectTo:'/'});
})
And the controller watch:
$scope.name = $routeParams.name;
$scope.$watch(function() { return $scope.name; }, function(newVal) {
if (!newVal){
$scope.single = false;
} else{
$scope.single = true
};
$scope.newname = newVal;
$scope.chosenexhibitor = $filter("filter")($scope.images, {'name':$scope.newname}, true);
}, true);
And the view:
<!-- MAIN PAGE -->
<div ng-hide="single" id="tiles" masonry='{ "transitionDuration" : "0.15s" , "itemSelector" : ".tile"}'>
<div masonry-tile ng-repeat="item in images | filter:filterExpr" class="tile col-xs-3 col-md-3">
<a href="#/{{item.name}}">
<img class="img-fluid" ng-src="img/{{item.link}}">
</a>
</div>
</div>
<!-- SINGLE PAGE -->
<div class="row flexing" ng-show="single">
<div class="col-md-12">
<h1>{{chosenexhibitor[0].name}}</h1>
<img ng-src="img/{{chosenexhibitor[0].link}}">
<a class="back" href="#/">Back</a>
</div>
</div>
What am I missing?

Separation components and codereview in AngularJS

I started read about modules, components, directives. I would like to ask you to check the code and say how I could improve it. The main thing for me is to figure out how I can improve the transparency of this code. I would also like to learn how to split this code into files, and how it should look like the file structure in this case. I heard that I should make one file for components, But how should it look in the inside?
I would really be grateful for help.
because the most important in the life of the programmer is the code review! :)
(function () {
angular.module('app.navbar', [])
.component('navbar', {
bindings: {
user: '<'
},
controller: function ($scope) {
var navbar = this;
this.$onInit = function () {
navbar.toggle = false;
};
this.activeMenu = function (name, $event) {
this.blockClosingList($event);
if (navbar.toggle === true && $scope.name == name) {
navbar.toggle = !navbar.toggle;
}
else if (navbar.toggle === false) {
navbar.toggle = !navbar.toggle;
}
$scope.name = name;
}
this.blockClosingList = function ($event) {
$event.stopPropagation();
}
},
controllerAs: 'navbar',
template: `
/////// MENU LEFT SIDE ///////
<div class="main-navbar">
<div class="menu-left">
<div class="btns">
<span class="glyphicon glyphicon-align-left btn__glyph"></span>
<span class="btn--name">Tablice</span>
</div>
<div class="btns navbar__search">
<span class="glyphicon glyphicon-search"></span>
</div>
</div>
<div class="navbar__logo">
<span class="navbar--logo">Tasker</span>
</div>
/////// MENU RIGHT SIDE ///////
<div class="menu-right">
<a href="#" ng-click="navbar.activeMenu('Create', $event);">
<div class="btns">
<span class="glyphicon glyphicon-plus"></span>
</div>
</a>
<!-- MENU CREATE -->
<div class="menu menu--create" ng-click="navbar.blockClosingList($event)" ng-class="{active : name === 'Create' && navbar.toggle === true}">
<!--close-->
<menu-create></menu-create>
</div>
<!---->
<!--MENU CREATE BOARD-->
<div class="menu cb__menu-coordinate menu--create-board" ng-click="navbar.blockClosingList($event)" ng-class="{active : name === 'menuCreateBoard' && navbar.toggle === true}">
<span class="settings-menu__header-title">Utwórz Tablicę</span>
<div class="menu__wrapper--create-board center-block">
<menu-create-board></menu-create-board>
</div>
</div>
<!---->
<a href="#" ng-click="navbar.activeMenu('Notice', $event);">
<div class="btns">
<span class="glyphicon glyphicon-bell"></span>
</div>
</a>
<!--NOTIFICATIONS-->
<div class="menu menu--notice" ng-click="navbar.blockClosingList($event)" ng-class="{active : name === 'Notice' && navbar.toggle === true}">
<notifications></notifications>
</div>
<!---->
<a href="#" ng-click="navbar.activeMenu('Profile', $event);">
<div class="btn__circle">
<span class="btn--circle">B</span>
</div>
</a>
<!--MENU PROFILE -->
<div class="menu menu--avatar" ng-click="navbar.blockClosingList($event)" ng-class="{active : name === 'Profile' && navbar.toggle === true}">
<profile-menu></profile-menu>
</div>
</div>
</div>
<!---->
`
})
.component('profileMenu', {
require: {
parent: '^navbar'
},
template: `
<!--close-->
<span class="settings-menu__header-title">
{{$ctrl.parent.user.username}} {{$ctrl.parent.user.role}}
</span>
<ul class="menu__avatar--list">
<li>
Profil
</li>
<li>
<a href="/{{$ctrl.parent.user.username}}">
Karty</a>
</li>
<li>
<a href="/{{$ctrl.parent.user.username}}">
Ustawienia</a>
</li>
<li>
<a href="/logout">
Wyloguj</a>
</li>
</ul>
`
})
.component('notifications', {
require: {
parent: '^navbar'
},
template: `
<!--close-->
<span class="settings-menu__header-title">Powiadomienia</span>
<div class="menu__field--placeholder">
<span>Brak powiadomień</span>
</div>
`
})
.component('menuCreateBoard', {
require: {
parent: '^navbar'
},
template: `
<form>
<div class="form-group">
<label for="">Tytuł</label>
<input type="text" class="form-control">
</div>
<div class="form-group">
<label for="">Zespół</label>
<input type="text" class="form-control">
</div>
<span class="bottom--create-board">Ta tablica będzie Prywatna. Zmień</span>
<button class="btn btn-success">Utwórz</button>
</form>
`
})
.component('menuCreate', {
require: {
parent: '^navbar'
},
template: `
<span class="settings-menu__header-title">Utwórz</span>
<ul class="menu__create--list">
<li>
<a href="#" ng-click="$ctrl.parent.activeMenu('menuCreateBoard', $event);">
<div class="menu__wrapper-create">
<span class="menu-create__heading">Utwórz tablicę</span>
<span class="menu-create__desc">Tablica składa się z kart uporządkowanych w listach. Użyj jej do zarządzania projektami, śledzenia informacji i organizowania wszystkiego.</span>
</div>
</a>
</li>
<li>
<a href="#">
<div class="menu__wrapper-create">
<span class="menu-create__heading">Utwórz zespół</span>
<span class="menu-create__desc">Zespół składa się z ludzi i tablic. Organizuj z jego pomocą firmę, swoją drugą pracę, plany dla rodziny i spotkania z przyjaciółmi.</span>
</div>
</a>
</li>
</ul>
`
})
})();
https://gist.github.com/Turqus/2a791c6b86adfc8b6732711eaec2e23d
in main app:
var App = angular.module('TodoListApp', ['dndLists', 'app.navbar']);
First I would advise you to go here and learn how to use webpack:
https://webpack.github.io/
After you learn how to bundle your files, you should separate templates out of your js files and use templateUrl to load template - you can webpack template loader - works very good.
Then to improve it even further you can learn to use javascript export (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export) functionality and separate your code in files.
I like the following directory structure (for example you could use navbar instead of my-component-name):
/my-component-name - directory
my-component-name.component.js - contains my component bindings and templateUrls, also here we import controller for that particular component.
my-component-name.controller.js - sets up controller for component
my-component-name.html - template
my-component-name.scss/less - less/sass file containing isolated styles for that particular component - wrapped in my-component-name {...}
index.js - contains all code put together declaration of angular module, all imports (component,services, directives) and dependencies.
index.js example (you can name it navbar-module.js):
import NavbarComponent from './navbar.component'; // <- here you import component
const NavbarModule = angular
.module('navbar', [])
.component('navbar', NavbarComponent) // <- here you define your component
.name;
export default NavbarModule;
navbar.component.js example:
import templateUrl from './navbar.html';
import controller from './navbar.controller.js'
const NavbarComponent = {
bindings: {
user: '<'
},
templateUrl, // just a shorthand for writing templateUrl: templateUrl
controller, // same as above
}
export default NavbarComponent;
and finally navbar.controller.js:
class NavbarController {
constructor($scope, $timeout) {
this.$scope = $scope;
this.$timeout = $timeout;
}
$onInit() {
// set up your on init code here
this.toggle = true;
}
// set up your functions on controller - they will behave same and you will be able to access them via $ctrl.activeMenu() or $ctrl.blockClosingList() in your templates
activeMenu (name, $event) {
this.blockClosingList($event);
if (this.toggle === true && this.$scope.name == name) {
this.toggle = !this.toggle;
}
else if (this.toggle === false) {
this.toggle = !this.toggle;
}
this.$scope.name = name;
}
blockClosingList($event) {
$event.stopPropagation();
}
}
NavbarController.$inject = ['$scope', '$timeout']; // inject here and put $scope parameter in your constructor
export default NavbarController;
navbar.html - just use your html code it should work fine.
If you need to create some inner functions and keep access to this (controller) you can use arrow functions instead of function (param) {...} just use (param)=>{...} and inside brackets you will have scope of your controller.
One more thing - isolate your components as much as you can and try to communicate to parent components via $emit/$broadcasts instead of requiring parent component like in your example.
Keep in mind what I wrote above is in no way tested and most likely has bugs.
De facto best read about component based architecture in AngularJS can be found here, Todd Motto's styleguide:
https://github.com/toddmotto/angularjs-styleguide

Delete json object from directive

hello I want to delete json in angularjs
for the first time I only use ng-repeat directive
<div ng-repeat="artworkItem in artworksItems | filter: {category:'artworks'}| filter:query" class="">
<p>{{artworkItem.name}}</p>
<button ng-click="remove($index)">delete</button>
</div>
controller
ItemFactory.get().then(function(data) {
$scope.artworksItems = data;
});
$scope.remove= function(index){
$scope.artworksItems.splice(index, 1);
}
it works. Then i try to move it with directive.
so my code will be like this
<div ng-repeat="artworkItem in artworksItems | filter: {category:'artworks'}| filter:query" class="">
<grid-artworks data="artworkItem"></grid-artworks>
</div>
directive.html
<div>
<div class=" col-xs-6 col-sm-4 col-md-3 productThumbnail">
<a href="#/Artworks/{{data.id}}" class="">
<img ng-src="{{data.imgUrl}}" alt="{{data.name}}" class="img-responsive">
</a>
<div class="caption">
<p class="title text-center">{{data.name}}</p>
<p class="text-center">{{data.priceTotal}} {{data.curency}}</p>
<button ng-click="remove($index)">d</button>
</div>
</div>
</div>
directive.js
angular
.module('app.directives.gridViewArtworks',[])
.directive('gridArtworks',function()
{
return{
restrict:'E',
scope:{
data: '='
},
transclude:true,
replace:true,
templateUrl:"templates/directives/gridViewArtworks.html",
controller:function($scope){
console.log($scope.data);
}
};
}
);
controller
ItemFactory.get().then(function(data) {
$scope.artworksItems = data;
});
$scope.remove= function(index){
$scope.artworksItems.splice(index, 1);
}
with directive I can't delete the item. Please help me why can't I delete the data.
Pass a callback to your directive from the controller, which will be triggered from removing the element from the array.
scope:{
data: '=',
onRemove: '&'
},
Then when you call the directive:
<grid-artworks data="artworkItem" on-remove="remove(id)"></grid-artworks>
And inside your directive:
<button ng-click="onRemove({id: data.id})">d</button>
And change your remove function in the controller in order to use the id for removing elements from the array, because it's safer than the $index:
$scope.remove= function(id){
$scope.artworksItems.splice($scope.artworksItems.findIndex(el => el.id === id), 1);
}
You could pass index as an attribute.
<grid-artworks data="artworkItem" index="{{$index}}"></grid-artworks>
You'll need to add it to your directive's scope.
scope: {
index: '#',
// ...
And then you can use it.
<button ng-click="remove(index)">d</button>
Alternatively, you should be able to do remove(data):
var index = $scope.artworksItems.indexOf(data);
$scope.artworksItems.splice(index, 1);

angularJS parent scope not updating from directive

So here is my issue, I can't get my $scope.documents to update from my directive.$scope.parentUpdate({ documents: $scope.docType}) does not seem to execute at all an so my documents never updates. $scope.docType= resultgets all the data I need but it just does not push it back to the parent controller.
app.controller('docGridController',['$scope','getSideNav', 'getDocuments',
function($scope, getSideNav, getDocuments){
getSideNav().then(function(result){$scope.SideNav = result;
},
function(error){$scope.error = result;});
$scope.slideToggle = true;
$scope.documents=[];
$scope.update = function(k){
$scope.documents = k;
consle.log($scope.documents);
}}]);
app.directive('foSidenav',['getDocuments',function(getDocuments){
return{
replace: true,
scope: {
info:'=',
docType:'=',
parentUpdate:'&'
},
templateUrl:function(element,attr){
return attr.url;
},
controller: ['$scope','$element', '$attrs' ,'getDocuments',
function($scope,$element, $attrs, getDocuments){
$scope.selectDocType = function(id)
{
getDocuments(id).then(function(result){$scope.docType= result;
console.log($scope.docType);
alert('Printed results');
$scope.parentUpdate({ documents: $scope.docType});
},
function(error){$scope.error = result;});
};
}]
};
}]);
Here is the tag I am using in my template
<ul class="side-nav">
<li class="mainCat" ng-repeat=" item in info">
<a href="#" id="CatHeader" ng-click="slideToggle =! slideToggle">
<i class="{{item.displayIcon}} left-bar"></i>
<span ng-bind-html="item.label | html"></span>
</a>
<ul class="subCat slide-toggle" ng-show="slideToggle">
<li ng-repeat="subItem in item.docTypes">
<a href="#" ng-click="selectDocType(subItem.id)" >
<i class="fi-folder"></i>
<span ng-bind-html="subItem.Name | html"></span>
</a>
</li>
</ul>
</li>
and here is the directive call
<fo-sidenav url="{!URLFOR($Resource.FOPS_Resource, 'components/sidenav.html')}" info='SideNav' docType='documents' parentUpdate="update(documents)"></fo-sidenav>
Any ideas?? these scope are really throwing me off
I think the issue is with how you are using the variables from your isolated scope in the html. Now you are using them like below:
<fo-sidenav url="{!URLFOR($Resource.FOPS_Resource, 'components/sidenav.html')}"info='SideNav' docType='documents' parentUpdate="update(documents)"></fo-sidenav>
Try like the following:
<fo-sidenav url="{!URLFOR($Resource.FOPS_Resource, 'components/sidenav.html')}" info='SideNav' doc-type='documents' parent-update="update(documents)"></fo-sidenav>
For more information about read Matching Directives and Normalization sections here

ng-click not calling the function

I am learning AngularJs and was trying to run a custom directive. I have a button and on click of the button, I need to handle to event inside my controller. I am not getting the function call on ng-click directive. I am attaching the plnkr link: Link to plnkr
// Code goes here
angular.module("app", []);
angular.module('app').controller('mainCtrl', function($scope){
$scope.developer={
name: "Pradeep Kumar L",
age: 32,
city: "Bengaluru",
friends:[
"Praveen",
"Kori",
"Kiran"
]
}
$scope.handleClick = function(){
developer.rank="friend";
console.log("button clicked..");
}
});
angular.module('app').directive('mySimpleDirective', function(){
return{
restrict: "E",
templateUrl: 'userInfoCard.html'
}
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div class="panel panel-primary">
<div class="panel-heading">
<h4>{{developer.name}}</h4>
</div>
<div class="panel-body">
<span ng-show='!!developer.age'><h4>User age: {{developer.age}}</h4></span>
<h4>User city: {{developer.city}}</h4>
<h4>Friends</h4>
<ul>
<li ng-repeat="friend in developer.friends">
{{friend}}
</li>
</ul>
<div ng-show="!developer.rank">
Rank: {{developer.rank}}
</div>
<div ng-show="!developer.rank">
<button class="btn btn-success" ng-click="handleClick(developer)">Click Me</button>
</div>
</div>
</div>
The function is triggered but you have a syntax error inside it :
$scope.handleClick = function(){
$scope.developer.rank="friend";
console.log("button clicked..");
};
or
Usually, people use the following way when they have to deal with a list of developers, and the function would have been inside a ng-repeat.
$scope.handleClick = function(developer){ //as far as you pass it as argument
developer.rank="friend";
console.log("button clicked..");
};
http://plnkr.co/edit/vsRr9CfXxK0HQ7XS5GFe?p=preview
if you switch on console in your plunker you will see an error:
ReferenceError: developer is not defined
at Scope.$scope.handleClick (script.js:18)
at $parseFunctionCall (angular.js:12456)
at callback (angular.js:21692)
at Scope.$eval (angular.js:14555)
at Scope.$apply (angular.js:14654)
at HTMLButtonElement.<anonymous> (angular.js:21697)
at HTMLButtonElement.n.event.dispatch (jquery-2.1.4.min.js:3)
at HTMLButtonElement.r.handle (jquery-2.1.4.min.js:3)
you should add or argument on function or $scope to line script.js:18
When clicking the button, you are passing in developer to the handleClick function:
<button class="btn btn-success" ng-click="handleClick(developer)">Click Me</button>
However, in your javascript, your handleClick function does not have developer as an argument:
$scope.handleClick = function () { // developer is not passed in...
developer.rank="friend"; // ...so it will be undefined here
console.log("button clicked..");
}
To fix this, your function should be:
$scope.handleClick = function (developer) { // developer can be passed in...
developer.rank="friend"; // ...so this WILL work!!
console.log("button clicked..");
}

Resources