I have some code which adds new input and I need after some time for example 3 seconds input to hide. Problem is how to hide each separate input after 3 seconds of display each.
In html code I have:
id="{{ 'inputNum-' + $id }}"
In Javascript:
$timeout(function () {
document.getElementsById('commentNum-' + $id).css('display', 'none');
}, 3000);
https://jsfiddle.net
If you really want to manipulate DOM, the AngularJS-way to do this is to write your custom directive:
(function(){
'use strict';
angular
.module('inputsApp', [])
.controller('InputsController', InputsController)
.directive('hideMe', ['$timeout', function ($timeout) {
return {
link: function (scope, element, attrs) {
var timeOut = $timeout(function () {
angular.element(element).css('display', 'none');
}, new Number(attrs.hideMe));
scope.$on('$destroy', function(){
if (timeOut) $timeout.cancel(timeOut);
});
}
}
}])
InputsController.$inject = ['$scope', '$timeout'];
function InputsController($scope, $timeout) {
var vm = this;
// Current input.
vm.input = {};
// Array where inputs will be.
vm.inputs = [];
// Run when input is submited.
vm.addInput = function() {
vm.inputs.push( vm.input );
vm.input = {};
// Reset clases of the form after submit.
$scope.form.$setPristine();
}
}
})();
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script><div class="inputs-app" ng-app="inputsApp" ng-controller="InputsController as cmntCtrl">
<div class="inputs">
<!-- Comment -->
<div class="input" hide-me="2000" ng-repeat="input in cmntCtrl.inputs" id="{{ 'inputNum-' + $id }}">
<!-- Comment Box -->
<div class="input-box">
<div class="input-text">{{ input.text }}</div>
</div>
</div>
</div>
<!-- From -->
<div class="input-form">
<form class="form" name="form" ng-submit="form.$valid && cmntCtrl.addInput()" novalidate>
<div class="form-row">
<textarea
class="input"
ng-model="cmntCtrl.input.text"
placeholder="Add input..."
required></textarea>
</div>
<div class="form-row">
<input type="submit" value="Add input">
</div>
</form>
</div>
</div>
I would use Angular's binding system.
add a property
vm.showCommentBox = true;
change to set the boolean
$timeout(function () {
vm.showCommentBox = false;
}, 3000);
add the binding to your html
<div class="input-box" ng-if="showCommentBox">
You need to think Angular way here: instead of modifying the UI directly, modify the model instead - and let framework do work for you. For example:
vm.addInput = function() {
var inputToAdd = vm.input;
vm.inputs.push(inputToAdd);
$timeout(function () {
var indexOfInput = vm.inputs.indexOf(inputToAdd);
vm.inputs.splice(indexOfInput, 1);
}, 3000);
vm.input = {};
// ... the rest of code
}
Demo. And if you don't actually those inputs to be dropped from the list, again, modify their attributes.
JS:
$timeout(function () {
var indexOfInput = vm.inputs.indexOf(inputToAdd);
vm.inputs[indexOfInput].hidden = true;
}, 3000);
Template:
<div class="input" ng-hide="input.hidden"
ng-repeat="input in cmntCtrl.inputs" id="{{ 'inputNum-' + $id }}">
Demo. With this approach, all the items are still in DOM (so it's exactly equivalent to what you tried to do). You might probably prefer using ng-if instead of ng-hide, dropping them from DOM completely.
General advice with Angular is doing things Angular way (it's convention over configuration framework).
And the Angular way would be to bind visibility with ng-show and ng-hide or existence in DOM via ng-if and then MODIFY THE MODEL NOT influence the DOM directly.
Alternatively, you can alter between element having a class and not with ng-class. And then separate styling in CSS file.
Depending on what you need: (choose class names yourself)
.hidden{
visibility: hidden;
}
or
.not-displayed{
display: none;
}
Related
Is there a clean way to access the element where the ng-disabled property is attached to inside the function that is given to it?
Html element:
<input class="needsDisabling" ng-disabled="isFieldDisabled()" type="text">
isFieldDisabled function in controller:
How can I access the element where the ng-disabled prop is attached to in this function ?
$scope.isFieldDisabled = function () {
// How can I access the element where the ng-disabled prop is attached to in this function ?
// if (element has class "needsDisabling")
// return true;
}
I am looking for another way then passing the element as a parameter
in the function.
JSFiddle
ngDisabled was designed to work with your model (#2) and does not allow to manipulate your DOM elements manually in your controllers (which is considered a bad practice in angularjs). But anyway you can still create your custom directive and manipulate disabled property manually (#1):
angular.module('myApp',[])
.directive('myDisabled', [function () {
var myDisabled = {
restrict: 'A',
scope: false,
link : function(scope, element, attrs, ngModelCtrl){
scope.$watch(function() {
return scope.$eval(attrs.myDisabled).apply(scope, element);
}, function(val) {
element.prop('disabled', val);
});
}
}
return myDisabled;
}])
.controller('MyCtrl', ['$scope', function MyCtrl($scope) {
$scope.isFieldDisabled = function (element) {
return angular.element(element).hasClass('disabled');
}
$scope.inputs = [{}, { disabled: true }, {}];
$scope.isInputDisabled = function (input){
return !!input.disabled;
};
}]);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://code.angularjs.org/1.6.4/angular.js" ></script>
<div ng-app="myApp">
<div ng-controller="MyCtrl">
<h2>#1</h2>
<input class="needsDisabling disabled" my-disabled="isFieldDisabled" type="text"/>
<input class="needsDisabling" my-disabled="isFieldDisabled" type="text"/>
<input class="needsDisabling disabled" my-disabled="isFieldDisabled" type="text"/>
<h2>#2</h2>
<div>
<input type="text" ng-repeat="input in inputs" ng-disabled="isInputDisabled(input)" ng-model="input.value"/>
</div>
</div>
</div>
You cannot pass the element to the controller's function in angularjs, but why not take advantage of the ng-class directive as your class and disabled values depend on each other?
ng-class="{needsDisabling: isDisabled}" ng-disabled="isDisabled"
I cannot think of a reason why you should not do it like this because;
If the class is not dynamic then there is no need for ng-disabled.
You could just use disabled="disabled" but I guess that is not the reason.
If the value of class IS dynamically switched outside of angular's
knowledge, then that part of the solution is problematic and should
be changed.
Another reason to deal with this issue in angularjs itself is to create your own directive as someone here pointed out.
I have a form in my Angular app where users can enter in multiples of the same field values using an add button.
<md-content layout-padding="">
<div>
<form name="userForm">
<div layout="row" ng-repeat="person in persons">
<md-input-container flex>
<label>First name</label>
<input ng-model="person.firstName">
</md-input-container>
<md-input-container flex>
<label>Last Name</label>
<input ng-model="person.lastName">
</md-input-container>
</div>
<md-button class="md-raised" ng-click="addAnother()">Add Another</md-button>
</form>
</div>
</md-content>
Working Codepen
So already a couple of inputs are populated, clicking the button adds a new object in order for a new set of inputs to appear.
What I would like is for the first input on the newly added row to receive focus so the user can start entering in right away.
Is there some way to set the tabIndex programatically?
Check this out. The great answer on your question you can find here: Input autofocus attribute
angular.module('ng').directive('ngFocus', function($timeout) {
return {
link: function ( scope, element, attrs ) {
scope.$watch( attrs.ngFocus, function ( val ) {
if ( angular.isDefined( val ) && val ) {
$timeout( function () { element[0].focus(); } );
}
}, true);
element.bind('blur', function () {
if ( angular.isDefined( attrs.ngFocusLost ) ) {
scope.$apply( attrs.ngFocusLost );
}
});
}
};
});
<input type="text" ng-focus="isFocused" ng-focus-lost="loseFocus()">
I have noticed you make use of a class named md-input-focused
Method 1(the best)
This is the most simplest & elegant solution to your problem. Making use of $last in angular. Add a class condition on your <md-input-container> First name Codepen like so:
<md-input-container flex ng-class="{'md-input-focused': $last}">
<label>First name</label>
<input ng-model="person.firstName">
</md-input-container>
This method requires no additional javascript changes, but will only add the focus & not make the input active. See below method if you want to make the input active.
Method 2
Check the codepen. This is the same, but the javascript way of adding the same class dynamically
FYI: JQuery needed for the below statement. Hope you have that.
This is your magic statement. Codepen
$('.layout-row:last-of-type').children().first().find('input').focus();
Add this onto your $scope.addAnother function inside a timeout(very important).
Your whole function should look like
$scope.addAnother = function() {
$scope.persons.push({});
setTimeout(function(){
$('.layout-row:last-of-type').children().first().find('input').focus();
}, 500);
}
You could even make use of the angular $timeout instead of the window setTimeout
Here you go - CodePen
Markup
<input ng-model="person.firstName" auto-focus="true">
Directive
// Inspired by Mark Rajcok's answer - http://stackoverflow.com/a/14837021/782358
.directive('autoFocus', function($timeout) {
return {
scope: { trigger: '#autoFocus' },
link: function(scope, element) {
scope.$watch('trigger', function(value) {
if (value.toString() === "true") {
$timeout(function() {
element[0].focus();
});
}
});
}
};
});
I am creating one form using Bootstrap & AngularJS. I am using CK editor in my page as textarea. But I am not able to retrieve the value of the textarea while the value of the input text field is easily captured in my AngularJS controller. Following is the code snippet:
HTML page:
<div class="container">
<div ng-controller="controller">
<form role="form">
<label for="sd"><b>Short Description: </b></label>
<input ng-model="sdesc" class = "form-control input-xxlarge" type = "text" placeholder ="Provide a short description here."/>
<br/>
<label for="dt"><b>Details: </b></label>
<textarea ng-model="details" class="form-control" name="details_editor" id="details_editor"></textarea>
<br/>
<button class = "btn btn-primary" ng-click="submitted()">Ask It!</button>
<script>
CKEDITOR.replace('details_editor');
</script>
</form>
</div>
<br/>
<hr>
</div>
JS
app.controller('controller', ['$scope', function($scope){
$scope.submitted = function(){
var sdesc = $scope.sdesc;
var details = $scope.details;
alert($scope.details);
};
}]);
The alert shows undefined for the text area value.
Please help me solve the issue.
You are using the plain Javascript version of CK editor and hence Angular is not getting notified to update the ng-model value of that textarea.
Basically, Angular runs a digest cycle to update all views and models but since in this case the values being changed in the CK editor is happening outside the Angular.s context which is not updating the ng-model value.
To fix this, we added a small directive and notifying the change in the ng-model to the Angular by using the $timeout. (We can also use the $apply directive, but it may fail sometimes if the digest cycle is already in progress)
Example directive:
var app = angular.module("your-app-name", []);
app.directive("ckEditor", ["$timeout", function($timeout) {
return {
require: '?ngModel',
link: function ($scope, element, attr, ngModelCtrl) {
var editor = CKEDITOR.replace(element[0]);
console.log(element[0], editor);
editor.on("change", function() {
$timeout(function() {
ngModelCtrl.$setViewValue(editor.getData());
});
});
ngModelCtrl.$render = function (value) {
editor.setData(ngModelCtrl.$modelValue);
};
}
};
}]);
Remove, your following code:
<script>
CKEDITOR.replace('details_editor');
</script>
And, modify your text-editor like:
<textarea ng-model="details" class="form-control" ck-editor name="details_editor" id="details_editor"></textarea>
I found ng-ckeditor to implement ckeditor in angularjs.
Please refer this :https://github.com/esvit/ng-ckeditor. I tried it, It is easy to implement and working as expected
It is possible make the required value dependet of some funcion?
Something like this? I want to do this because I want to change the required attribute to some form inputs...
HTML:
Name: <input type="text" ng-model="user.name" ng-required="isRequired('name')" />
Age: <input type="text" ng-model="user.age" ng-required="isRequired('age')" />
JS:
$scope.isRequired(fieldName){
$scope.requiredFields = [];
//$scope.requiredFields = STUFF FROM SOME REST SERVICE
for (i in requiredFields) {
if (requiredFields[i] == fieldName){
return true;
}
}
return false;
}
Updated Answer:
So based on your updated OP, what you want is certainly doable. The problem with what you were trying to do is that ng-required has no ability to execute a function, it only reads a boolean. But we can dynamically create variables based on data from the server to automatically set fields to required:
Updated Plunker
<form>
Name: <input type="text" ng-model="user.test" ng-required="name" /><br/>
<input type="text" ng-model="user.name" ng-required="age" />
<br/>
<button type="submit">Submit</button>
</form>
Note that I put a $scope property for each input in the ng-required attribute. Now we can dynamically create that $scope property and set it to true if our data says we need to:
$scope.isRequired = function(){
$scope.requiredFields = [];
$http.get('fields.json')
.success(function(data){
$scope.requiredFields = angular.fromJson(data);
console.log($scope.requiredFields.required)
for (i = 0; i < $scope.requiredFields.required.length; i++) {
$scope[$scope.requiredFields.required[i]] = true
}
console.log($scope[$scope.requiredFields.required[0]]);
})
//$scope.requiredFields = STUFF FROM SOME REST SERVICE
}
$scope.isRequired()
So it is iterating over an array of required fields received from the server, and then dynamically creating a $scope property for each one that is required, and setting it to true. Any field that has that $scope property in it's ng-required will be required now. Anything not dynamically created will just return false, and ng-required doesn't trigger.
Original answer:
Plunker
As Pratik mentioned, ng-required only accepts a Boolean value, but we can toggle the value of that with a function.
HTML
<form>
Name: <input type="text" ng-model="user.name" ng-required="isRequired" />
<br/><button ng-click="toggle()">Required: {{isRequired}}</button>
<button type="submit">Submit</button>
</form>
code:
$scope.isRequired = true;
$scope.toggle = function() {
$scope.isRequired = !$scope.isRequired;
}
I know this is a couple of years old and so AngularJS may have changed, but the accepted answer as it stands today isn't correct. You can very easily execute a function within ng-required, as it takes an expression, which can be a function. For example:
index.html
<div ng-controller="ExampleController" class="expressions">
Expression:
<input type='text' ng-model="expr" size="80"/>
<button ng-click="addExp(expr)">Evaluate</button>
<ul>
<li ng-repeat="expr in exprs track by $index">
[ X ]
<code>{{expr}}</code> => <span ng-bind="$parent.$eval(expr)"></span>
</li>
</ul>
</div>
script.js
angular.module('expressionExample', [])
.controller('ExampleController', ['$scope', function($scope) {
var exprs = $scope.exprs = [];
$scope.expr = '3*10|currency';
$scope.addExp = function(expr) {
exprs.push(expr);
};
$scope.removeExp = function(index) {
exprs.splice(index, 1);
};
}]);
In script.js, a function addExp is defined and added to the scope, and then it's called in the ng-click directive of the a tag, which also takes an expression as its argument.
This code is taken directly from the AngularJS documentation on expressions. It doesn't use ng-require directly, but any directive that takes an expression will work the same. I have used the same syntax to use a function for ng-require.
Well in other cases i will get datepicker binded to my textbox which will be straight forward but not in this case .
Fiddle link : http://jsfiddle.net/JL26Z/1/ .. while to setup perfect seanrio i tried but unable to bind datepicker to textboxes . except that everything is in place
My code :
**<script id="Customisation" type="text/html">** // here i need to have text/html
<table style="width:1100px;height:40px;" align="center" >
<tr>
<input style="width:125px;height:auto;" class="txtBoxEffectiveDate" type="text" id="txtEffective" data-bind="" />
</tr>
</script>
The above code is used for my dynamic generation of same thing n no of time when i click each time on a button . So above thing is a TEMPLATE sort of thing .
My knockout code :
<div data-bind="template:{name:'Customisation', foreach:CustomisationList},visible:isVisible"></div>
<button data-bind="click:$root.CustomisatioAdd" >add </button>
I tried same old way to bind it with datepicker
$('#txtEffective').datepicker(); // in document.ready i placed
Actually to test this i created a textbox with some id outside script with text/html and binded datepicker to it and It is working fine sadly its not working for the textbox inside text/html and i want to work at any cost.
PS: well i haven't posted my view model as it is not required in this issue based senario
View model added with Js
var paymentsModel = function ()
{
function Customisation()
{
var self = this;
}
var self = this;
self.isVisible = ko.observable(false);
self.CustomisationList = ko.observableArray([new Customisation()]);
self.CustomisationRemove = function () {
self.CustomisationList.remove(this);
};
self.CustomisatioAdd = function () {
if (self.isVisible() === false)
{
self.isVisible(true);
}
else
{
self.CustomisationList.push(new Customisation());
}
};
}
$(document).ready(function()
{
$('#txtEffective').datepicker();
ko.applyBindings(new paymentsModel());
});
Any possible work around is appreciated
Regards
The best way I've found to do this is create a simple bindingHandler.
This is adapted from code I have locally, you may need to tweak it...
** code removed, see below **
Then update your template:
** code removed, see below **
By using a bindingHandler you don't need to try to hook this up later, it's done by knockout when it databinds.
Hope this is helpful.
EDIT
I created a fiddle, because I did indeed need to tweak the date picker binding quite a lot. Here's a link to the Fiddle, and here's the code with some notes. First up, the HTML:
<form id="employeeForm" name="employeeForm" method="POST">
<script id="PhoneTemplate" type="text/html">
<div>
<span>
<label>Country Code:</label>
<input type="text" data-bind="value: countryCode" />
</span>
<span><br/>
<label>Date:</label>
<input type="text" data-bind="datepicker: date" />
</span>
<span>
<label>Phone Number:</label>
<input type="text" data-bind="value: phoneNumber" />
</span>
<input type="button" value="Remove" data-bind="click: $parent.remove" />
</div>
</script>
<div>
<h2>Employee Phone Number</h2>
<div data-bind="template:{name:'PhoneTemplate', foreach:PhoneList}">
</div>
<div>
<input type="button" value="Add Another" data-bind="click: add" />
</div>
</div>
</form>
Note I removed the id=... from in your template; because your template repeats per phone number, and ids must be unique to be meaningful. Also, I removed the datepicker: binding from the country code and phone number elements, and added it only to the date field. Also - the syntax changed to "datepicker: ". If you need to specify date picker options, you would do it like this:
<input type="text" data-bind="datepicker: myObservable, datepickerOptions: { optionName: optionValue }" />
Where optionName and optionValue would come from the jQueryUI documentation for datepicker.
Now for the code and some notes:
// Adapted from this answer:
// https://stackoverflow.com/a/6613255/1634810
ko.bindingHandlers.datepicker = {
init: function(element, valueAccessor, allBindingsAccessor) {
//initialize datepicker with some optional options
var options = allBindingsAccessor().datepickerOptions || {},
observable = valueAccessor(),
$el = $(element);
// Adapted from this answer:
// https://stackoverflow.com/a/8147201/1634810
options.onSelect = function () {
if (ko.isObservable(observable)) {
observable($el.datepicker('getDate'));
}
};
$el.datepicker(options);
// set the initial value
var value = ko.unwrap(valueAccessor());
if (value) {
$el.datepicker("setDate", value);
}
//handle disposal (if KO removes by the template binding)
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
$el.datepicker("destroy");
});
},
update: function(element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor()),
$el = $(element);
//handle date data coming via json from Microsoft
if (String(value).indexOf('/Date(') === 0) {
value = new Date(parseInt(value.replace(/\/Date\((.*?)\)\//gi, "$1")));
}
var current = $el.datepicker("getDate");
if (value - current !== 0) {
$el.datepicker("setDate", value);
}
}
};
function Phone() {
var self = this;
self.countryCode = ko.observable('');
self.date = ko.observable('');
self.phoneNumber = ko.observable('');
}
function PhoneViewModel() {
var self = this;
self.PhoneList = ko.observableArray([new Phone()]);
self.remove = function () {
self.PhoneList.remove(this);
};
self.add = function () {
self.PhoneList.push(new Phone());
};
}
var phoneModel = new PhoneViewModel();
ko.applyBindings(phoneModel);
Note the very updated binding handler which was adapted from this answer for the binding, and this answer for handling onSelect.
I also included countryCode, date, and phoneNumber observables inside your Phone() object, and turned your model into a global variable phoneModel. From a debugger window (F12 in Chrome) you can type something like:
phoneModel.PhoneList()[0].date()
This will show you the current value of the date.
I notice that your form is set up to post somewhere. I would recommend instead that you add a click handler to a "Submit" button and post the values from your phoneModel using ajax.
Hope this edit helps.
Dynamic entities need to have datepicker applied after they are created. To do this I'd use an on-click function somewhere along the lines of
HTML
<!-- Note the id added here -->
<button data-bind="click:$root.CustomisatioAdd" id="addForm" >add </button>
<script>
$(document).on('click', '#addForm', function(){
$('[id$="txtEffective"]').datepicker();
});
</script>