I am trying to change input value in particular web site from chrome extension. In order to do that I am using jQuery in my content script. It works in most of the cases, but I didn't manage to change value of the input when it is part of AngularJS view. I found the same problem when I use let say kendoUI. I am trying to set the value calling $('element').val('value') and then try to call blur and change event, but without any success.
I went through may be 99% of the posts related to this topic, but still can't find working solution.
You just need to Call $scope.apply() in order to let angular know about updating the bindings. This is mainly because by default, angular doesn't know anything about the changes you are making in jQuery.
Below is a sample code, and here is a jsFiddle. Hope this helps.
app.controller('testCtrl', ['$scope', function ($scope) {
$scope.changeValue = function() {
$('#test').val("new Value");
$scope.apply();
}
<div class="form-inline">
<input type="text" id="test" value="test">
<input type="submit" value="submit" ng-click="changeValue()">
</div>
Related
I am looking for a solution on passing data from a specific input text field to AngularJS. it may be a Javascript variable too. If the variable is changed from inside a javascript code it is not updating on AngularJS side. If i take the same variable and in the text field add at least one character or modify something i see variable updating and everything working as it should.
I tried something with angular.element(document.getElementById('ControllerElementID')).scope().funct(); but still no luck. When i update at least one field from the keyboard, all text fields that are related to "ng-model="sig.sigBase6422"" are updating properly as it should. If i call this updates through a JavaScript function i see updates only on specific text field and no updates at all on ng-model happening. How to make it updating as simple as possible? Below i will post a small example. I was able to store data from variable to a external file and in AngularJS read it from file and use it. this is way too long, complicated and ridiculous. I am sure there should be a better way.
Thank you!
<script type="text/javascript">
function addtext1() {document.getElementById("myID1").value = "1111111111111111";}
function addtext2() {document.getElementById("myID2").value = "2222222222222222";}
</script>
<div>
<form action="#" name="FORM1">
<TEXTAREA NAME="sigData" ng-model="sig.sigBase6422" ROWS="10" COLS="20">String: </TEXTAREA>
</form><br>
<input type="text" name="myID1" id="myID1" ng-model="sig.sigBase6422" ><br>
<input type="text" name="myID2" id="myID2" ng-model="sig.sigBase6422" ><br>
<p>Value {{sig.sigBase6422}}!</p>
</div>
<!-- test field -->
Add text 1
Add text 2
Indeed if you want to use AngularJS for what it was created, you have to rewrite your code completely using directive or controller. You variables and functions accessible from the view should be attached to the $scope too.
var myApp = angular.module("myApp", []);
myApp.controller("myCtrl", function($scope){
$scope.addtext1 = function () {
$scope.sig.sigBase6422 += "1111111111111111";
};
$scope.addtext2 = function () {
$scope.sig.sigBase6422 += "2222222222222222";
};
$scope.sig = {
sigBase6422: ""
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myCtrl">
<form action="#" name="FORM1">
<TEXTAREA ng-model="sig.sigBase6422" ROWS="10" COLS="20">String: </TEXTAREA>
</form><br/>
<input type="text" name="myID1" id="myID1" ng-model="sig.sigBase6422" /><br/>
<input type="text" name="myID2" id="myID2" ng-model="sig.sigBase6422" /><br/>
<p>Value {{sig.sigBase6422}}!</p>
<!-- test field -->
<button ng-click="addtext1()">Add text 1</button>
<button ng-click="addtext2()">Add text 2</button>
</div>
You seem to have misunderstood how angular works. What you're trying to do is not how angular works. What you're trying to do with native JavaScript can be done with angular. Angular can update dom and Dom updates angular as it's responsible for causing updates.... anyway without getting any deeper. You need to read more on how angular works and try sticl within the bounds of angular instead of mixing.
That being said :
Tigger change on the Dom element after you have updated its value. Or better yet get access to scope variable on the Dom and call a function in angular with the value you're and set they value from inside of a angular.
Use this code while updating the value.
pick the controller first using
var scope = angular.element(document.getElementById('yourControllerElementID')).scope();
scope.<variablename> = <your operation>;
then
scope.$apply();
the remaining thing will be taken care by Angular.
JSFiddle here: http://jsfiddle.net/c6tzj6Lf/4/
I am dynamically creating forms and buttons and want to disable the buttons if the required form inputs are not completed.
HTML:
<div ng-app="choicesApp">
<ng-form name="choicesForm" ng-controller="ChoicesCtrl">
<div ng-bind-html="trustCustom()"></div>
<button ng-repeat="button in buttons" ng-disabled="choicesForm.$invalid">
{{button.text}}
</button>
</ng-form>
</div>
JavaScript:
angular.module('choicesApp', ['ngSanitize'])
.controller('ChoicesCtrl', ['$scope', '$sce', function($scope, $sce) {
$scope.custom = "Required Input: <input required type='text'>";
$scope.trustCustom = function() {
return $sce.trustAsHtml($scope.custom);
};
$scope.buttons = [
{text:'Submit 1'},
{text:'Submit 2'}];
}]);
choicesForm.$invalid is false and does not change when entering text into the input field.
Solution:
I ended up using the angular-bind-html-compile directive from here: https://github.com/incuna/angular-bind-html-compile
Here is the relevant bit of working code:
<ng-form name="choicesForm">
<div ng-if="choices" bind-html-compile="choices"></div>
<button ng-click="submitForm()" ng-disabled="choicesForm.$invalid">
Submit
</button>
</ng-form>
And choices might be a snippit of HTML like this:
<div><strong>What is your sex?</strong></div>
<div>
<input type="radio" name="gender" ng-model="gender" value="female" required>
<label for="female"> Female</label><br>
<input type="radio" name="gender" ng-model="gender" value="male" required>
<label for="male"> Male</label>
</div>
The main problem is that ngBindHtml doesn't compile the html - it inserts the html as it is. You can even inspect the dynamic input and see that it doesn't have the ngModel's CSS classes (ng-pristine, ng-untouched, etc) which is a major red flag.
In your case, the form simply doesn't know that you've added another input or anything has changed for that matter. Its state ($pristine, $valid, etc) isn't determined by its HTML but by the registered NgModelControllers. These controllers are added automatically when an ngModel is linked.
For example this <input required type='text'> won't affect the form's validity, even if it's required, since it doesn't have ngModel assigned to it.
But this <div ng-model="myDiv" required></div> will affect it since it's required and has ngModel assigned to it.
The ngDisabled directive on your buttons works as expected since it depends on the form's $invalid property.
See this fiddle which showcases how ngModel registers its controller. Note that the html containing the dynamic input gets compiled after 750ms just to show how NgModelControllers can be added after FormController has been instantiated.
There are a few solutions in your case:
use a custom directive to bind and compile html - like this one
use ngInclude which does compile the html
use $compile to compile the newly added HTML but this is a bit tricky as you won't know exactly when to perform this action
This is an answer yet imcomplete because i cannot do the code at the moment.
I think your html will be included, not compiled. So the inputs are not bind to angular and are not part of the angular form object.
The only way i see is to use a directive that will compile the passed html and add it to your form. This may be quite tricky though, if you want to go on this way i suggest to edit your question to ask for the said directive.
However i'm not really familiar with $compile so i don't know if it'll work to just add $compile around $sce.trustAsHtml()
You can write a method as ng-disabled does not work with booleans, it works with 'checked' string instead:
So on your controller place a method :
$scope.buttonDisabled = function(invalid){
return invalid ? "checked" : "";
};
And on your view use it on angular expression :
<button ng-repeat="button in buttons" ng-disabled="buttonDisabled(choicesForm.$invalid)">
Here is a working fiddle
Working DEMO
This is the solution you are looking for. You need a custom directive. In my example I have used a directive named compile-template and incorporated it in div element.
<div ng-bind-html="trustCustom()" compile-template></div>
Directive Code:
.directive('compileTemplate', function($compile, $parse){
return {
link: function(scope, element, attr){
var parsed = $parse(attr.ngBindHtml);
function getStringValue() { return (parsed(scope) || '').toString(); }
//Recompile if the template changes
scope.$watch(getStringValue, function() {
$compile(element, null, -9999)(scope); //The -9999 makes it skip directives so that we do not recompile ourselves
});
}
}
});
I found the directive in this fiddle.
I believe what is really happening though due to jsfiddle I'm unable to dissect the actual scopes being created here.
<div ng-app="choicesApp">
<ng-form name="choicesForm" ng-controller="ChoicesCtrl">
<div ng-bind-html="trustCustom()"></div>
<button ng-repeat="button in buttons" ng-disabled="choicesForm.$invalid">
{{button.text}}
</button>
</ng-form>
</div>
The first div is your top level scope, your form is the first child scope. Adding the div using a function creates the dynamically added input field as a child of the first child, a grandchild of the top level scope. Therefore your form is not aware of the elements you're adding dynamically causing only the static field to be required for valid form entry.
A better solution would be to use ng-inclue for additional form fields or if your form isn't to large then simply put them on the page or template you're using.
I currently have a partial HTML that is being routed to, with a template and a custom Controller. The code snippet in my Angular template I would like to get working is:
<input type="text" typeahead=val for val in getValue($viewValue)>
However, it never enters into the function getValue(), while all other functions in my controller seem to be okay. When I take the typeahead out of the Angular template/partial, it seems to work. Why is this and how do I fix it?
You need to have an ng-model attribute to use the typeahead directive from AngularUI, even if you don't need to bind it to anything.
Change your markup to similar to the following:
<input type="text" ng-model="typeaheadVal" typeahead="val for val in getValue($viewValue)">
I'm a big fan of angularjs, I started lately to use it in all of my 'coding for fun' projects.
I have a big curiosity:
I have a two inputs, one disabled by a ng-disabled directive and the other disabled with an html tag (A better illustration in this link):
//...
<input type="text" disabled value="This is an html input text disabled" />
<input type="text" ng-disabled="true" value="disabled with angular js directive" />
//...
Using the browser ability I can right click on the input and remove the disabled and ng-disabled tags but only the one with the disabled tag would be editable, the other one will still be tracked by angular even when ng-disabled directives has been removed.
So, When and Why should I prefer using ng directives over native html tags? Which could be the impact of letting angular track all these actions? is it really worth to use it everywhere?
Use the native html 'disabled' if the element should always be disabled. (static, for example if you want to provide an input with text and never let the user change it)
Use angular if it should change based on variables value in the scope.
For example, say a button should change the state of an input.
<input type="button" ng-click="inpDisabled = true" >Disable Input</input>
<input type="text" ng-disabled="inpDisabled" />
live example
No harm will come if you still use ng-disabled="true" but it's redundant.
If you want to make directive static, you shoud use native html
<your-tag disable><your-tag>
against
<your-tag ng-disabled="true"><your-tag>
But AngularJS does not work this way, you shoud initialize your application and controller, then pass a variable as parameter to your directive:
JS:
$scope.isDisabled = true;
HTML:
<your-tag ng-disabled="isDisabled"><your-tag>
You shoud read more tutorials to make things clear
I have this piece of html code in my application (the ng-app and ng-controller values are defined before):
<div>
<label for="projectSearchDateFrom"><%= Res("Projects.Search.From")%></label>
<input id="projectSearchDateFrom" type="text" ng-model="startDate" ui-date="dateOptions"/>
<img ng-show="hasStartDate()" ng-click="clearStartDate()" src="/_Layouts/ClientPortal/Images/layout/TA/delete-small.png" alt="<%= Res("ToolbarDialog.Clear")%> <%= Res("Projects.Search.From")%>" title="<%= Res("ToolbarDialog.Clear")%>" />
</div>
My AngularJS controller looks like this:
function ProjectSearchCtrl($scope) {
$scope.startDate = '';
$scope.hasStartDate = function () {
return $scope.startDate != '';
};
$scope.clearStartDate = function () {
$scope.startDate = '';
};
$scope.dateOptions = {
dateFormat: "yy-mm-dd",
showOn: "focus"
};
}
This works perfectly: I have a datepicker set up correctly thanks to AngularUI, the AngularJS binding works...
But if I change the showOn value to "button" or "both" (the two possible options which will actually show the datepicker button), everything after the input (containing the ui-date attribute) stops working: ng-show, ng-click... The controller doesn't even get called.
Versions (all is up-to-date):
jQuery 1.7.2
angularJS 1.0.0
angularUI 0.1.0
Chrome 20
Please take a look at this line in the Select2 directive. This is a note to ANYONE writing a directive / implementing a plugin in AngularJS (not just AngularUI):
Any plugin that injects a new DOM element immediately after the linked element runs the risk of disrupting the compiler. The reason is because the way AngularJS works, it caches the index of each DOM element at compile time, and then makes a second pass upon linking. When you inject new DOM, you offset the index of all siblings immediately after the directive.
For this reason, I've been forced to wrap both TinyMCE and Select2 in a setTimeout so that the DOM is injected after the linking is done. Note that I don't bother using $timeout because I really don't need/want $apply() to fire just to turn on the plugin, as there are already callbacks in place that do this when the plugin changes the data.
I'll look into making sure this is uniform across AngularUI. Unfortunately, there appears to be no elegant solution to this problem in AngularJS at this time, however it's a problem I've been thinking about for some time and am constantly looking for a better solution towards.
Read this Google Groups post for more information about compiling vs linking: https://groups.google.com/forum/?fromgroups#!searchin/angular/compile$20link/angular/RuWn5W3Q5I0/KJhcQJ_RNsIJ
You can also open a bug ticket on the AngularUI project in the future.
As suggested by Pete BD in his comment on the question, there is some kind of bug/unwanted behaviour in the way that jQueryUI and angularJS interact. A workaround is to wrap the input control in a div.
<div class="date">
<label for="projectSearchDateFrom"><%= Res("Projects.Search.From")%></label>
<div>
<input id="projectSearchDateFrom" type="text" ng-model="startDate" ui-date="dateOptions"/>
</div>
<img class="clear" ng-show="hasStartDate()" ng-click="clearStartDate()" src="/_Layouts/ClientPortal/Images/layout/TA/delete-small.png" alt="<%= Res("ToolbarDialog.Clear")%> <%= Res("Projects.Search.From")%>" title="<%= Res("ToolbarDialog.Clear")%>" />
</div>
Now I can use showOn "both" or "button".
This is fixed in the latest release!