How to pass data from controller to custom directive - angularjs

I am creating a custom directive in AngularJS. This directive should open a popup to display data. The code for the popup is in another html page and the custom directive injects the code into my main page. I am able to open the popup however I cannot display the existing data anywhere in the pop up.
Normally, I am able to display the data in the main page however the data just do not want to go into the html injected by the custom directive.
Like this I do not get any error however it does not pass the data.
Note: I had to trim some of the code here to simplify it.
This is my custom directive:
function updateCandidatePopup() {
var directive = {};
directive.restrict = "E";
directive.scope = {};
directive.templateUrl = "UpdateCandidatePopup.html";
directive.controller = function ($scope) {
$scope.SingleCandidate;
}
return directive;
}
This is where I register it:
myApp.directive("updateCandidatePopup", UpdateCandidatePopup);
This is how I use the directive in the mainpage
<update-candidate-popup value="SingleCandidate" class="modal fade" ng-model="SingleCandidate"
id="myUpdateModal"
role="dialog"
popup-data="SingleCandidate">
zxc</update-candidate-popup>
This is the UpdateCandidatePopup.html:
<div> {{SingleCandidate.FirstName}} </div>
This is the to display the data in the pop up controller: (FYI it is still trimmed)
myApp.controller('CandidatesController', function ($scope, $http, EmployerService, CandidateService) { //we injected localservice
//Select single data for update
$scope.getSingleData = function (C_ID) {
alert(C_ID);
$http.get('http://localhost:49921/api/Candidates/?C_ID=' + C_ID).success(function (data) {
$scope.SingleCandidate = data;
$scope.FName = $scope.SingleCandidate.FirstName;
alert($scope.SingleCandidate.FirstName);
alert($scope.FName);
}).error(function () {
$scope.error = "An Error has occured while loading posts!";
});
};
});

Sorry wrong !, answered your question, here I leave I found a code that will serve for your problem. In the background to the template you want to take, you let a controller and in the statement of your policy, put you going to do with those values, I think in your case is just printing.
myApp.directive('editkeyvalue', function() {
return {
restrict: 'E',
replace: true,
scope: {
key: '=',
value: '=',
accept: "&"
},
template : '<div><label class="control-label">{{key}}</label>' +
'<label class="control-label">{{key}}</label>' +
'<input type="text" ng-model="value" />'+
'<button type="button" x-ng-click="cancel()">CANCEL</button>' +
'<button type="submit" x-ng-click="save()">SAVE</button></div>',
controller: function($scope, $element, $attrs, $location) {
$scope.save= function() {
console.log('from directive', $scope.key, $scope.value);
$scope.accept()
};
}
}
});
jsFiddle

Solved the problem like below. It was only to inject to $scope in the directive controller.
myApp.directive("updateCandidatePopup", function () {
return {
templateUrl : "UpdateCandidatePopup.html",
restrict: 'E',
controller: function ($scope) {
}
}
});

Related

Extending a controller function within directive

I have a cancel function in my controller that I want to pass or bind to a directive. This function essentially clears the form. Like this:
app.controller('MyCtrl', ['$scope', function($scope){
var self = this;
self.cancel = function(){...
$scope.formName.$setPristine();
};
}]);
app.directive('customDirective', function() {
return {
restrict: 'E'
scope: {
cancel : '&onCancel'
},
templateUrl: 'form.html'
};
});
form.html
<div>
<form name="formName">
</form>
</div>
However, the $setPristine() don't work as the controller don't have access on the form DOM. Is it possible to extend the functionality of controller's cancel within the directive so that I will add $setPristine()?
Some suggested using jQuery to select the form DOM, (if it's the only way) how to do that exactly? Is there a more Angular way of doing this?
Since the <form> is inside the directive, the controller should have nothing to do with it. Knowing it would break encapsulation, i.e. leak implementation details from the directive to the controller.
A possible solution would be to pass an empty "holder" object to the directive and let the directive fill it with callback functions. I.e.:
app.controller('MyCtrl', ['$scope', function($scope) {
var self = this;
$scope.callbacks = {};
self.cancel = function() {
if( angular.isFunction($scope.callbacks.cancel) ) {
$scope.callbacks.cancel();
}
};
});
app.directive('customDirective', function() {
return {
restrict: 'E'
scope: {
callbacks: '='
},
templateUrl: 'form.html',
link: function(scope) {
scope.callbacks.cancel = function() {
scope.formName.$setPristine();
};
scope.$on('$destroy', function() {
delete scope.callbacks.cancel;
});
}
};
});
Use it as:
<custom-directive callbacks="callbacks"></custom-directive>
I'm not sure I am OK with this either though...

angular directive scope missunderstanding

Well, I want to create a "summernote" directive (wysiwyg editor).
This is the template:
<summernote active="false">
<button class="edit" ng-click="edit()">Edit</button>
<button class="save" ng-click="saveData()">Save</button>
<button class="cancel" ng-click="cancel()">Cancel</button>
<div class="summernote"></div> // here will be loaded the summernote script
</summernote>
Directive code:
...
.directive('summernote', function($compile) {
return {
restrict: 'E',
replace: true, // Not sure about what this code does,
scope: {
active: '='
},
link: function($scope, elem, attrs) {
var $summernote = elem.find('.summernote'),
$edit = elem.find('.edit'),
$save = elem.find('.save'),
$cancel = elem.find('.cancel');
$scope.active = false;
$scope.$watch('active', function(active) {
// switch summernote's & buttons' state
// code ...
});
// here I have the buttons' click event defined
// QUESTION 1: Is there a better way?
// I'm doing this because the code below is not working.
$edit.on('click', function() {...});
$cancel.on('click', function() {...});
$save.on('click', function() {...});
// THIS IS NOT WORKING...
$scope.edit = function() {
alert('edit');
};
$scope.cancel = function() {
alert('cancel');
};
}
}
});
When I click save button, I want to send some data, so I have declared saveData on the mainController, but I have to send the div.summernote data and I don't know how to do
<button class="save" ng-click="saveData(getSummernoteDataFromDirectiveScope?)">Save</button>
MainController:
.controller('MainController', function($scope, myDataFactory) {
$scope.saveSummernoteData(data) {
myDataFactory.updateData('field_name', data);
}
}
The main question is, how to works with different? scopes. The thing is that I want to separate the directive logic (edit, cancel, div.summernote behaviour), and the "save" button, which its logic is declared in the MainController (or main $scope, here is my mess).
Are the $scope of the link function and the MainController $scope the same??
I think I have a little mess with all of this, so any help (documentation) would be appreciated.
Documentation can be found here directives and here compile.
Replace: true, would replace the element you have attached the directive to with your template, because in your case your template seems to be inline with the code you don't need that (also it will be removed in the next major release of angular).
Question 1: You shouldn't need to bind $on events ng-click should just work.
If you want to define your save function inside your controller you can pass your function as an attribute and call it inside your save routine defined in your directive:
In your html:
<summernote active="false" on-save="saveData()">
<button class="edit" ng-click="edit()">Edit</button>
<button class="save" ng-click="save()">Save</button>
<button class="cancel" ng-click="cancel()">Cancel</button>
<div class="summernote"></div> // here will be loaded the summernote script
</summernote>
Inside your directive:
scope: {
active: '=',
invokeOnSave: '&onSave'
},
link: function($scope, elem, attrs) {
...
$scope.save = function() {
var data = "some data"; //Whatever mechanism you use to extract the data from your div
$scope.invokeOnSave(data);
};
...
}

Two way databinding in AngularJs not working with async callback

So I'm trying to build an AngularJS app and am having some troubles with two way databinding between a controller and a directive when used with an async callback. I have a page controller that pulls data from a server and then uses multiple custom form directives to edit the data. Here is my setup:
function pageController($scope, $http) {
// this is what the data will look like
$scope.controllerModel = {
obj1: {},
obj2: {}
}
$http.get('get the data').then(function(data) {
$scope.controllerModel = data; // fill in the data
$scope.$broadcast('formDataReady'); // tell the forms the data is ready
});
}
The directive:
module('blah').directive('customForm', function() {
return {
restrict: 'E',
scope: { model: '=' },
transclude: true,
replace: true,
controller: function($scope, $attrs) {
$scope.cleanModel = $scope.model ? _.cloneDeep($scope.model) : {};
$scope.reset = function() {
$scope.model = _.cloneDeep($scope.cleanModel);
};
$scope.isClean = function() {
return _.isEqual($scope.model, $scope.cleanModel);
};
// Let page controllers tell the from when the model has been loaded
$scope.$on('formDataReady', function() {
console.log('custom-form: resetting the clean model');
$scope.reset();
console.log($scope);
console.log($scope.model);
});
$scope.reset();
},
template:
'<div>' +
'<form name="form" novalidate>' +
'<div ng-transclude></div>' +
'<div class="form-actions">' +
'<button class="btn btn-primary" ' +
'ng-click="save()" ' +
'ng-disabled="form.$invalid || isClean()">' +
'Save</button>' +
'<button class="btn" ' +
'ng-click="reset()" ' +
'ng-disabled=isClean()>' +
'Cancel</button>' +
'</div>' +
'</form>' +
'</div>'
};
});
And a bit of html:
<div ng-controller="pageController">
<custom-form model="controllerModel.obj1">
<!-- inputs with ng-model to edit the data -->
</custom-form>
<custom-form model="controllerModel.obj2">
<!-- inputs with ng-model to edit the data -->
</custom-form>
</div>
The problem is that the directive's model is not updated as a result of the async callback. The really strange thing is that in the event listener on the directive, those two console.log calls seem to give contradictory information:
console.log($scope):
...
model: { object with data inside it as expected }
...
console.log($scope.model):
Object {} // empty
So in the first log the scope has the model, but $scope.model is somehow empty.
Thanks so much for any help with this. It really, really appreciated.
If you get your data in a resolve before the controller is instantiated, then the directive should read it from it just fine:
app.config(function($routeProvider) {
$routeProvider.route('myRoute', {
url: '/myroute',
resolve: {
getData: function($http) {
return $http.get('get the data').then(function(data) {
return data;
}
}
}
});
});
function pageController($scope, getData) {
// getData from your $http call is now available before your controller was instantiated
// and can be used by your directive
$scope.controllerModel = getData;
}
I'm not sure why the console log is giving contradictory info though

Angularjs directive depends on outer directive

I need to develop a workflow editor in Angularjs
This requires a directive(inner) that should add a div with some data and data for this directive should come from another directive(outer)
series of divs will be added right, top or bottom based on parameters.
Since you didn't post any code or exact requirements, please take a look on this demo where it shows calling directive from other directive:
HTML
<div ng-controller="MyCtrl">
<div directive-foo></div>
JS
var app = angular.module('myApp',[]);
app.directive('directiveFoo', function() {
return {
template: '<div directive-bar="123">bar</div>',
replace: true,
controller: function() {
console.log('in foo ctrl');
this.isFooAlive = function() {
return 'Foo is alive and well';
}
}
}
});
app.directive('directiveBar', function() {
return {
controller: function() {
console.log('in bar ctrl');
},
require: 'directiveFoo',
link: function(scope, element, attrs, fooCtrl) {
console.log(fooCtrl.isFooAlive());
}
}
});
function MyCtrl($scope) {
}
FIDDLE DEMO
Hope it will help you

Can I require an adjacent directive?

I have two element-level directives, a search box and a search results. My markup is something like this (simplified):
<catalogue-search-box query="{{query}}">
<catalogue-search-results></catalogue-search-results>
I'm trying to access the search box controller from the search results directive, but the documentation suggests that in the directive's require property I can only find controllers on the same element or on the parent element. Is there a way to find controllers on adjacent elements?
After you comments here is how I would do it: use an object to hold all your state and pass it to both directives. Demo plunker
HTML
<body ng-controller="MySearchController">
<search-box search="mySearch"></search-box>
<search-results search="mySearch"></search-results>
</body>
JS
var search = angular.module('search', []);
//simulated service
search.service('Search', ['$timeout', '$q', function($timeout, $q) {
return {
findByQuery : function(query) {
var deferred = $q.defer();
$timeout(function() {
deferred.resolve([query + ' result1', query + ' result2']);
console.log('resolved query ' + query);
}, 2000);
return deferred.promise;
}
};
}]);
search.controller('MySearchController', ['$scope', function($scope) {
$scope.mySearch = {
query : ''
}
}]);
search.controller('SearchBoxCtrl', ['$scope', 'Search', function($scope, Search) {
$scope.execute = function(search) {
console.log(search);
if(search.query && search.query.length > 3 && !search.running) {
search.running = true;
search.promise = Search.findByQuery(search.query).then(function(val) {
search.results = val;
});
}
};
}]);
search.directive('searchBox', function(){
return {
restrict: 'E',
scope : {
search : '='
},
controller: 'SearchBoxCtrl',
template : '<div ng-hide="search.results">Query: <input type="text" ng-model="search.query" ng-disabled="search.running"></input> <button ng-click="execute(search)" ng-disabled="search.running">Search</button></div>',
replace: 'true'
};
});
search.controller('SearchResultsCtrl', function(){
});
search.directive('searchResults', function(){
return {
restrict: 'E',
scope : {
search : '='
},
controller: 'SearchResultsCtrl',
template : '<div ng-show="search.results"><div ng-repeat="result in search.results">{{result}}</div></div>',
replace: true,
link : function(scope, element, attrs, ctrl){
}
};
});
PS:
Don't use p tags in directive templates as the root node. The html parser reports 2 nodes if you have p child nodes and angular has a requirement for a single root node.
You can further use the promise in the controller to register other functions to execute when the results come in.
One way I've been experimenting with since the question is having some kind of controller directive i.e.
<catalogue-search>
<catalogue-search-box query="{{query}}">
<catalogue-search-results></catalogue-search-results>
</catalogue-search>
I can then access the "controller directive" this using the parent (^) modifier in my require statement. Each directive can then talk to each other via the controller directive.
Does this seem sensible or is it overcomplicating things?

Resources