AngularJS call custom directive (A form) on ng-click - javascript

New to angular. I have a custom directive which basically is a form that I want to call when a click event happens when a track within an album is clicked. Something like this:
- album1
*album1-track1
*album1-track2
- album2
So when I click Album1, I will be displaying tracks for album1. When I click album2, I will display tracks of album2 and not for album1. Now, when I click on track of album 2, I want to display the form (which is defined as a custom directive). Something like this:
- album1
*album1-track1 ---> (this track clicked) -----> (form displayed)
*album1-track2
- album2
Here is my code:
// HTML
<div class="wrapper wrapper-content">
<div>
<ul>
<li ng-repeat="album in vm.albums">
<a ng-click="vm.getAlbumTracks(album, $index)">{{album}}</a>
<ul ng-show="$index === vm.showingAlbumIndex">
<li ng-repeat="track in vm.albums.tracks"><a ng-click="vm.displayForm(track)">{{track}}</a></li>
</ul>
</li>
</ul>
<metadata-form></metadata-form>
<a ng-click="vm.newFunction('RAVI')">Click Me!</a>
</div>
Controller:
(function (){
angular.module('app.fileUploadForm').controller('FileUploadFormController', FileUploadFormController);
FileUploadFormController.$inject = ['$http', '$log', '$scope', '$state', '$rootScope', 'APP_CONFIG'];
function FileUploadFormController ($http, $log, $scope, $state, $rootScope, APP_CONFIG){
var vm = this;
vm.albums = init;
vm.getAlbumTracks = getAlbumTracks;
vm.newFunction = newFunction;
vm.displayForm = displayForm;
return init();
function init(){
$http.get('http://localhost:8080/api/albums').then(function(responseData){
// Parse the json data here and display it in the UI
vm.albums = [];
for(var album in responseData.data)
$scope.vm.albums.push(album);
$log.debug(angular.toJson(responseData, true));
})
}
function getAlbumTracks(album, index){
$http.get('http://localhost:8080/api/albums/'+album).success(function(trackResponse){
//parse each album and get the track list
//alert('Function getAlbumTracks () called');
vm.showingAlbumIndex = index;
vm.albums.tracks = [];
for(var i = 0; i<trackResponse.tracks.length; i++)
vm.albums.tracks.push(trackResponse.tracks[i].title);
$log.debug(vm.albums.tracks);
})
}
function displayForm(track, index){
// display the form for that track only
}
function newFunction(name){
$log.debug("Hello " + name);
}
}
})();
Custom directive.js
(function (){
'use strict';
angular.module('app.fileUploadForm').directive('metadataForm', metadataForm);
function metadataForm(){
return {
restrict: 'EA',
templateUrl: 'app/fileUploadForm/metadata-form.html'
};
};
})();
metadata-form.html file:
<div ng-show="showForm">
<form name="uploadForm" ng-controller="FileUploadFormController as uploadCtrl">
<br>UPC:<br>
<input type="text" ng-model="UPC">
<br>
<br>Artist:<br>
<input type="text" ng-model="artist">
<br>
<br>Genre:<br>
<input type="text" ng-model="genre">
<br>
<br>Album:<br>
<input type="text" ng-model="album">
<br>
<br>Hold Date:<br>
<input type="text" ng-model="holddate">
<br>
<br>SomeField:<br>
<input type="text" ng-model="somefield">
</form>
<!-- trying the date picker here
<h4>Standard date-picker</h4>
<md-datepicker ng-model="myDate" md-placeholder="Enter date"></md-datepicker>
--><br>
<button class="btn btn-primary block m-b" ng-click="uploadFile()">Send to Ingestion</button>
Any idea how to achieve this ?

To begin with, I think your custom directive shouldn't use the same controller of your first code. Remove ngController directive in metadataForm.
Use data binding to get track information when the directive is called:
(function (){
'use strict';
angular.module('app.fileUploadForm').directive('metadataForm', metadataForm);
function metadataForm(){
return {
restrict: 'EA',
templateUrl: 'app/fileUploadForm/metadata-form.html',
scope: {}, // Isolated scope
bindToController: {
track: '='
}
controller: controllerFunc
controllerAs: 'vm'
};
function controllerFunc() {
var vm = this;
// Your track is accessible at vm.track
}
};
})();
You have to change the ng-model in metadataForm template to bind your data with data from your controller.
To make the metadataForm directive appear on click, you could use ng-ifdirective:
// HTML
<div class="wrapper wrapper-content">
<div>
<ul>
<li ng-repeat="album in vm.albums">
<a ng-click="vm.getAlbumTracks(album, $index)">{{album}}</a>
<ul ng-show="$index === vm.showingAlbumIndex">
<li ng-repeat="track in vm.albums.tracks"><a ng-click="vm.displayForm(track)">{{track}}</a></li>
</ul>
</li>
</ul>
<metadata-form ng-if="vm.isFormDisplayed" track="vm.displayedTrack"></metadata-form>
<a ng-click="vm.newFunction('RAVI')">Click Me!</a>
</div>
And in the controller you described:
function displayForm(track, index){
vm.isFormDisplayed = true;
vm.displayedTrack = track;
}

Related

Using directives in angular

I am quite new to angular.
I am trying to manage a list of contacts to be able to select one to display some details on the side. I was able to do it without directives but then I wanted to use directives and be able to show which one is selected in the list.
With directives I am able to display the list but when I select a contact it doesn't show details anymore so the select is not working and I can't get it to work as those directives are making me crazy.
Here is contact.html:
<li><a href selected="selected" ng-click='selectContact(contact)' ng-transclude></a></li>
// Code goes here
var app = angular.module("contactList", []);
app.service("peopleService", function($http) {
this.getPeople = function() {
return $http.get('people.json');
};
});
app.controller("peopleController", function($scope, peopleService) {
$scope.selectedContact = {};
$scope.selectedContactImage = ""
$scope.showDetail = false;
peopleService.getPeople().then(function(response) {
$scope.people = response.data.People;
});
$scope.selectContact = function(contact) {
$scope.selectedContact = contact;
if (contact.img) {
$scope.selectedContactImage = contact.img;
}
$scope.showDetail = true;
};
});
app.directive("contactList", function(){
return{
restrict: 'EA',
replace: true,
transclude: true,
template: "<div ng-transclude></div>",
controller: function(){
this.gotOpened = function(selectedContact){
contacts.forEach(function(contact){
if(selectedContact != contact){
contact.selected = false;
}
});
};
}
};
});
app.directive("contact", function(){
return {
restrict: 'EA',
replace: true,
transclude: true,
require: '^?contactList',
scope: {selected: '='},
templateUrl: 'contact.html',
link: function(scope, element, attrs, contactListController){
scope.selected = false;
scope.selectContact = function selectContact(){
scope.selected = !scope.selected;
contactListController.gotOpened(scope);
};
}
};
});
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css" />
<script data-require="angularjs#1.6.4" data-semver="1.6.4" src="https://code.angularjs.org/1.6.4/angular.min.js"></script>
<script src="script.js"></script>
</head>
<body ng-app="contactList">
<div ng-controller="peopleController">
<contact-list>
<contact class="contact" ng-repeat="contact in people">
{{contact.name}}
</contact>
</contact-list>
<div class="list">
<ul>
<li ng-repeat="p in people">
<a href ng-click='selectContact(p)'>{{p.name}}</a>
</li>
</ul>
</div>
<div class="detail" ng-show="showDetail">
<div class="detailimagewrapper">
<img class="detailimage" src="{{selectedContactImage}}">
</div>
<label>Rating</label>
<p>{{selectedContact.rating}}</p>
<label>Description</label>
<p>{{selectedContact.Description}}</p>
</div>
</div>
</body>
</html>
Looks like you are using transclude and replace here. I expect this is causing your anchor tag to be replaced by the content which your contact directive is wrapping. Try removing the ng-transclude attribute from the anchor tag and instead adding <ng-transclude></ng-transclude> within the anchor tag.
contact.html would become:
<li>
<a href selected="selected" ng-click='selectContact(contact)'>
<ng-transclude></ng-transclude>
</a>
</li>

Correct way to access the scope from a directive on multiple controllers

I have a directive named add-tags than I am planning to use in different places of the app, I am using it to add tags to an array then save from the main ctrl, then the user can edit the list when previewing from a different view/ctrl (modal), on main page I have:
<add-tags tags="tags"></add-tags>
and my directive is set up as follow:
'use strict';
angular.module('theApp')
.directive('addTags', function() {
return {
templateUrl: 'components/add-tags/add-tags.html',
//restrict: 'EA',
scope: {
tags:'='
},
link: function($scope, $element, attrs) {
} //link
};
});
From the edit controller, how can access the previous tags data? when I do,
<add-tags tags="workDetails.tags"></add-tags>
the entire data from is gone from the scope, but when I do:
<span ng-repeat="x in workDetails.tags">
<h1>{{x.name}}</h1>
</span>
I can see the list of tags, any help will be appreciated :)
I am adding the add-tags.html example:
<div ng-repeat="tag in tags" class="text-center new-item">
<div class="input-group">
<input type="text" name="" class="form-control" ng-model="tag.name">
<span class="input-group-btn">
<button
type="button"
class="btn btn-default"
ng-click="deleteTag($index)">
Delete
</button>
</span> <!-- /.input-group-btn -->
</div> <!-- /.input-group -->
</div>
<div class="form-group" ng-hide="tags.length == tags_max">
<input type="text" class="form-control" placeholder="Enter tag:" ng-model="tag">
</div>
<!-- /.form-group -->
<button
type="button"
class="btn btn-primary center-block"
ng-disabled="tags.length == tags_max"
ng-class="{ 'btn-danger': tags.length == tags_max}"
ng-click="addTag()">
Add tag
</button>
<hr>
There is nothing wrong with the code you've given. However - unless I am missing some information - is it possible you're assuming that your "workDetails" object should be available everywhere? The only way I could get your scenario to work is by adding a "workDetailsService" that holds the tag state.
Here's a working plunkr of the code bits you provided, but with a workDetailsService added to maintain state and some routing.
Basically I added a service to maintain the tags:
theApp.factory('workDetailsService', function() {
return {
tags: [{
name: "Tag 1"
}, {
name: "Tag 2"
}, {
name: "Tag 3"
}]
}
});
And injected that service into two directives, "listTags" and "editTags":
theApp.directive('editTags', ['workDetailsService', function(workDetailsService) {
return {
templateUrl: '/edit-tags.html',
link: function($scope, $element, attrs) {
$scope.tags_max = 4;
$scope.tags = workDetailsService.tags;
$scope.addTag = function() {
workDetailsService.tags.push({
name: $scope.tag
})
}
$scope.deleteTag = function(index) {
workDetailsService.tags.splice(index, 1)
}
}
};
}]);
theApp.directive('listTags', ['workDetailsService', function(workDetailsService) {
return {
templateUrl: '/list-tags.html',
link: function($scope, $element, attrs) {
$scope.tags = workDetailsService.tags;
}
};
}]);
This way you have your tags state in one place, and the directives wrap the tags up instead of controllers so you can reuse everywhere.

$rootScope to set active tab not working

would anyone be able to help me on this issue I am having?
I have a NavCtrl for manage my active tag, I was able to change the active tab when click on the menu item, however when I click on the link in the body views, it take me to the page I want to, but the active tag is not change.
//controller for nevigation menu
sp.controller('NavCtrl', ['$scope', 'auth', '$window','$rootScope', function ($scope, auth, $window,$rootScope) {
$scope.LogOut = auth.logOut;
$scope.isLoggedIn = auth.isLoggedIn;
$scope.tab = $rootScope.navTav;
$scope.toTop = function(){
$window.scrollTo(0, 0);
};
}]);
I try to use the $rootScope to set the navTab, but it's still not working
//setting root scope
sp.run(function($rootScope) {
$rootScope.navTav = 1;
})
ui-Router
.state('qaanswer', {
url: '/qa/{quesId}',
views: {
view50': {
templateUrl: './qa/qaanswer.html',
controller: 'QAAnswerCtrl'
},
'view60': {
templateUrl: './shareviews/activityupdates.html',
controller: 'HomeCtrl'
}
},
onEnter:['$rootScope',function($rootScope){
$rootScope.navTav = 5;
}]
Thank you so much for the help
Update HTML :
<body ng-controller="NavCtrl">
<!-- Desktop Menu -->
<div>
<div>
<ul>
<a href="#/home" ng-class="{active: navTab === 1}" ng-click="navTab = 1">
<li>Home</li>
</a>
<a href="#/qa" ng-class="{active: navTab === 2}" ng-click="navTab = 2">
<li>QA</li>
</a>
</ul>
</div>
</div>
<div>
<div>
<div class="row">
<div ui-view="view50"></div>
<div ui-view="view60"></div>
</div>
</div>
</div>
</body>
Working plunker
You can simplify your implementation and have no issues. Simply use the $rootScope variable directly in your template along side ng-class like so:
<body ng-controller="NavCtrl">
<a ng-class="{red: $root.navTab===0}" ui-sref="first">first</a>
<a ng-class="{red: $root.navTab===1}" ui-sref="second">second</a>
<div ui-view></div>
</body>
Then update $rootScope in your controllers.
.controller('NavCtrl', function($scope, $rootScope) {});
.controller('FirstCtrl', function($scope, $rootScope) {
$rootScope.navTab = 0;
});
.controller('SecondCtrl', function($scope, $rootScope) {
$rootScope.navTab = 1;
});
Your states get to stay relatively simple:
.state('first', {
url: '/first',
templateUrl: 'first.html',
controller: 'FirstCtrl'
})
.state('second', {
url: '/second',
templateUrl: 'second.html',
controller: 'SecondCtrl'
})
plunker
Ideally you would make a directive for such a task, and avoid using $rootScope. A simple way to do this is to broadcast a message whenever you land on a new page, then listen on that event in your tab directive and flip the correct tab as active.

Custom search filter in angularjs

I am using the inbuilt search filter of angular js like this-
<!--header starts-->
<input class="form-control fs-mini search" ng-model="search.name" type="text" placeholder="&#xe80a Search">
<!--header ends-->
<!--content area-->
<div ng-repeat="user in users | filter:search">
{{ user.name }}
{{user.l}}
{{user.time}}
</div>
<!--content area ends-->
Now i removed the header template codes and created the header directive.
<!--header starts-->
<div site-header>
</div>
<!--header ends-->
<!--content area-->
<div ng-repeat="user in users | filter:search">
{{ user.name }}
{{user.l}}
{{user.time}}
</div>
<!--content area ends-->
site-header.js:
'use strict';
angular.module('myApp')
.directive('siteHeader', function () {
return {
templateUrl: 'views/header-view.html',
scope: {
},
restrict: 'A',
controller: [ '$scope', '$rootScope', '$location', function($scope, $rootScope, $location) {
console.log($scope.data);
}],
link: function postLink(scope, element, attrs) {
}
};
});
header-view.html
<input class="form-control fs-mini search" ng-model="search.name" type="text" placeholder="&#xe80a Search">
All html templates is loading correctly. but the search filter is not working. I dont understand how to bind search.name to the directive in order to work. I tried like this-
<div site-header="search.name">
</div>
but how to access this data in directive and bind it to ng-model?
Change your directive definition:
app.directive('myDirective', function(){
return {
restrict: 'A',
template: [
'<input class="form-control fs-mini search" ng-model="search.name" type="text" placeholder="&#xe80a Search"/>'
].join(''),
scope: true
}
});
here is a demo:http://plnkr.co/edit/JNCjzs?p=preview
Directives get their own scopes in angular. Scopes prototypically inherit from their parent scopes, which can be confusing and takes some time and knowledge of js inheritance to get your head around. If you can't be bothered with that, a quick solution to your problem would be to use an object to store the search string and instantiate it in the parent scope, which will make it available in both scopes. I think all you need is to put the line:
`var search = {};`
in your controller.

Create a list of items built using angularjs

I would like to create a list of items built in Directive and share through controllers.
Here is my example in plunker: Example of my code
Here is my code in js:
var app = angular.module('app', []);
app.controller("BrunchesCtrl", function ($scope, $log) {
$scope.brunches = [];
$scope.addNew = function () {
$scope.brunches.push({ address: "" });
};
$scope.$watch('brunch.address', function(value) {
$scope.address = value;
$log.info($scope.address);
});
$scope.$watch(function() { return $scope.brunches.length; }, function() {
$scope.brunches = $scope.brunches.map(function (brunch) {
return brunch;
});
});
});
app.directive('brunchesView', function ($compile) {
return {
restrict: 'E',
templateUrl: 'brunch.html',
controller: 'BrunchesCtrl',
replace: true,
link: function (scope, element, attrs, controller) {
}
};
});
app.directive('businessSubmit', function ($log, $compile, formManagerService) {
return {
restrict: 'AE',
controller: 'BrunchesCtrl',
link: function (scope, element, attrs, controller) {
formManagerService.init();
var hasError;
element.on('click', function (e) {
e.preventDefault();
$log.info("brunches: \n");
$log.info(scope.brunches);
});
}
};
});
Here is an HTML:
<!DOCTYPE html>
<div class="controls">
<a class="btn btn-danger" id="addBrunch" data-ng-click="addNew()">
<i class="icon-plus-sign"></i>
add new...
</a>
</div>
</div>
<brunches-view class="well" data-ng-repeat="brunch in brunches">{{brunch}}</brunches-view>
<br/>
<p class="well">
JSON: {{brunches | json}}
</p>
<div class="control-group">
<div class="controls">
<a class="btn btn-primary" href="#" data-business-submit>
<i class="icon-save"></i>
Save...
</a>
</div>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0/angular.min.js"></script>
<script src="//netdna.bootstrapcdn.com/bootstrap/3.0.3/js/bootstrap.min.js"></script>
<script src="script.js"></script>
And here is the template:
<div class="fc-border-separate">
<div class="control-group">
<label class="control-label">Address</label>
<div class="controls">
<input type="text" class="span6 m-wrap"
name="Address" data-ng-model="address"
value="{{address}}" />
</div>
</div>
</div>
The final thing I want to save the whole data inside the BusinessSubmit directive.
Help please...
Several issues with your code.
First, your ng-model for the <input> is set to address, but the object you are wanting to bind it to a brunch object that has an address property. Important to realize that ng-repeat will create a child scope for every repeated item
<input data-ng-model="brunch.address"/>
Another issue is you are assigning the parent controller to a directive as well. Important to understand that controllers are singletons so if you use controller more than once , each is a separate instance. Therefore nesting the parent controller in a directive makes no sense.
DEMO- [ng-model] fix
If you want the data shared with other controllers you should set up a service that holds the brunches data by injecting it into whatever controllers will need access

Categories