Dynamically update ion.rangeSlider ngModel in AngularJS directive - angularjs

I'm trying to update the value of an Ion.RangeSlider when its bound ng-model scope variable changes. The model updates when the Ion.RangeSlider is used, but not vice-versa. Other inputs with the same ng-model update when the model value changes, so this must be some special case.
Edit: Woo! Here's a snippet #lin :) Also jsfiddle.
var app = angular.module('ngModelIonRangeSliderDemo', []);
app.controller('MainCtrl', function ($scope, $rootScope, $timeout) {
$scope.someNumber = 10;
}).directive('ionRangeSlider', function ionRangeSlider() {
return {
restrict: 'A',
scope: {
rangeOptions: '=',
model: '=ngModel'
},
link: function (scope, elem, attrs) {
scope.$watch('model',function () {
elem.ionRangeSlider(scope.rangeOptions);
});
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.10/angular.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/1.1.2/ui-bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ion-rangeslider/2.1.6/js/ion.rangeSlider.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ion-rangeslider/2.1.6/css/ion.rangeSlider.min.css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ion-rangeslider/2.1.6/css/ion.rangeSlider.skinFlat.min.css" />
<div ng-app="ngModelIonRangeSliderDemo">
<div ng-controller="MainCtrl" class="wrapper">
<h3>Text input updates slider, but not vice-versa.</h3>
<input ion-range-slider ng-model="someNumber" range-options="{min: -100, max: 100, step: .001}">
<br/>
<input type="text" ng-model="someNumber" class="form-control">
</div>
</div>
I have tried all kinds of suggestions in over ten somewhat-related stack overflow posts (which is how I have set up the current scope.$watch scheme on the ngModel), but none have worked. There aren't any errors in my console. What's wrong? Also, why doesn't it work without any mention of the model in my directive? Please let me know if there's anything important I have failed to include in this post.

Just use slider.update() inside your directive and you will be fine:
var app = angular.module('myApp', []);
app.controller('MainCtrl', function ($scope, $rootScope, $timeout) {
$scope.someNumber = 15;
$scope.apply = false;
}).directive('ionRangeSlider', function ionRangeSlider() {
return {
restrict: 'A',
scope: {
rangeOptions: '=',
model: '=ngModel',
apply: '=apply'
},
link: function (scope, elem, attrs) {
elem.ionRangeSlider(scope.rangeOptions);
scope.$watch('apply',function () {
if (scope.apply) {
scope.apply = false;
var slider = elem.data("ionRangeSlider");
slider.update({
from: scope.model
});
}
});
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.10/angular.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/1.1.2/ui-bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ion-rangeslider/2.1.6/js/ion.rangeSlider.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ion-rangeslider/2.1.6/css/ion.rangeSlider.min.css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ion-rangeslider/2.1.6/css/ion.rangeSlider.skinFlat.min.css" />
<div ng-app="myApp" ng-controller="MainCtrl" class="wrapper">
<h3>Text input updates slider and vice-versa.</h3>
<input ion-range-slider ng-model="someNumber" apply="apply" range-options="{min: -100, max: 100, step: .001}">
<br/>
<input type="text" ng-model="someNumber" class="form-control" ng-change="apply = true">
</div>

Extra demo, how binding ion.rangeSlider to input works:
http://jsfiddle.net/IonDen/r5aox84v/
var $range = $(".js-range-slider"),
$inputFrom = $(".js-input-from"),
$inputTo = $(".js-input-to"),
instance,
min = 0,
max = 1000,
from = 0,
to = 0;
$range.ionRangeSlider({
type: "double",
min: min,
max: max,
from: 200,
to: 800,
onStart: updateInputs,
onChange: updateInputs
});
instance = $range.data("ionRangeSlider");
function updateInputs (data) {
from = data.from;
to = data.to;
$inputFrom.prop("value", from);
$inputTo.prop("value", to);
}
$inputFrom.on("input", function () {
var val = $(this).prop("value");
// validate
if (val < min) {
val = min;
} else if (val > to) {
val = to;
}
instance.update({
from: val
});
});
$inputTo.on("input", function () {
var val = $(this).prop("value");
// validate
if (val < from) {
val = from;
} else if (val > max) {
val = max;
}
instance.update({
to: val
});
});

Related

Merge two examples in Angularjs

I am trying to build #TagFriends and comment box feature from FACEBOOK.
I found two examples that might help, I am having library issue when I import one examples library in second one, whole code fails to work, if they are executed individually they work correctly.
1.Simple comment box :
http://devzone.co.in/angularjs-example-simple-user-comment-box/
2.#Tag friend code Below:
<!DOCTYPE html>
<html>
<head>
<link data-require="bootstrap#*" data-semver="3.3.2" rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css" />
<link rel="stylesheet" href="http://urbanoalvarez.es/smart-area/dist/smart-area.css">
<script data-require="jquery#2.1.3" data-semver="2.1.3" src="http://code.jquery.com/jquery-2.1.3.min.js"></script>
<script data-require="bootstrap#*" data-semver="3.3.2" src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js"></script>
<script data-require="angular.js#1.3.8" data-semver="1.3.8" src="https://code.angularjs.org/1.3.8/angular.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.8/angular-sanitize.js"></script>
<script src="http://urbanoalvarez.es/smart-area/dist/smart-area.js"></script>
<script src="elastic.js"></script>
<script src="app.js"></script>
<style>
.user{
color: #0074D9;
}
</style>
</head>
<body ng-app="myApp">
<div class="container" ng-controller="DemoController">
<h3>MOC 12</h3>
<!--<h4>#user mentions</h4>-->
<textarea class="form-control code" rows="5" ng-model="text" ng-trim="false" msd-elastic smart-area="config"></textarea>
<hr>
<small class="text-muted">
<b>Available users:</b><br> Bret, Antonette, Samantha, Karianne, Kamren, Leopoldo_Corkery, Elwyn.Skiles, Delphine, Maxime_Nienow, Moriah.Stanton <br>
Type for example "Hey #Antonette"
</div>
<script>
/*
* angular-elastic v2.4.2
* (c) 2014 Monospaced http://monospaced.com
* License: MIT
*/
angular.module('myApp', ['smartArea', 'monospaced.elastic'])
.controller('DemoController', ['$scope', '$http', function($scope, $http) {
$scope.text = '';
$scope.config = {
autocomplete: [
{
words: [/#([A-Za-z]+[_A-Za-z0-9]+)/gi],
cssClass: 'user'
}
],
dropdown: [
{
trigger: /#([A-Za-z]+[_A-Za-z0-9]+)/gi,
list: function(match, callback){
// match is the regexp return, in this case it returns
// [0] the full match, [1] the first capture group => username
$http.get('http://jsonplaceholder.typicode.com/users')
.success(function(data){
// Prepare the fake data
var listData = data.filter(function(element){
return element.username.substr(0,match[1].length).toLowerCase() === match[1].toLowerCase()
&& element.username.length > match[1].length;
}).map(function(element){
return {
display: element.username, // This gets displayed in the dropdown
item: element // This will get passed to onSelect
};
});
callback(listData);
}).error(function(err){
console.error(err);
});
},
onSelect: function(item){
return item.display;
},
mode: 'replace'
}
]
};
}]);
angular.module('monospaced.elastic', [])
.constant('msdElasticConfig', {
append: ''
})
.directive('msdElastic', [
'$timeout', '$window', 'msdElasticConfig',
function($timeout, $window, config) {
'use strict';
return {
require: 'ngModel',
restrict: 'A, C',
link: function(scope, element, attrs, ngModel) {
// cache a reference to the DOM element
var ta = element[0],
$ta = element;
// ensure the element is a textarea, and browser is capable
if (ta.nodeName !== 'TEXTAREA' || !$window.getComputedStyle) {
return;
}
// set these properties before measuring dimensions
$ta.css({
'overflow': 'hidden',
'overflow-y': 'hidden',
'word-wrap': 'break-word'
});
// force text reflow
var text = ta.value;
ta.value = '';
ta.value = text;
// exit if elastic already applied (or is the mirror element)
if ($ta.data('elastic')) {
return;
}
// Opera returns max-height of -1 if not set
maxHeight = maxHeight && maxHeight > 0 ? maxHeight : 9e4;
// append mirror to the DOM
if (mirror.parentNode !== document.body) {
angular.element(document.body).append(mirror);
}
// set resize and apply elastic
$ta.css({
'resize': (resize === 'none' || resize === 'vertical') ? 'none' : 'horizontal'
}).data('elastic', true);
/*
* methods
*/
/*
* initialise
*/
// listen
if ('onpropertychange' in ta && 'oninput' in ta) {
// IE9
ta['oninput'] = ta.onkeyup = adjust;
} else {
ta['oninput'] = adjust;
}
$win.bind('resize', forceAdjust);
scope.$watch(function() {
return ngModel.$modelValue;
}, function(newValue) {
forceAdjust();
});
scope.$on('elastic:adjust', function() {
initMirror();
forceAdjust();
});
$timeout(adjust);
/*
* destroy
*/
scope.$on('$destroy', function() {
$mirror.remove();
$win.unbind('resize', forceAdjust);
});
}
};
}
]);
</script>
</body>
</html>
I have implemented this feature. Find the GitHub: github.com/noob93/Smart-Comment-Box
The Devzone example is using a deprecated feature of AngularJs, I modified the code and feature started working fine.

How to use bootstrap-select with angular

I would like to style my dropdowns with bootstrap-select.
There are two dropdowns in the page. The first one has hardcoded options. The second one is bound dynamically with data from database.
The bootstrap-select is applied all well for the first one but not the second. It adds a text 'Nothing selected' and the options also are not bound.
How to fix this.
aspx:
<link href="../assets/css/bootstrap-select.css" rel="stylesheet" />
<script src="../assets/js/bootstrap-select.js"></script>
<script src="../assets/js/angular-1.3.16.min.js"></script>
<script src="../scripts/app.js"></script>
<script src="../scripts/controllers/Ctrl.js"></script>
<script src="../scripts/services/Factory.js"></script>
<script src="../scripts/directives/directives.js"></script>
<select id="ddlReports" bootstrapselectpicker >
<option data-ng-repeat="item in Reports" value="{{item.Id}}">{{item.Name}}</option>
</select>
<select id="ddlSets" bootstrapselectpicker>
<option value="0">-- Select Question Set --</option>
<option data-ng-repeat="item in QuestionSets" value="{{item.SetId}}">{{item.Title}}</option>
</select>
Ctrl.js
app.controller("Ctrl", ["$scope", "Factory", function ($scope, Factory) {
var init = function() {
$scope.Reports = [
{ Id: 1, Name: "abc" },
{ Id: 2, Name: "def" }
];
var promise = Factory.GetData();
promise.then(function (success) {
if (success.data != null && success.data != '') {
var data = success.data;
$scope.QuestionSets = data.QuestionSets;
}
},
function (error) {
});
}
init();
}]);
directive.js
app.directive('bootstrapselectpicker', function ($timeout) {
return {
restrict: "A",
link: function (scope, element, attrs, ctrl) {
$timeout(function () {
element.selectpicker();
});
}
};
});

AngularJS: How to refresh $viewValue even when $modelValue is unchanged

In the example below, the value in input 'ia' can get out of sync with the model value (e.g. if you type in 'aaa'). What I would like to achieve is when the input 'ia' loses focus, update its value to the formatted value of the current model value (e.g. when model value is null, update the view value to string 'N/A'). Anybody know I this might be achieved? Thanks.
The current behaviour is if you type in 'aaa' as profession, model value is updated to null bot 'aaa' stays in the input.
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<!DOCTYPE html>
<html ng-app="myapp">
<head>
<script>
var myapp = angular.module('myapp', []);
myapp.controller('mycontroller', function($scope) {
$scope.model = {
name: "ttt",
age: 24,
profession: null
};
});
var professionList = [{
id: 1,
name: "p1"
}, {
id: 2,
name: "p2"
}, {
id: 3,
name: "p3"
}, {
id: 4,
name: "p4"
}];
myapp.directive("prof", function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, element, attrs, ngModel) {
ngModel.$formatters.push(function(value) {
for (var i = 0; i < professionList.length; i++) {
if (value == professionList[i].id)
return professionList[i].name;
}
return "N/A";
});
//format text from the user (view to model)
ngModel.$parsers.push(function(value) {
for (var i = 0; i < professionList.length; i++) {
if (value == professionList[i].name)
return professionList[i].id;
}
return null;
});
element[0].ngModel = ngModel;
}
};
});
</script>
</head>
<body ng-controller="mycontroller">
<form>
Name:
<input ng-model="model.name" />
<br />Age:
<input ng-model="model.age" />
<br />Profession:
<input prof="" ng-model="model.profession" id="ia" />
<input ng-model="model.profession" />
</form>
<hr />Name: {{model.name}}
<br />Age: {{model.age}}
<br />Profession: {{model.profession}}
</body>
</html>
UPDATE:
I found a solution based on the answer in this question
//add this to the link function:
ngModel.$render = function(){
element[0].value = ngModel.$viewValue;
};
element[0].onblur = function(){
var viewValue = ngModel.$modelValue;
for (var i in ngModel.$formatters) {
viewValue = ngModel.$formatters[i](viewValue);
}
ngModel.$viewValue = viewValue;
ngModel.$render();
};
While returning null you are not re-initializing ngModel and I dont think directive is needed here , there is something called ng-blur in angularjs,
Profession : <input ng-model="model.profession" ng-blur="checkProfession() "/>
Profession ID : <input ng-model="model.id" />
In your controller write a function as,
$scope.checkProfession=function(){
var flag=0;
for (var i = 0; i < professionList.length; i++) {
if ($scope.model.profession == professionList[i].name){
$scope.model.id=professionList[i].id;
flag=1;
}
if(!flag){
$scope.model.id="";
$scope.model.profession="";
}
}
}

ng-minlength in directive not populated when invalid

I'm creating an input directive that encapsulates a label, and some error messaging but I'm getting stuck with ng-minlength and ng-maxlength. The issue is that they don't populate the model value until it's a valid value, so my parent scope can display a value while my directive doesn't, and vice versa. View this plunk for an example of what I mean.
Is the only way around this to define my own minlength and maxlength validators? Is there some way configure this behaviour so the model is always populated? I want to use all the built in validators in my directive, so no doubt this will be an issue with all of them and I'd rather not redefine them all.
HTML:
<!DOCTYPE html>
<html>
<head>
<script data-require="angular.js#*" data-semver="1.3.0-beta.5" src="https://code.angularjs.org/1.3.0-beta.5/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-app="app" ng-controller="Controller">
<label>Outer scope 1</label>
<input name="input1" type="text" ng-model="model1" ng-minlength="4"/>
<br/>
<label>Directive scope 1</label>
<input-dir ng-model="model1" ng-minlength="0"></input-dir>
<br/>
<br/>
<br/>
<label>Outer scope 2</label>
<input name="input3" type="text" ng-model="model2"/>
<br/>
<label>Directive scope 2</label>
<input-dir ng-model="model2" ng-minlength="4"></input-dir>
</body>
</html>
Javascript:
var app = angular.module('app', []);
app.controller('Controller', function($scope){
$scope.model1 = "Model1";
$scope.model2 = "Model2";
});
app.directive('inputDir', function(){
return {
restrict: 'E',
template: '<input type="text" ng-model="model" ng-minlength="{{ minlength }}" />',
scope: {
model: '=ngModel',
minlength: '=ngMinlength'
}
};
});
For anyone else with the same issue, I solved this by redefining all the validators, except now they return the value when invalid so the model is populated. Just include the valuedValidators module. These validators are available on the $error object as vvminlength etc.
vv-validators.js
(function () {
'use strict';
var validators = {};
validators.vvUrl = function(value){
var urlRegex = new RegExp(/[-a-zA-Z0-9#:%_\+.~#?&//=]{2,256}\.[a-z]{2,4}\b(\/[-a-zA-Z0-9#:%_\+.~#?&//=]*)?/gi);
return urlRegex.test(value);
}
validators.vvEmail = function(value){
var emailRegex = new RegExp(/^(([^<>()[\]\\.,;:\s#\"]+(\.[^<>()[\]\\.,;:\s#\"]+)*)|(\".+\"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/);
return emailRegex.test(value);
}
validators.vvMinlength = function(value, min){
var huh = value.length >= parseInt(min);
return min && value && huh;
}
validators.vvMaxlength = function(value, max){
return max && value && value.length <= parseInt(max);
}
validators.vvMin = function (value, min) {
return min && parseFloat(value) >= parseFloat(min);
}
validators.vvMax = function (value, max) {
return max && parseFloat(value) <= parseFloat(max);
}
validators.vvPattern = function (value, pattern) {
return pattern && new RegExp(pattern).test(value);
}
validators.vvFloat = function (value) {
var floatRegex = new RegExp(/^\-?\d+((\.|\,)\d+)?$/);
return floatRegex.test(value);
}
validators.vvInteger = function (value) {
var integerRegex = new RegExp(/^\-?\d+$/);
return integerRegex.test(value);
}
var app = angular.module('valuedValidators', []);
var names = Object.keys(validators);
names.forEach(function(name){
app.directive(name, function(){
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attrs, controller) {
controller.$parsers.unshift(function (viewValue) {
var valid = true;
var attrValue = attrs[name];
var func = validators[name];
if (attrValue !== 'false') {
valid = func(viewValue, attrValue);
controller.$setValidity(name.toLowerCase(), valid);
}
return viewValue;
});
}
};
});
});
}());
There is an even easier solution: use two textareas.
The first serves as a draft, the second is the model where the verification is to take place.
<textarea name="message_body_draft" cols="47" rows="15" style="width: 100%;"
ng-model="message_body_draft"
ng-change="message_body=message_body_draft"></textarea>
<textarea name="message_body" style="width: 100%;" disabled ng-model="message_body"
ng-minlength="100" required></textarea>
When the user writes in the first one, it automatically will populates the right model (the second one).

How to focus to next field when field value reaches a max length

I have a scenario where I have two or more textarea, when user enters the value in the textarea and when the values reaches to max-length for example ng-max-length is 15, the focus should automatically move to next text-area.
How this can be achieved?
For controlling the max-length I have taken solution from this link .
But i do not know how to make the focus to next element automatically
Below i have given the code which i tried
textarea.html
<!DOCTYPE html>
<html>
<head>
<title>Angular App</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
</head>
<body>
<div ng-app="taApp">
<div ng-controller="taController">
<textarea my-maxlength="15" ng-model="ta1.text1" rows="4" cols="20"> </textarea>
<textarea my-maxlength="15" ng-model="ta2.text2" rows="4" cols="20"> </textarea>
<textarea my-maxlength="15" ng-model="ta3.text3" rows="4" cols="20"> </textarea>
</div>
</div>
<script src="js/libs/angularjs-1.0.2/angular.js" type="text/javascript"></script>
<script src="js/controller/pageController.js" type="text/javascript"></script>
<script src="js/controller/textAreaFocus.js" type="text/javascript"></script>
</body>
</html>
textAreaFocus.js
angular.module('textareafocus',[]).directive('myMaxlength', function() {
return {
require: 'ngModel',
link: function (scope, element, attrs, ngModelCtrl) {
var maxlength = Number(attrs.myMaxlength);
function fromUser(text) {
if (text.length > maxlength) {
var transformedInput = text.substring(0, maxlength);
ngModelCtrl.$setViewValue(transformedInput);
ngModelCtrl.$render();
return transformedInput;
}
return text;
}
ngModelCtrl.$parsers.push(fromUser);
}
};
});
pageController.js
var taApp = angular.module('taApp',["textareafocus"]); // this creates a new angular module named "myApp";
taApp.controller('taController', function ($scope,$http) {
$scope.ta1 = { text1: 'TextArea 1'};
$scope.ta2 = { text2: 'TextArea 2'};
$scope.ta3 = { text3: 'TextArea 3'};
});
Screenshot
Currently i have only 3 textarea but it could be more, therefore focus should move to next element automatically.
I tried with element[ 1 ].focus() but it doesn't works.
Kindly help me how to resolve this issue.
You can implement a custom directive that requires jQuery to find the other text areas, ngMaxlength to determine the maximum length (this can be changed to a custom attribute), and tabindex to determine tab order:
Directive
app.directive('autoNext', function() {
return {
restrict: 'A',
link: function(scope, element, attr, form) {
var tabindex = parseInt(attr.tabindex);
var maxLength = parseInt(attr.ngMaxlength);
element.on('keypress', function(e) {
if (element.val().length > maxLength-1) {
var next = angular.element(document.body).find('[tabindex=' + (tabindex+1) + ']');
if (next.length > 0) {
next.focus();
return next.triggerHandler('keypress', { which: e.which});
}
else {
return false;
}
}
return true;
});
}
}
});
HTML
<textarea ng-maxlength="10" tabindex="1" auto-next></textarea>
<textarea ng-maxlength="10" tabindex="2" auto-next></textarea>
<textarea ng-maxlength="10" tabindex="3" auto-next></textarea>
Demo Fiddle
Can't you use jQuery in your textarea.html? If you can, then here's a solution:
$("textarea").keyup(function(event) {
if ($(this).val().length == $(this).attr("maxlength")) {
textfields = $("textarea");
curr = textfields.index(this);
next = textfields[curr + 1];
if (next != null) {
next.focus();
}
}
});
JSFiddle Link: http://jsfiddle.net/a2gtnmrm/

Resources