AngularJS =?bind not updating - javascript

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.

Related

AngularJS - Why I cannot get element class name in directive's link function?

I got below code. I expect it will show me true. However, it show me false.
Can anyone explain it to me and provide me a solution to check if the class existed in the element? Thanks in advance.
// HTML
<tit-txt class="{{editable}}" ng-model="mdEnt.phone"></tit-txt>
//JS
.directive('titTxt', function () {
return {
restrict: 'E',
scope: {
ngModel: '=',
},
link: function (scope, element, attrs) {
console.log(element.hasClass('editable'));
},
template: '<input ng-model="ngModel" />',
};
})
Use a watcher to detect when the class is updated:
app.directive('titTxt', function () {
return {
restrict: 'E',
scope: {
myModel: '=',
},
link: function (scope, element, attrs) {
scope.$watch(hasClassEditable, function() {
console.log(element.hasClass('editable'));
});
function hasClassEditable() {
return element.hasClass('editable');
}
},
template: '<input ng-model="myModel" />',
};
})
Interpolated bindings such as class={{editable}} update each digest cycle. The directive needs to wait for a binding to update before using the value.
The DEMO
angular.module("app",[])
.run(function($rootScope,$timeout) {
$rootScope.$timeout = $timeout;
})
.directive('titTxt', function () {
return {
restrict: 'E',
scope: {
myModel: '=',
},
link: function (scope, element, attrs) {
scope.$watch(hasClassEditable, function(value) {
console.log("has class 'editable'", value);
});
function hasClassEditable() {
return element.hasClass('editable');
}
},
template: '<input ng-model="myModel" />',
};
})
<script src="//unpkg.com/angular/angular.js"></script>
<body ng-app="app">
<input type="checkbox" ng-model="editable" ng-change="$timeout()"
ng-true-value="'editable'" ng-false-value="''" />
add class "editable"
<br>
<tit-txt class="{{editable}}" my-model="inputTxt"></tit-txt>
<br>
editable="{{editable}}"
<br>
inputTxt="{{inputTxt}}"
</body>
Try to do it like this:
// HTML
<div ng-app="myApp" ng-controller="myController">
<tit-txt custom-class="customClass"
cust-var="myVal"></tit-txt>
<div ng-bind="myVal"></div>
</div>
//JS
var app = angular.module('myApp', []);
app.controller('myController', function($scope) {
$scope.customClass = "editable";
$scope.myVal = "this";
});
app.directive('titTxt', function () {
return {
restrict: 'AE',
replace: true,
scope: {
customClass: '=',
custVar: '='
},
link: function (scope, element, attrs) {
console.log(scope);
console.log((scope.customClass === "editable"));
},
template: '<input class="myClass" ng-Model="custVar"/>',
};
})
EDIT: Edited the code to include the working code. Here is the link for plunker
Try this instead:
link: function (scope, element, attrs, controller) {
console.log(angular.element(element).hasClass('editable'));
}
Try with pre function,
link: {
pre: function(scope, element, attr, controllers) {
console.log(element.hasClass('editable'));
},
post: function(scope, element, attr, controllers) {
},
}

Angular: how to bind to required/ngRequired

I have this directive which can be required or not. It can be used in two ways (as far as I know)
<my-foo required></my-foo>
or
<my-foo ng-required="data.value > 10"></my-foo>
So, because require and ngRequire are basically the same thing you would think that the directive could do this
HTML:
<my-foo ng-require="data.isRequired"></my-foo>
JS:
...
.directive('myFoo', function () {
return {
restrict: 'E',
scope: {
required: '='
}
...
DEMO
Well, nope, this doesn't work, scope.require is undefined. You actually have to change the scope definition to
scope: {
required: '=ngRequired'
}
So the question is what is the preferred way to handle both situation such that the value gets stored in scope.required ? Should I defined both or use attrs from the link function ?
There are basically 2 approaches you can pick:
1. Custom form element supporting ng-model
If you peek at the ng-required directive source code you'll find it only deals with ng-model controller:
restrict: 'A',
require: '?ngModel',
link: function(scope, elm, attr, ctrl) {
if (!ctrl) return;
attr.required = true; // force truthy in case we are on non input element
ctrl.$validators.required = function(modelValue, viewValue) {
return !attr.required || !ctrl.$isEmpty(viewValue);
};
attr.$observe('required', function() {
ctrl.$validate();
});
}
Thus if you custom directive supports ng-model you already have support for ng-required i.e.:
angular.module('test', [])
.directive('myInput', function(){
return {
restrict: 'E',
require: 'ngModel',
scope: true,
template: '<div><button ng-click="changeValue()">Change Value from: {{currentValue}}</button></div>',
link: function (scope, element, attrs, ngModelCtrl) {
ngModelCtrl.$parsers.push(function(val){
if(!val){
return null;
}
return parseFloat(val, 10) * 100;
});
ngModelCtrl.$render = function() {
scope.currentValue = ngModelCtrl.$viewValue || 'No value';
};
scope.changeValue = function read(){
var newValue = Math.random();
if(newValue > 0.5){
ngModelCtrl.$setViewValue(newValue + "");
} else {
ngModelCtrl.$setViewValue(null);
}
ngModelCtrl.$render();
};
}
};
});
2. Wrap existing directive and pass ng-required:
angular.module('test', [])
.directive('myFormElement', function() {
return {
restrict: 'E',
scope: {
model: '=',
required: '='
},
template: '<div>Enter number: <input type="number" ng-model="data.number" ng-required="required"></div>'
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="test" ng-init="data={value:'Initial', required: false}">
<form>
Is required: <input type="checkbox" ng-model="data.required">
<my-form-element required="data.required" model="data"></my-form-element>
</form>
</div>

$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

Making a simple directive only modal dynamic

So i am a new to angular. Trying to make a simple modal using angular directives only.
The reasons for making this:
No need to use bootstrap
No need for a controller and
Thus no dependency injection
function modalTrigger() {
return {
restrict: 'A',
transclude: true,
link: function(scope, element, attrs) {
scope.show = false;
scope.toggleModal = function() {
console.log(scope);
scope.show = !scope.show;
};
},
template: '<div class="modal-trigger" ng-click="toggleModal()" ng-transclude></div>'
};
}
function modalDialog() {
return {
restrict: 'E',
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;
scope.hideModal = function() {
console.log("called hide modal");
scope.show = false;
};
},
template: "<div class='ng-modal' ng-show='show'><div class='modal-backdrop' ng-click='hideModal()'></div><div class='container' ng-style='dialogStyle'><div class='ng-modal-close' ng-click='hideModal()'><a href=''>×</a></div><div class='modal-content' ng-transclude></div></div></div>"
};
}
"modal-trigger" directive is used to trigger the "modal-dialog" directive by setting "show" to true. here are the two directives
Right now, if you click any of the triggers, both the modals show up. how do i make the triggers specific to their own modals?
To make things more clear, here is an example on plunker: http://plnkr.co/edit/lNwTy3ddFFBBm2DPHUGA?p=preview
The problem is that both modals observe the same attribute (show) for showing/hiding themselves. To correct this, the show attribute must be scoped for each modal and I propose the following:
Have a controller that defines the 2 show attributes:
app.controller('Ctrl', function($scope) {
$scope.show1 = false;
$scope.show2 = false;
});
And activate it as:
<body ng-app="angularApp" ng-controller="Ctrl as ctrl">
Change the modal trigger to have isolated scope and the show attribute, as follows:
function modalTrigger() {
return {
restrict: 'A',
transclude: true,
link: function(scope, element, attrs) {
scope.toggleModal = function() {
scope.show = !scope.show;
};
},
scope: {
show: '='
},
template: '<div class="modal-trigger" ng-click="toggleModal()" ng-transclude></div>'
};
}
Use it e.g. as:
Waiting List
Change the modal directive to use isolated scope with the show attribute; simply add:
scope: {
show: '='
},
to the directive. Use it as:
<modal-dialog data-modal-name="modal1" show="ctrl.show1">
A forked plunker with the full solution: http://plnkr.co/edit/cJOXju9H3PMjRdUKqVCF?p=preview

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

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';
}
});
}
}
});

Categories

Resources