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
Related
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.
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;
}
I hava a directive already(appView) and that has some html to load through templateUrl. As of now, I need to add one more custom directive to the template that is being used by another directive(appView).
Please see my code below and it is not working as expected. Any help on this how i can make this work please?
View.html (template)
<div>
<div class="dragdrop" id="dropzone" dropzone> //here is my custom directive
<div class="fileUpload btn btn-primary">
<span>UPLOAD ASSETS</span>
<input id="dzFile" type="file" class="upload" />
</div>
</div>
</div>
angular js
var appModule = angular.module("Newapp", []);
appModule.directive("appView", appView);
function appView(){
var directive = {
restrict: 'E',
templateUrl: 'app/View.html'
};
return directive;
}
appModule.directive("dropzone", function(){ //This is added to the View.html as attribute(see above html code with **)
var directive = {
restrict: 'A',
link: FullDragDrop
};
return directive;
});
function FullDragDrop(){
console.log("soemthing goes here");
}
How can I make this possible Please?
This code works for me.
Make sure templateUrl: 'app/View.html' exists
<script>
var appModule = angular.module("Newapp", []);
appModule.directive("appView", appView);
function appView(){
var directive = {
restrict: 'E',
templateUrl: 'view.html'
};
return directive;
}
appModule.directive("dropzone", function(){ //This is added to the View.html as attribute(see above html code with **)
var directive = {
restrict: 'A',
link: FullDragDrop
};
return directive;
});
function FullDragDrop(){
console.log("soemthing goes here");
}
</script>
<script type="text/ng-template" id="view.html">
<div class="dragdrop" id="dropzone" dropzone> //here is my custom directive
<div class="fileUpload btn btn-primary">
<span>UPLOAD ASSETS</span>
<input id="dzFile" type="file" class="upload" />
</div>
</div>
</script>
<body>
<app-view></app-view>
</body>
That's a noob question. I'm looking for the correct way to access the parent scope inside a directive in a nested ng-repeat. This is exactly what i mean:
<div ng-app="myApp" ng-controller="myCtrl">
<div ng-repeat="section in sections">
{{section.Name}}
<div ng-repeat="item in section.Items" ng-init="parent = section">
<span class="menuItem">{{item}}</span>
</div>
</div>
</div>
And the directive:
myApp.directive('menuItem', function () {
return {
restrict: 'C',
link: function (scope, element, attrs) {
console.log(scope.$parent.section.SectionId);
}
}
});
The directive is attached to an item in the inner ng-repeat, and i need to access a property of the parent object. The problem is that i cannot access directly to the parent properties (with scope.$parent), because ng-repeat creates a new scope, and i must append the name of the object i set in the ng-repeat (in this case scope.$parent.section.):
<div ng-repeat="section in sections">
console.log(scope.$parent.section.SectionId);
JsFiddle: http://jsfiddle.net/7Lra7Loy/2/
As i want the directive to be generic, so it can be used inside other ng-repeat blocks, without being forced to use the same names in the ng-repeat, the only way i found is to use an ng-init, that would be the same in all ng-repeat blocks (ng-init="parent = section"):
<div ng-repeat="section in sections">
{{section.Name}}
<div ng-repeat="item in section.Items" ng-init="parent = section">
<span class="menuItem">{{item}}</span>
</div>
</div>
myApp.directive('menuItem', function () {
return {
restrict: 'C',
link: function (scope, element, attrs) {
console.log(scope.parent.SectionId);
}
}
});
JsFiddle: http://jsfiddle.net/7Lra7Loy/1/
Is there a better way to handle this situation? Or am i just missing something? I searched a bit, but i couldn't find anything really useful.
Template:
<div ng-app="myApp" ng-controller="myCtrl">
<div ng-repeat="section in sections">
{{section.Name}}
<div ng-repeat="item in section.Items">
<span class="menuItem" section="{{section}}">{{item}}</span>
</div>
</div>
</div>
And directive:
myApp.directive('menuItem', function () {
return {
restrict: 'C',
scope: {
section: '#' // text-binding
//section: '&' //one-way binding
//section: '=' //two-way binding
},
link: function ($scope, element, attrs) {
console.log($scope.section);
}
}
});
JsFiddle: https://jsfiddle.net/nrkmn/26zhqbjg/
I have developed a transclude directive and I set it to use angularjs template script everything works fine but I still cannot access the bind data.
My code:
index.html
<div side-element="box" title="Links">
<ul>
<li>Link 1</li>
<li>Link 2</li>
</ul>
</div>
<script id="box" type="text/ng-template">
<div class="side-box">
<div class="content">
<h2 class="header">Box {{ title }}</h2>
<span class="content" ng-transclude></span>
</div>
</div>
</script>
<script id="ad" type="text/ng-template">
<div class="side-ad">
<div class="content">
<h2 class="header">AD {{ title }}</h2>
<span class="content" ng-transclude></span>
</div>
</div>
</script>
app.js:
angular.module('myApp.directives')
.directive('sideElement', function ($templateCache, $log) {
return {
scope: {
title: '#'
},
transclude: 'element',
link: function(scope, element, attrs, ctrl, transclude){
var template = $templateCache.get(attrs.sideElement);
var templateElement = angular.element(template);
$log.info(scope.title);//Output the title i put in the html which is (Links)
transclude(scope, function(clone){
element.after(templateElement.append(clone));
});
}
};
});
the scope inside the link function(....) displaies the correct title but it doesn't work in the html:
Box {{ title }}
Link 1
Link 2
I think I missed one thing but I can't figure it out, I need your help to complete the cycle.
Thanx in advance,
The template element that contains the angular binding expressions must be compiled first, and then linked. The compilation phase prepares the template, while the linking phase sets up your $watchers for your binding expressions.
Demo Here
Here is a compile function that should work:
.directive('sideElement', function ($templateCache, $compile, $log) {
return {
restrict: 'A',
transclude: true,
scope:'#',
compile: function(element, attrs) {
var template = $templateCache.get(attrs.sideElement);
var templateElement = angular.element(template);
element.append(templateElement);
return function(scope, element, attr, ctrl, transclude) {
$log.info(scope.title);
}
}
}