How to use $watch within directive to update class (no jQuery)? - javascript

I'm trying to create an app where I poll live data every 10 seconds or so. And if the data changes it should get highlighted in the view. I have successfully managed this, but with a little help from jQuery. I want to do it with a template in angular instead.
This is my controller:
angular.module('myApp.controllers', [])
.controller('MainCtrl', ['$rootScope', 'liveDataPoller', function($rootScope, liveDataPoller) {
$rootScope.liveData = liveDataPoller.getData();
}]);
This is my directive:
angular.module('myApp.directives', []).
directive('liveValue', function() {
return {
restrict: 'A',
scope: {
val: '='
},
link: function(scope, element, attrs) {
scope.$watch('val', function(newValue, oldValue) {
if(newValue && oldValue && (newValue !== oldValue)) {
if(newValue < oldValue) {
element.removeClass("up").addClass("down");
} else {
element.removeClass("down").addClass("up");
}
} else {
element.removeClass("down").removeClass("up");
}
});
}
}
});
This is my view:
<div live-value val="liveData.randomInteger">{{liveData.randomInteger}}</div>
Is it possible to have the add/remove class changed to use transclude and a template instead? Don't really want to mix jQuery in this.
Just let me know if anything is unclear.

Sample demo: http://plnkr.co/edit/uXhOceXYWLLkmRkzxx1h?p=preview
A scope variable can be used to track the new changes and use a two way binding to update the class in the html.
app.directive('liveValue', function() {
return {
restrict: 'E',
replace: true,
scope: {
data: '=', change:'='
},
template: '<div class="{{change}}">The value({{data}}) has gone: {{change}}</div>',
link: function(scope, element, attrs) {
scope.change = '';
scope.$watch('data', function (newValue, oldValue) {
if (newValue == oldValue) {
scope.change='nochange';
}else if(newValue<oldValue){
scope.change='down';
}else{
scope.change='up';
}
});
}
}
});

Related

AngularJS =?bind not updating

I've lost my bearings with regards to why I can't pass updates to my directives.
What I'm trying to accomplish with the following piece of code is to be able to set focus on by pressing a button. The problem is however that the "focus" binding on drInput only ever is set when the directive have loaded when it should change whenever it changes in drWrap. How come and how do I get around this?
And now, ladies and gentlemen, I present to you: THE CODE!
<div ng-app="myApp">
<dr-wrap></dr-wrap>
</div>
var myApp = angular.module('myApp', []);
myApp.directive('drWrap', function($timeout) {
return {
scope: {
focus: '=?bind'
},
restrict: 'E',
replace: 'true',
template: '<div><button ng-click="openSearch()">focus</button><dr-input focus="focusSearch" /></div>',
link: function(scope, elem, attr){
scope.openSearch = function(){
$timeout(function(){
scope.focusSearch = true
alert('scope.focusSearch 2 = ' + scope.focusSearch)
}, 1000)
}
}
};
})
.directive('drInput', function() {
return {
scope: {
focus: '=?bind'
},
restrict: 'E',
replace: 'true',
template: '<input type="test" focus-me="{{ focus }}" />',
link: function(scope, elem, attr){
scope.$watch('focus', function(value){
if(value != undefined){
scope.focus = value
alert('scope.focus = ' + scope.focus)
}
})
}
};
})
.directive('focusMe', ['$timeout', function ($timeout) {
return {
link: function (scope, element, attrs) {
attrs.$observe('focusMe', function(value){
if ((value === true) || (value == 'true')) {
$timeout(function () {
element[0].focus()
scroll(element[0])
})
} else {
element[0].blur()
}
})
}
}
}])
And the FIDDLE!
https://jsfiddle.net/L56rdqLp/168/
When you write scope: { focus: '=?bind' }, this means that the attribute name should be bind but not focus, so the template of drWrap should look like:
template: '<div><button ng-click="openSearch()">focus</button><dr-input bind="focusSearch" /></div>'
Add ngBlur event handler to drInput directives input like:
template: '<input type="test" focus-me="{{ focus }}" ng-blur="focus = false"/>',
to change the model to false, when input has lost its focus.
Here is working fiddle.

How can i update a directive $scope from other directive contoller in Angular.js?

I have a diretive with a list of events loading from my service service:
.directive('appointments', [function () {
return {
restrict: 'CE',
scope: {
ngTemplate: '=',
},
controller: ['$scope','calendarService', function ($scope, calendarService) {
var vm = this;
vm.events = calendarService.getEvents();
}],
controllerAS:'vm',
link: function (scope, elem, attrs) {
scope.getTemplateUrl = function () {
if (angular.isDefined(scope.ngTemplate))
return scope.ngTemplate;
else
return "/list.directive.html";
}
},
template: '<div ng-include="getTemplateUrl()"></div>'
}
}])
Now in another directive i am updating this list, how can i update the list in the first controller?
.directive('appointmentsUpdate', [function () {
return {
restrict: 'CE',
scope: {
ngEventId: '=',
},
controller: ['$scope','calendarService', function ($scope, calendarService) {
var vm = this;
vm.update = calendarService.updateEvent(scope.ngEventId).then(function(res){
// Here is where the newly created item, should be added to the List (vm.events) from first directive
)
});
}],
controllerAS:'vm',
link: function (scope, elem, attrs) {
scope.getTemplateUrl = function () {
if (angular.isDefined(scope.ngTemplate))
return scope.ngTemplate;
else
return "/list.directive.html";
}
},
template: '<div ng-include="getTemplateUrl()"></div>'
}
}])
you can use angular broadcast service for this:
in first directive use this:
$rootScope.$broadcast('greeting', data_needs_to_be_send);
in other directive listen the event to update its scope:
$scope.$on('greeting', listenGreeting)
function listenGreeting($event, message){
alert(['Message received',message].join(' : '));
}
We use require property to make communication between directives.
Something like this
return {
restrict: 'AE',
require: '^ParentDirective or ^SameLevelDirective'
}
Here is the clear explanation of Driectives That Communicate by ToddMotto
Services are singletons, so if you update the list from one place (with your calendarService.updateEvent()), then if you retrieve the data from the service in the other directive, it should be the updated list.
You could use a watch to check when the list is updated:
$scope.$watch(() => calendarService.getEvents(), (newValue, oldValue) => {
// update your scope with the new list
}, true);

change directive when scope variable changes

I am using http request to get data from json file which I than use in controller.
app.controller('mainCtrl', ['$scope', 'loaderService', function ($scope, loaderService) {
//gets data from service
loaderService.getLoadedHtml().then(function (result) {
$scope.fields = result.data;
});
}]);
I need to update directive when this $scope.fields change as
app.directive('dform', function () {
return {
scope: {
action: '#',
method: '#',
html: '='
},
link: function (scope, elm, attrs) {
var config = {
"html": scope.fields
};
scope.$watch('fields', function (val) {
elm.dform(config);
});
//console.log(config);
//elm.dform(config);
}
};
})
and here is how I am using this directive
<div html="fields" dform></div>
But in my case when $scope.fields changes, i get scope as undefined in my directive $watch function.
Question:
How can I get the updated value for scope.fields in scope.$watch function?
You need to give the directive access to fields by adding a binding for it:
scope: {
action: '#',
method: '#',
html: '=',
fields: '='
}
And HTML:
<dform fields="fields" ...
The value might be undefined the first time, then you don't want to call dform:
scope.$watch('fields', function(newValue, oldValue) {
if (newValue === oldValue) return;
var config = {
"html": newValue
};
elm.dform(config);
});
Update
With this HTML:
<div html="fields" dform></div>
You just need to watch html instead, no need for $parent or adding fields as a binding:
scope.$watch('html', ...
Usually for directives that should be as transparent as possible, no new scope is supposed be used. Having a new scope also prevents other directives from requesting a new scope on the same element.
If only one of the attributes is supposed to be dynamic, it is as simple as
scope: false,
link: function (scope, elm, attrs) {
scope.$watch(function () { return scope[attrs.html] }, function (val) {
if (val === undefined) return;
var config = {
action: attrs.action,
method: attrs.method,
html: val
};
elm.dform(config);
});
}
Alternatively, bindToController can be used in more modern, future-proof fashion (depending on what happens with html, $scope.$watch can be further upgraded to self.$onChanges hook).
scope: true,
bindToController: {
action: '#',
method: '#',
html: '='
},
controller: function ($scope, $element) {
var self = this;
$scope.$watch(function () { return self.html }, function (val) {
if (val === undefined) return;
var config = {
action: attrs.action,
method: attrs.method,
html: val
};
$element.dform(config);
});
}
Considering that html="fields", the code above will watch for fields scope property.
use $parent.fields instead of fields

$watch isn't called after one directive is initiated in AngularJS

I am trying call up to do focus of a element in directive of modal.
After the directive is called the focus-me directive is called only first time.
I tried adding the focus-me element in dialog directive as shown in the Plunker.
Below are things which I am trying in the directive.
Show the Modal dialog box [working]
After the dialog box is shown i am trying to focus a specfic element in the dialog box [For which i added watch element in focus-me directive]
The Focus is happening only for the first time.
Plunker
Please help with,how to trigger the watch element on the directive for the above Plunker. Thanks a lot.
Below is the directive code for the focus and dialog which i am currently using.`
app.directive('focusMe', function($timeout, $parse) {
return {
link: function(scope, element, attrs) {
var model = $parse(attrs.focusMe);
scope.$watch(model, function(value) {
console.log('value=',value);
if(value === true) {
$timeout(function() {
element[0].select();
element[0].focus();
});
}
});
element.bind('blur', function() {
console.log('blur')
scope.$apply(model.assign(scope, false));
})
}
};
}).directive('modalDialog', function(){
return {
restrict: 'AE',
scope: {
show: '=',
focusDesign:'=focusDesign'
},
replace: true,
transclude: true,
link: function(scope, element, attrs) {
scope.dialogStyle = {};
if (attrs.width)
scope.dialogStyle.width = attrs.width;
if (attrs.height)
scope.dialogStyle.height = attrs.height;
if (attrs.focusDesigniso)
scope.focusDesigniso=attrs.focusDesigniso;
scope.hideModal = function(focusDesign) {
scope.show = false;
focusDesigniso=false;
//$scope.focusDesign=false;
};
scope.hideModalC = function() {
alert("I m in hide");
scope.show = false;
};
},
template: "<div class='ng-modal' ng-show='show'><div class='ng-modal-overlay' ng-click='hideModal(focusDesign)' ng-model='focusDesign'></div><div class='ng-modal-dialog' ng-style='dialogStyle'><div class='ng-modal-close' ng-click='hideModal(focusDesign)' ng-model='focusDesign'>X</div><div class='ng-modal-dialog-content' ng-transclude></div></div></div>"
};
});
I got solution for the problem in the directive.
The $scope.apply i was applying it model instead i should i used for the element in focus.
The updated the directive of focus i have copied below with working Plunker
app.directive('focusMe', function($timeout, $parse) {
return {
link: function(scope, element, attrs) {
var model = $parse(attrs.focusMe);
scope.$watch(model, function(value) {
console.log('value=',value);
if(value === true) {
$timeout(function() {
element[0].select();
element[0].focus();
});
}
});
element.bind('blur', function() {
scope.$apply(function()
{
attrs.focusMe=!attrs.focusMe;
})
})
}
};
});
Working Plunker

Why doesn't the background color of the knob load before click? [Angular.js]

I will keep this simple. You can check this fiddle here jsfiddle. Here when you load the knob, the color of the knob only gets updated on clicking/scrolling over it (the number changes and hence colors up). I have this same problem on my project and was hesitant to ask this as I was skeptic if I could properly make you understand my question. Now that I have this fiddle, I hope you all can see what is happening. I am new to angular.js. Every answer is a learning experience for me. Thanks in advance.
view
<div ng-app="Knob" ng-controller="myCtrl">
<input type="text" ng-model="number" knob class="dial">
</div>
Controller + Directive
var App = angular.module('Knob', []);
App.controller('myCtrl', function($scope) {
$scope.number = 24;
})
App.directive('knob', function() {
return {
restrict: 'A',
link: function(scope, element, attrs) {
$(element).knob();
}
};
});
I believe it the directive is being called before it has the value.
Wrapping it in a timeout works.
App.directive('knob',['$timeout', function ($timeout) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
$timeout(function () { // You might need this timeout to be sure its run after DOM render.
$(element).knob();
}, 0, false);
}
};
}]);
if anyone still looking to make this work on the latest angular...
angular.module('ui.knob', []).directive('knob', ['$timeout', function ($timeout) {
return {
restrict: 'EA',
replace: true,
template: '<input value="{{ knobData }}"/>',
scope: {
knobData: '=',
knobOptions: '&'
},
link: function ($scope, $element) {
var knobInit = $scope.knobOptions() || {};
knobInit.release = function (newValue) {
$timeout(function () {
$scope.knobData = newValue;
$scope.$apply();
}, 0, false);
};
$scope.$watch('knobData', function (newValue, oldValue) {
if (newValue !== oldValue) {
$($element).val(newValue).change();
}
});
$($element).val($scope.knobData).knob(knobInit);
}
};
}]);
created the fiddle with the working sample...
http://jsfiddle.net/k4yq06yt/

Categories

Resources