AngularJs directive watching async data - angularjs

I'm trying to use prettyprint with some ajax data. The problem is that when the directive is invoked, the data arem't ready, so I get undefined variable output.
Plunkr: http://plnkr.co/edit/fdRi2nIvVzT3Rcy2YIlK?p=preview
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope,$http) {
$scope.result = $http.get('data.json').success(function(result){
return result.data.dom
})
});
app.directive('prettyprint', function() {
return {
restrict: 'C',
link: function postLink(scope, element, attrs) {
element.html(prettyPrintOne(scope.result))
}
};
});

Use scope's $watch method:
scope.$watch("result" , function(n,o){
element.html(prettyPrintOne(scope.result));
})
And instead of this:
$scope.result = $http.get('data.json').success(function(result){
return result.data.dom
})
Use this:
$http.get('data.json').success(function(result){
$scope.result = result.dom;
})
Plunk: http://plnkr.co/edit/Autg0V

Related

Angular directive not triggered on dynamic element

I have a directive (DirectiveA), which makes an $http call and creates a new html code.
directiveA
(function(){
angular.module('app').directive('directiveA', directiveA);
})();
(function(){
angular.module('app').controller('DirectiveAController', DirectiveAController);
})();
function directiveA($timeout){
return {
restrict: 'E',
scope: {
url:'#'
},
template: '<div ng-if="template" ng-bind-html="template"></div>',
link: function ( scope, element, attrs ) {
scope.element = element;
},
controller: DirectiveAController
};
}
directiveA.$inject = ['$timeout']
function DirectiveAController($scope, $http, $sce){
$http.get(`${$scope.url}`).then(function(res){
if(res.success){
$scope.template = $sce.trustAsHtml(res.template);
}
});
}
DirectiveAController.$inject = ['$scope', '$http','$sce'];
this works fine.
On the new created element, i want to capture the click function using another directive.
Directive 2
(function(){
angular.module('mcq').directive('captureClick', captureClick);
})();
function captureClick($timeout, $compile){
return {
link: function ( scope, element, attrs ) {
console.log("i am called") // Working on page load but not on dynamic element
scope.element = element;
},
};
}
captureClick.$inject = ['$timeout', '$compile'];
response.template
<button capture-click></button>
Rendered a dummy element of response.template (as static content) and the directive works. How can i get it work on dynamically rendered element.
Using compile my directive is picked up.
(function(){
angular.module('app').directive('directiveA', directiveA);
})();
(function(){
angular.module('app').controller('DirectiveAController', DirectiveAController);
})();
function directiveA($timeout){
return {
restrict: 'E',
scope: {
url:'#'
},
template: '',
link: function ( scope, element, attrs ) {
scope.element = element;
},
controller: DirectiveAController
};
}
directiveA.$inject = ['$timeout']
function DirectiveAController($scope, $http, $sce, $compile){
$http.get(`${$scope.url}`).then(function(res){
if(res.success){
var com = $compile(res.template)($scope);
$scope.element.append(com[0].outerHTML);
}
});
}
DirectiveAController.$inject = ['$scope', '$http','$sce', '$compile'];
Note sure, this is the correct way and has any cons

How to Pass parameter from AngularJS Directive to AngularJS controller function

Thanks in advance,Actually I want to call a function in controller from app.Directive, Please anyone let me know How I can call?Also I passing parameter to that function?I'm new in angular and here is all code.
var app = angular.module('quizApp', []);
app.controller("SaveCtrl", function (scope) {
$scope.Save = function (score) {
$scope.TestDetailsViewModel = {};
$scope.TestDetailsViewModel.CorrectAnswer = $scope.score;
$http({
method: "post",
url: "/Home/ResultSave",
datatype: "json",
data: JSON.stringify($scope.TestDetailsViewModel)
}).then(function (response) {
alert(response.data);
})
};})
app.directive('quiz', function (quizFactory) {
return {
restrict: 'AE',
scope: {},
templateUrl: '/Home/Dashboard',
link: function (scope, elem, attrs) {
scope.getQuestion = function () {
var q = quizFactory.getQuestion(scope.id);
if (q) {
scope.question = q.question;
scope.options = q.options;
scope.answer = q.answer;
scope.answerMode = true;
} else {
scope.quizOver = true;
//Calling function save(); in Controller
//scope.Save(scope.score);
}
};
}
}});
In the case of an isolated scope, the directive scope is completely unaware of its parent’s scope.
To call a function of a controller you have to bind that function to the scope of directive and then call scope functions from inside the directive.
For example:
app.controller('MainCtrl', function($scope) {
$scope.commonFunc = function(passed){
$scope.name = passed;
};
});
app.directive('demodirective', function(){
return {
scope: {
commonFunc: '&'
},
link: function(scope){
scope.commonFunc({passed:"world"});
}
};
});
HTML
<body ng-controller="MainCtrl">
<demodirective common-func="commonFunc(passed)">
</demodirective>
Hello {{name}}
</body>
For Reference - https://plnkr.co/edit/fMIsQ87jgdx49QSnWq4o?p=preview

AngularJS Directive data binding not happening from controller

I am facing the problem of data binding from controller to directive because of delay in response from the server.
To better understand just see a small program below.When I remove the timeout function, binding happens.
<msg track="{{customer}}"></msg>
angular.module('myApp').directive('msg', function() {
return {
scope: {
track :"#"
},
link : function(scope, element, attrs) {
},
template : "<a href>{{track}}</a>"
}
});
angular.module('myApp').controller('HomeCtrl', ['$scope', function($scope) {
setTimeout(function() {
$scope.customer = "shreyansh";
}, 5000);
// assume some service call inside controller
// service1.getData().then(function(data){$scope.customer = data})
}]);
How can i fix above problem so that above code should render as
<msg track="shreyansh" class="ng-isolate-scope">shreyansh</msg>.
Any help is appreciated.
Thanks
var app = angular.module('plunker', []);
app.factory('myService', function($http) {
var promise;
var myService = {
getData: function() {
if (!promise) {
promise = $http.get('test.json').then(function(response) {
return response.data;
});
}
return promise;
}
};
return myService;
});
app.controller('MainCtrl', function(myService, $scope) {
myService.getData().then(function(d) {
$scope.data = d;
});
});
app.directive('msg', function() {
return {
restrict: 'E',
scope: {
track: "#"
},
link: function(scope, element, attrs) {
},
template: "<a href>{{track}}</a>"
}
});
<msg track="{{data.name}}"></msg>
test.json file
{
"name": "Pete"
}

How to update which template is shown based on what the user selects from the select box with a custom directive

I am trying to create a page that dynamically loads a template based on the option that the user chooses from a select box. I currently have it loading the template on page load but after that it does not change based on user action.
.directive('ngUsersearch', ['$compile', '$http', '$templateCache', function($compile, $http, $templateCache) {
var getTemplate = function(contentType) {
var templateLoader,
baseUrl = 'view2/components/',
templateMap = {
beer: 'beerList.html',
brewery: 'breweryList.html',
event: 'eventList.html',
guild: 'guildList.html'
};
var templateUrl = baseUrl + templateMap[contentType];
templateLoader = $http.get(templateUrl, {cache: $templateCache});
return templateLoader;
}
var linker = function(scope, element, attrs) {
var loader = getTemplate(scope.ngModel);
var promise = loader.success(function(html) {
element.html(html);
}).then(function (response) {
element.replaceWith($compile(element.html())(scope));
});
}
return {
restrict:"E",
scope: {
ngModel: '='
},
link: linker
}
}]);
Here is my HTML:
<select ng-model="userFilter">
<option value="beer">Beer</option>
<option value="brewery">Brewery</option>
<option value="event">Event</option>
<option value="guild">Guild</option>
</select>
<ng-usersearch ng-model="userFilter"></ng-usersearch>
you forgot listen the change event of the model;
var linker = function(scope, element, attrs) {
scope.$watch('ngModel', function(newValue, oldValue) {
var loader = getTemplate(newValue);
var promise = loader.success(function(html) {
element.html(html);
}).then(function (response) {
element.replaceWith($compile(element.html())(scope)); // you compile and you have isolated scope?
});
});
}
on your compile the only scope available would be ngModel
This solution worked for me. I switched the way that the directive was loading the template. This can be done at the link function, but after the directive is set up and a part of the DOM, I was trying to remove the directive itself from the DOM by replacing it, which does not play well with how Angular's selectors work. So, now I am just replacing its contents. Also, in order to get the ng-repeat to work within the custom directive I had to add the search-results='searchResults' and then define that in the directives scope as well.
HTML:
<ng-usersearch ng-model="userFilter" search-results='searchResults'></ng-usersearch>
Controller:
.controller('View2Ctrl', [ '$scope', 'Restangular', function($scope, Restangular) {
$scope.userSearch = "";
$scope.userFilter = "beer";
$scope.search = function(userSearch, userFilter) {
$scope.searchResults = ("No " + userFilter + " Information Available");
Restangular.all('search?q=' + userSearch + '&type=' + userFilter + '&withBreweries=Y').customGET().then(function(data) {
$scope.searchResults = data;
});
};
}])
Directive:
.directive('ngUsersearch', ['$http', '$templateCache', '$compile', function($http, $templateCache, $compile) {
var getTemplate = function(contentType) {
var templateLoader,
baseUrl = 'view2/components/',
templateMap = {
all: 'all.html',
beer: 'beerList.html',
brewery: 'breweryList.html',
event: 'eventList.html',
guild: 'guildList.html'
};
var templateUrl = baseUrl + templateMap[contentType];
templateLoader = $http.get(templateUrl, {cache: $templateCache.get()});
return templateLoader;
}
var link = function(scope, element) {
scope.$watch('ngModel', function(newValue, oldValue) {
var loader = getTemplate(newValue);
var promise = loader.success(function(html) {
var rendered = $compile(html)(scope);
element.empty();
element.append(rendered); });
});
}
return {
restrict:"E",
scope: {
ngModel: '=',
searchResults: '='
},
link: link
}
}]);
I hope this helps other coders because I struggled with this for a day.

How to dynamically load directive into page

I have an html file with a controller and a directive with a template url. I want to load/compile the directive conditionally in the controller:
Controller:
app.controller('TestController', function TestController($http, $scope, $compile) {
$scope.loadData = function (pageId) {
var pUrl = <some url>
$http({
method: 'GET',
url: pUrl
}).success(function (data, status) {
$scope.pData = data;
var htm = '<test-directive></test-directive>';
var elm = angular.element("#id").append(htm);
$compile(elm)($scope);
}).error(function (data, status) {
alert('error');
});
};
$scope.loadData();
});
Directive:
'use strict';
app.directive('testdirective', function ($http) {
var uDirective = {};
uDirective.restrict = 'E';
uDirective.templateUrl = 'js/directives/testdirective.html';
uDirective.controller = function ($scope, $element, $attrs) {
$scope.showDirectiveData();
$scope.showDirectiveData = function () {
$scope.directiveDataCollection = <get data>;
};
};
uDirective.compile = function (element, attributes) {
// do one-time configuration of element.
var linkFunction = function ($scope, element, atttributes) {
};
return linkFunction;
};
return uDirective;
});
Template used in Directive
<div>
<div ng-repeat="directiveData in directiveDataCollection">
<span><h4>{{directiveData.Title}}</h4></span>
</div>
</div>
How do i get to compile the code in the TestController, load the directive dynamically, and finally load the content and append the content in scope?
Here is a general template for you to reference that abstracts and also demonstrates a few Angular concepts:
JS
.directive('parentDirective', function(Resource, $compile){
return {
restrict: 'E',
link: function(scope, elem, attrs){
Resource.loadData().then(function(result){
scope.data = result.data;
var htm = '<child-directive></child-directive>';
var compiled = $compile(htm)(scope);
elem.append(compiled);
});
}
}
})
.directive('childDirective', function(){
return {
restrict: 'E',
template: '<div>Content: {{data.key}}</div>'
}
})
.factory('Resource', function($http){
var Resource = {};
Resource.loadData = function(){
return $http.get('test.json');
}
return Resource;
})
HTML
<body ng-app="myApp">
<parent-directive></parent-directive>
</body>
Notice that there is no controller code. This is because controllers should never manipulate the DOM - one reason is that it will make your code a PITA to test. So, I put everything in directives, where it should probably be in your case as well.
I also moved the $http service into a factory. Anything state/model related should be in a service. Among other reasons, by doing this, you can inject it almost anywhere (including inside of directives) to access your data without worrying about it disappearing when a controller unloads.
EDIT
You should also consider the dissenting view of the dynamic loading approach in general in the accepted answer of Dynamically adding Angular directives

Resources