Angular directive does not update when $http completes - javascript

I'm having trouble getting a directive to update after a $http completes. The two solutions I've tried are setting a flag $scope.ready to true and using with ngIf on a parent container and using $scope.$watch() for my data object. The flag doesn't seem to work, though the element.replaceWith('<h1>test</h1>'); does seem to work, just both newVal and oldVal are undefined.
<div ng-if="ready">
<puzzle data="data"></puzzle>
</div>
//function
function myCtrl($scope, $http){
$scope.ready = false;
$scope.data = {};
$http({
method: 'GET',
url: './app/data/test1.json'
})
.then(function(res){
$scope.ready = true;
$scope.data = res.data;
console.log($scope.data);
//tried $scope.$apply() here, no success
}, function(){
console.error('err', arguments);
})
}
//directive
function myDirective(){
return {
restrict: 'E',
scope: {
data: '=',
ready: '='
},
link: function(scope, element, attrs){
scope.$watch('data',function(newVal, oldVal){
console.log(scope, element, attrs);
}, true);
element.replaceWith('<h1>test</h1>'); //this prints out
}
};
}

Related

$watch on array in service not working in directive

I am trying to watch when an array in a service is updated. I update this array in the service using a function in a directive based controller. Now for some reason the watch function does not get called in the link function of the second directive. Why is watch not being called in the second directive. I am trying to update the scope of a variable in the second directive so that it updates when the first directive function updates the service.
The Service
var productServices = angular.module('productServices', ['ngResource']);
productServices.factory('PlayerListS', [function() {
var playerList = [];
function getList() {
console.log(playerList);
return playerList;
}
function addToList(name) {
playerList.push(name);
}
return {
addToList :addToList,
getList: getList
}
}]);
The Directives
'use strict';
bands.directive("player",['PlayerListS', function (PlayerlistS) {
return {
restrict: 'E',
scope: {
person:'#person',
add:'&add'
},
replace: false,
templateUrl: "partials/player.html",
controller: function($scope, $element, $compile) {
$scope.playerList = ["A", "B"];
$scope.add = function(name) {
PlayerlistS.addToList(name);
PlayerlistS.getList();
}
},
link: function(scope, el, attrs) {
}
};
}]);
bands.directive("playerList", ['PlayerListS', function (PlayerlistS) {
return {
restrict: 'E',
replace: false,
template: "<p>Test</p>",
controller: function($scope, $element, $compile) {
},
link: function($scope, $el,$attrs) {
console.log('added');
var x = PlayerlistS.getList()
/*THIS IS WHERE THE WATCH IS HAPPENING*/
$scope.$watch('x', function (newVal, oldVal) {
console.log("CHANGED");
}, true);
}
};
}]);
The Controller
var bands = angular.module('bands', []);
bands.controller('ViewHousesCtrl', ['$scope', '$element', '$routeParams', '$q',
function ViewHousesCtrl($scope, $element, $routeParams, $q) {
$scope.playerLis = ["A","B","C"];
}]);
HTML
<player ng-show="true" person="RandomName" add="add()"></player>
<player-list ng-show="true" ng-repeat="a in playerLis"></player-list>
What your watcher is really doing, is trying to watch a variable called x on the directive scope. But your variable x is just a regular local variable, so your watcher doesn't trigger. So what your watcher basically translates to is this:
$scope.$watch(function(scope){
return scope['x'];
}, function (newVal, oldVal) {
console.log("CHANGED");
}, true);
You can probably see why it doesn't trigger. There is no variable $scope.x. Instead you should try watching the service directly, by specifying the watch function. Like this:
$scope.$watch(function(){
return PlayerlistS.getList();
}, function (newVal, oldVal) {
console.log("CHANGED");
}, true);
You have a spelling mistake in your HTML, it should be:
<player-list ng-show="true" ng-repeat="a in playerList"></player-list>

How do I access a directive scope from a parent directive/controller?

I have a template that goes something like this:
<parent-directive>
<child-directive binding="varFromParent"></child-directive>
<button ng-click="parentDirective.save()"></button>
</parent-directive>
When executing a function in the parentDirective controller, is it possible to access and manipulate the scope variables of the childDirective for e.g. if I have them set up as so
angular.module('app').directive('parentDirective', function() {
return {
restrict: 'E',
templateUrl: '...',
controllerAs: 'parentDirective',
controller: function($rootScope, $scope) {
//...
this.save = () => {
//Need to manipulate childDirective so that its
//scope.defaultValue == 'NEW DEFAULT'
}
}
}
});
and
angular.module('app').directive('childDirective', function() {
return {
restrict: 'E',
templateUrl: '...',
scope: {
binding: '='
},
controllerAs: 'childDirective',
link: function(scope, elm, attrs) {
scope.defaultValue = 'DEFAULT';
}
}
});
How would I go about doing this? Is there any way to do this without setting up a bidirectional binding? I would like to avoid a mess of attributes on the <child-directive> element if possible.
There are many way to set up a communication between your children and your parent directive:
Bidirectional binding (like you said)
Registration of your children in your parent.
You can use the directive require property and the last parameter of the link function controllers to register a children in his parent.
Events, see $scope.on/broadcast
Angular services (as they are "singletons", it's very easy to use it to share data between your directives)
etc.
Example for 2:
angular.module('Example', [])
.directive('parent', [function () {
return {
controller: function (){
// registerChildren etc
}
// ...
};
}])
.directive('children', [function () {
return {
require: ['^^parent', 'children'],
controller: function (){
// ...
}
link: function ($scope, element, attributs, controllers) {
ParentController = controllers[0];
OwnController = controllers[1];
ParentController.registerChildren(OwnController);
// ...
}
// ...
};
}])
In this case you probably don't need to isolate child's directive scope. Define a variable you need to change on parent's scope and then child's directive scope would inherit this value so you can change it value in child's directive and it would be accessible from parent.
angular.module('app').directive('parentDirective', function() {
return {
restrict: 'E',
controllerAs: 'parentCtrl',
controller: function($scope) {
$scope.value = 'Value from parent';
this.value = $scope.value
this.save = function() {
this.value = $scope.value;
}
}
}
});
angular.module('app').directive('childDirective', function() {
return {
restrict: 'E',
controllerAs: 'childCtrl',
controller: function($scope) {
$scope.value = 'Value from child';
this.setValue = function() {
$scope.value = 'New value from child';
}
}
}
});
Here is the fiddle
http://jsfiddle.net/dmitriy_nevzorov/fy31qobe/3/

How to pass data from a custom angular element to the controller?

I have a controller and element directive:
ngModule
.controller('summaryCtrl', [ '$scope', '$http', function($scope, $http){
$scope.loaded = false;
$http
.get('some/item/'+itemId) //how do I get this itemId
.success(function(data){
$scope.data = data;
$scope.loaded = true;
})
.error(function(data){
//TODO
});
}])
.directive('cpSummary', function(){
return {restrict: 'E', templateUrl: 'some/path.html'};
});
and I want to use the directive something like this:
<cp-summary item-id="{id}" ng-controller="summaryCtrl"></cp-summary>
the item-id attribute get's set by the parent controller that is rendering the cp-summary elements in a ng-repeat. So I'd just like to know if it's possible to get the item-id attribute value inside the summaryCtrl.
You should use a controller for your directive. and then you can pass the item-id to the directive controller and do the stuff.
app.directive('cpSummary', function(){
return {
restrict: 'E',
templateUrl: 'some/path.html',
scope: {
item_id: '=itemId'
},
controller: ['$scope','$http',function($scope,$http) {
$http
.get('some/item/'+$scope.item_id)
.success(function(data){
$scope.data = data;
$scope.loaded = true;
})
.error(function(data){
//TODO
});
}]
};
});
As you wanted to pass id to you directive then you should include that variable inside directive isolated scope. It should be {{id}} instead of {id} & then use # inside your directive. # is for one way binding. Also assign the controller from directive.
Markup
<cp-summary item-id="{{id}}"></cp-summary>
Directive
.directive('cpSummary', function(){
return {
restrict: 'E',
templateUrl: 'some/path.html',
scope: {
item_id: '#itemId'
},
controller: 'summaryCtrl'
};
});
var app = angular.module("App", []);
app.directive("myDirective", function() {
var itemFunction = function(scope, element, attributes) {
scope.item-id= attributes["myDirective"];
};
return {
restrict: "A",
template: "<p></p>",
link: itemFunction
};
});

angular directive load after got variables

I have an angular directive that loads data from service,
BUT
it loads the data with a variable he go from a controller that it was loaded as well from a service.
code:
directive:
app.directive("posts", ['Posts', function(Posts) {
return {
restrict: 'E',
template: '' +
'<div ng-repeat="post in posts"></div>',
scope: {
showLoading: '&',
hideLoading: '&',
spot: '#'
},
controller: ['$scope', function ($scope) {
}],
link: function(scope, element, attrs) {
$scope.load = function () {
Posts.loadPostsBySpot(scope.spot)
};
}
};
}]);
Controller
app.controller('spotPageController', ['$scope', 'Spots', function ($scope, $Spots) {
doit = function () {
Spots.getSpot($)
.success(function (data) {
$scope.spotId = data.data;
console.log($scope.spot);
}).error(function (data) {
console.log('error');
});
};
}]);
html inside
<posts spot="{{spotId}}" showLoading="showLoading()" hideLoading="hideLoading()"></posts>
but when the directive is loaded the "spot" is not yet set,
so how do I make the directive load only after the spot is set.
Use ng-if.
<posts ng-if="spotId" spot="{{spotId}}" showLoading="showLoading()" hideLoading="hideLoading()"></posts>
This element will be rendered only after the spotId is initialized. Therefore, your directive will not be called before that.
If you want to encapsulate this behavior in directive, you should watch for changes of the scopeId. See the fiddle.

How to implement a disable when loading directive

Hi I have a variable on my scope named loadingdata. It will have the values true or false to determine if data is loading or not. I would like to put an attribute on an element to disable it if data is loading. Here is the code I already have but it is not working:
module.directive('disableWhenLoadingData', function () {
return {
restrict: 'A',
scope: {},
link: function ($scope, element, attrs) {
$scope.$watch('loadingData', function(newValue, oldValue) {
element.attr('disabled', newValue);
});
}
};
});
any ideas
You can use Angular's own ngDisabled directive instead of writing your own.
Service:
module.factory('GetDataService', function ($http) {
return {
getCustomers: function() {
return $http({ url: '/someurl', method: 'GET'});
}
}
});
Directive:
module.directive('disableWhenLoadingData', function (GetDataService) {
return {
restrict: 'A',
scope: {},
link: function ($scope, element, attrs) {
$scope.loadingData = true;
GetDataService.getCustomers().success(function (data) {
$scope.loadingData = false;
});
}
};
});
Generally I set $scope.loading in my controller, and my button or whatever i set ng-disabled.
In my controller:
$scope.loadData = function () {
$scope.loading = true;
$http
.get('url')
.success(function (ret) {
$scope.loading = false;
});
}
In my view:
<button ng-disabled="loading" ng-click="loadData()">{{loading? 'loading Data' : 'Submit'}}</button>

Categories