AngularJS directive with change parameter lags - javascript

I have this code:
app.js
var app = angular.module('plunker', []);
app.controller('MainCtrl', function() {
var vm = this;
vm.unit = '';
vm.unitForm = "unitForm";
vm.unitInput = "unitInput";
vm.amount = '';
vm.amountForm = "amountForm";
vm.amountInput = "amountInput";
vm.errorText = "Error";
vm.pattern = new RegExp("^[0-9]*$");
vm.calculate = calculate;
function calculate(first, second){
var firstFloat = parseFloat(first.replace(",","."));
var secondFloat = parseFloat(second.replace(",","."));
var total = (firstFloat * secondFloat).toFixed(2);
console.log('calculating', firstFloat, secondFloat, total)
return total.toString().replace(".",",");
}
});
app.directive('textInput', function textInput() {
return {
restrict: 'E',
templateUrl: 'text-input.html',
scope: {
myClass: "#",
value: '=?value',
formName: '=',
inputName: '=',
pattern: '=',
errorText: '=',
change: "&"
}
};
});
text-input.html
<form name="formName">
<input type="text"
name="inputName"
ng-model="value"
ng-class="myClass"
ng-pattern="pattern"
ng-trim="false"
ng-change="change()"
/>
<div data-ng-show="formName.inputName.$error.pattern">
{{errorText}}
</div>
</form>
index.html
<text-input my-class="input"
form-name="vm.unitForm"
input-name="vm.unitInput"
pattern="vm.pattern"
error-text="vm.errorText"
value="vm.unit"
change="vm.total = vm.calculate(vm.amount, vm.unit)"
></text-input>
<br>
<text-input my-class="input"
form-name="vm.amountForm"
input-name="vm.amountInput"
pattern="vm.pattern"
error-text="vm.errorText"
value="vm.amount"
change="vm.total = vm.calculate(vm.amount, vm.unit)"
></text-input>
vm.unit: {{vm.unit}}
<br>
vm.amount: {{vm.amount}}
<br><br>
Just jusing *:{{vm.unit*vm.amount}}
<br>
Change:
{{vm.total}}
Link to plnkr: http://plnkr.co/edit/DOc0v7o7arS6PhYOHaK0?p=preview
As can be seen, I have a directive that binds a variable to a function.
For some reason I can't figure out, the whole thing lags.
To see what's wrong do this: Enter a value in the top box, i.e 2. Enter a value in the second box, i.e 3.
The models lag in input. To see a change from the function, you need to enter another value. But the function now uses the values before your last input!
So, I am thoroughly confused here. What's going on and why won't this work?

Related

how to bind a directive var to controller

i have a problem with using 2 way binding in angular, when i change my input, the change dosnt affect to controller. but the first init from controller affect directive.
in the picture i changed the value, but vm.date still have value test.
my directive:
(function (app) {
app.directive('datePicker', function () {
//Template
var template = function (element, attrs) {
htmltext =
'<input ng-readonly="true" type="text" id="' + attrs.elementId +
'" ng-model="' + attrs.model + '" type="date" />';
return htmltext;
}
//Manipulation
var link = function ($scope, elements, attrs, ctrls) {
//Declare variables we need
var el = '#' + attrs.elementId + '';
var m = attrs.model;
var jdate;
var date;
$scope[attrs.model] = [];
$(el).on('change', function (v) {
jdate = $(el).val();
gdate = moment(jdate, 'jYYYY/jMM/jDD').format('YYYY-MM-DD');
if (moment(gdate, 'YYYY-MM-DD', true).isValid()) {
date = new Date(gdate);
$scope[m][0] = date;
$scope[m][1] = jdate;
//console.log($scope[m]);
$scope.vm[m] = $scope[m];
console.log($scope.vm); //----> Here Console Write Right Data
} else {
//console.log('Oh, SomeThing is Wrong!');
}
});
} // end of link
return {
restrict: 'E',
scope: {vm: '='},
template: template,
link: link
};
});
}(angular.module('app')));
and my controller:
(function (app) {
app.controller('test', ['$scope', function ($scope) {
var vm = this;
vm.date = 'test';
vm.mydate = 'test2';
}]);
}(angular.module('app')));
and html:
<body ng-app="app">
<div ng-controller="test as vm">
<date-picker element-id="NN" model="vm.date" vm="vm"></date-picker>
<p>{{vm.date}}</p>
<date-picker element-id="NN2" model="vm.mydate" vm="vm"></date-picker>
<p>{{vm.mydate}}</p>
</div>
</body>
I am not sure why you made the textbox as readonly, but if you remove that readonly and try to update the textbox then the two way binding works. Here's the fiddle for that
https://fiddle.jshell.net/dzfe50om/
the answer:
Your controller has a date property, not a vm.date property. – zeroflagL May 25 at 13:48
You should define vm to $scope instead of this;
var vm = $scope;

Input field not being updated from controller in Angularjs

I am in learning phase of Angularjs and am stuck in a problem for last two days. I have seen lots of answer but don't know how to adapt those solutions in my case. What I want to do is update the input field via buttons using angularjs.
// html
<body ng-controller="Controller">
<input type="number" ng-model="data" update-view>
<br>
<label for="data">{{data}}</label>
<button name="btn1" ng-click='updateInput(1)'>1</button>
</body>
// js
var app = angular.module('calculator',[]);
app.controller('Controller', function($scope, $timeout){
$scope.data = 0;
var val = '';
$scope.updateInput = function(param) {
val += String(param);
$scope.data = val;
// val = param;
// $scope.data = val;
}
});
The expressions gets evaluated but the input field is not updating. I have seen other updating views with $setViewValue and $render but I don't know how to use them here.
app.directive('updateView', function () {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
element.bind('change', function () {
// console.log(ngModel);
scope.$apply(setAnotherValue);
});
function setAnotherValue() {
ngModel.$setViewValue(scope.data);
ngModel.$render();
}
}
};
});
Any help would be appreciated. Thanks
You don't need a directive for updating.
You seem to be setting a string value to $scope.data, which throws an error, because the input type is number.
angular.module('calculator', [])
.controller('Controller', function($scope){
$scope.data = 0;
var val = '';
$scope.updateInput = function(n){
val = n;
$scope.data = val;
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
<body ng-app="calculator" ng-controller="Controller">
<input type="number" ng-model="data">
<button ng-click="updateInput(1)">1</button>
</body>
I was noting that without converting the parameter into string, the input field would update with the changed model but as soon as I would change it into String, it would not update the input field. Also there was error thrown in console. So I just, on hit and trial basis, converted it back to int by changing only one piece of line $scope.data = val; into $scope.data = parseInt(val, 10); and hurrrayyy the input field is all updating just like I wanted. And as #cither suggested, I don't need to directive for this. Following is my working code
var app = angular.module('calculator',[]);
app.controller('Controller', function($scope, $timeout){
$scope.data = 0;
var val = '';
$scope.updateInput = function(param) {
val += String(param);
$scope.data = parseInt(val, 10);
}
});

Why can't I do a $scope update within a jQuery scope?

I'm using typeahead to get some suggestions on an input text, this is inside a div controlled by an Angular controller.
The code for the suggestion tasks works with a jQuery plugin, so when I select, something I'm trying to assign a value to $scope, however this is NEVER happening.
I already tried getting the scope of the element with var scope = angular.element($("#providersSearchInput").scope() and then assign it as suggested here but it didn't work.
This is what I'm trying:
<div class="modal-body" ng-controller="ProvidersController" ng-init="orderReviewTab = 'observations'">
<input type="text" id="providersSearchInput" data-provide="typeahead" class="form-control input-md" placeholder="Buscar proovedores">
{{currentProvider}}
</div>
The controller looks like this:
tv3App.controller('ProvidersController', function($scope, $rootScope, $http, $timeout) {
var resultsCache = [];
$("#providersSearchInput").typeahead({
source: function (query, process) {
return $.get("/search/providers/?query=" + query, function (results) {
resultsCache = results;
return process(results);
},'json');
},
matcher: function (item) {
var name = item.name.toLowerCase();
var email = item.email.toLowerCase();
var contact_name = item.contact_name.toLowerCase();
//console.log(name);
var q = this.query.toLowerCase();
return (name.indexOf(q) != -1 || email.indexOf(q) != -1 || contact_name.indexOf(q) != -1);
},
scrollHeight: 20,
highlighter: function (itemName) {
var selected = _.find(resultsCache,{name:itemName});
var div = $('<div></div>');
var name = $('<span ></span>').html('<strong style="font-weight:bold">Empresa: </strong> ' + selected.name);
var contact = $('<span ></span>').html(' <strong style="font-weight:bold">Contacto: </strong> ' + selected.contact_name);
var email = $('<span ></span>').html(' <strong style="font-weight:bold">e-mail:</strong> ' + selected.email);
return $(div).append(name).append(contact).append(email).html();
},
minLength: 3,
items: 15,
afterSelect: function (item) {
console.log(item);
$scope.$emit('providerSelected',item);
}
});
$scope.$on('providerSelected', function (event,provider) {
console.log(provider);
$scope.currentProvider = provider;
$scope.$apply();
});
});
Edit
I tried this to check any changes:
$scope.$watch('currentProvider',function (newValue,oldValue) {
console.log(oldValue);
console.log(newValue);
});
So when selecting something it actually triggers and $scope.currentProvider seems to be updated but its never getting rendered at view ...
get https://angular-ui.github.io/bootstrap/
once you do, in your code make sure
angular.module('myModule', ['ui.bootstrap']);
and for typeahead have
<input type="text" ng-model="currentProvider" typeahead="provider for provider in getProviders($viewValue) | limitTo:8" class="form-control">
In your controller make sure you have
$scope.getProviders = function(val){
return $http.get('/search/providers/?query=' + val).then(function(response){
return response.data;
})
}
This should do the trick although I haven't tested

How dynamically add new input element if all others was filled in AngularJS

please watch this Plunker
So I working with angular and need to add new input field when all others are filled in (by default on page placed 5 inputs and if all of them are filled automatically add one more input if new input also using will add one more input and etc).
For generate inputs I use ng-repeat and name_list[] for it:
<div collect-input>
<div class="form-group" ng-repeat="(i, name) in name_list track by $index">
<div class="row">
<div class="col-xs-12">
<input class="form-control" type="text" ng-model="data.name_list[i]" add-input/>
</div>
</div>
</div>
Each input have directive attr "add-input" with $watch() method inside. This method method track when $isEmpty parameter had changed.
Then value function pass value of this parameter to listen function.
directive('addInput', ['$compile', '$sce', '$timeout', function ($compile, $sce, $timeout) {
return {
restrict: 'A',
require: ['^collectInput', '?ngModel'],
link: function (scope, element, attrs, ctrl) {
var collectInput = ctrl[0];
var ngModel = ctrl[1];
$timeout(function(){
scope.$watch(
function(){
return ngModel.$isEmpty(ngModel.$modelValue);
},
function(isEmpty){
collectInput.reportInput(ngModel, isEmpty);
}
);
},1000)
}
}
}]);
Then this function call "reportInput()" that placed inside parent directive "collect-input". Main goal of this function is to add new input name to name_list[] for generating via ng-repeat
userApp.directive('collectInput', function() {
return {
restrict: 'A',
controller: function($scope) {
var dirtyCount = 0;
this.reportInput = function(modelValue, isEmpty) {
var count = $scope.name_list.length;
if (isEmpty == false){
dirtyCount ++;
console.log('+1:' + dirtyCount);
}
if (isEmpty == true){
if (dirtyCount <= 0){
dirtyCount = 0;
console.log('0:' + dirtyCount);
}
else if(dirtyCount > 0){
dirtyCount --;
console.log('-1:' + dirtyCount)
}
}
if (count === dirtyCount) {
$scope.name_list.push(modelValue);
//dirtyCount = dirtyCount + 1;
}
console.log('count:' + count);
console.log('dirtyCount:' + dirtyCount);
console.log(modelValue)
}
},
link: function(scope, element, attrs) {
}}});
So when I filled 5 default inputs everything is good after it appears new input but it is all in my IDE it work perfect if I add only one symbol for 5+ label (in plunker in some reason it not work) but when I add or delete something more code logic crash. It's hard to explain. I hope Plunker code more clarify this.
Not tested, and could be optimized, but here's my idea:
HTML :
<div class="form-group" ng-repeat="name in name_list">
<div class="row">
<div class="col-xs-12">
<input class="form-control" ng-model="name"/>
</div>
</div>
</div>
JS :
//watch any modification in the list of names
$scope.$watchCollection('data.name_list', function (list) {
//is there an empty name in the list?
if (!list.filter(function (name) { return !name; }).length) {
//if not, let's add one.
data.name_list.push('');
//and that will automatically add an input to the html
}
});
I don't see the point of a directive.

alternative method instead $watch in angularjs

I have a scope variable, when it returns true, i need to trigger some events or do something. I my case, the every first time, the scope variable returns undefined and later it returns true. In this case i used $watch method to get the expected funcionality. Is there any alternative approach to do the same instead using $watch ?
scope.$watch () ->
scope.initiateChild
, (value) ->
if value is true
$timeout ->
scope.buildOnboarding()
, 1000
You can try using AngularJS $on(), $emit() and $broadcast().
Here is an example: http://www.binaryintellect.net/articles/5d8be0b6-e294-457e-82b0-ba7cc10cae0e.aspx
You can use JavaScript getters and setters without any expense of using $watch.
Write code in the setter to do what you want when angular changes the your model's value you are using in scope. It gets null or an a State object as user types. Useful for working with type ahead text boxes that have dependencies on each other. Like list of counties after typing state without user selecting anything.
Here is some pseudo style code to get the idea.
<input ng-model="searchStuff.stateSearchText" />
<div>{{searchStuff.stateObject.counties.length}}</div>
<div>{{searchStuff.stateObject.population}}</div>
$scope.searchStuff=new function(){var me=this;};
$scope.searchStuff.stateObject = null;
$scope.searchStuff.getStateObjectFromSearchText = function(search){
// get set object from search then
return stateObject;
};
$scope.searchStuff._stateSearchText= "";
Object.defineProperty($scope.searchStuff, 'stateSearchText', {
get: function () {
return me._stateSearchText;
},
set: function (value) {
me,_stateSearchText = value;
me.stateObject = getStateObjectFromSearchText (value);
}
});
See this fiddle: http://jsfiddle.net/simpulton/XqDxG/
Also watch the following video: Communicating Between Controllers
A sample example is given below
Html:
<div ng-controller="ControllerZero">
<input ng-model="message" >
<button ng-click="handleClick(message);">LOG</button>
</div>
<div ng-controller="ControllerOne">
<input ng-model="message" >
</div>
<div ng-controller="ControllerTwo">
<input ng-model="message" >
</div>
javascript:
var myModule = angular.module('myModule', []);
myModule.factory('mySharedService', function($rootScope) {
var sharedService = {};
sharedService.message = '';
sharedService.prepForBroadcast = function(msg) {
this.message = msg;
this.broadcastItem();
};
sharedService.broadcastItem = function() {
$rootScope.$broadcast('handleBroadcast');
};
return sharedService;
});
function ControllerZero($scope, sharedService) {
$scope.handleClick = function(msg) {
sharedService.prepForBroadcast(msg);
};
$scope.$on('handleBroadcast', function() {
$scope.message = sharedService.message;
});
}
function ControllerOne($scope, sharedService) {
$scope.$on('handleBroadcast', function() {
$scope.message = 'ONE: ' + sharedService.message;
});
}
function ControllerTwo($scope, sharedService) {
$scope.$on('handleBroadcast', function() {
$scope.message = 'TWO: ' + sharedService.message;
});
}
ControllerZero.$inject = ['$scope', 'mySharedService'];
ControllerOne.$inject = ['$scope', 'mySharedService'];
ControllerTwo.$inject = ['$scope', 'mySharedService'];

Categories

Resources