How to mix struts1 with angularjs to auto populate a field - javascript

I'm building a jsp page with struts1 (i cannot use struts2) where i have a field i'm auto populating with a value coming from the form, but the user is free to overwrite anything in that text field. I have also used AngularJS to be able to give options while the user types (google-like search) when they are overriding the value pre-populated by struts.
My problem is that the tags ng-model and uib-typeahead are not recognized by the html:text tag in struts. If i switch to a plain html input tag then the angular feature works but now i cannot pre-populate the field with struts.
script.js:
angular.module('plunker', ['ui.bootstrap','ngAnimate']).controller('TypeaheadCtrl', function($scope, $http) {
$scope.selected = undefined;
// Any function returning a promise object can be used to load values asynchronously
$scope.getLocation = function(val) {
return $http.get('http://maps.googleapis.com/maps/api/geocode/json', {
params: {
address: val,
sensor: false
}
}
).then(function(response){
return response.data.results.map(function(item){
return item.formatted_address;
});
});
};
});
mypage.html
<body ng-controller="TypeaheadCtrl">
<div class='container-fluid typeahead-demo' >
<div class="section">
<!-- OPTION 1: angular works and gives suggestions, but the field doesn't get pre-populated by struts-->
<input type="text" name="city" id="city" class="gui-input" ng-model="asyncSelected" uib-typeahead="address for getLocation($viewValue)" placeholder="Type city name">
<html:hidden property="city"/>
<!-- OPTION 2: auto-populates value but it can't compile with ng-model and typegead tags -->
<html:text property="city" styleClass="gui-input" styleId="cityStyle" />
</div>
</div>
Can someone please tell me what the best way or appropriate way of doing this is? how do i mix the functionality of Option 1 and Option 2?
Thanks in advance.

I already resolved it by setting the value of the field using jquery on load, but if someone has a better solution or better practice to do this, i'd like to know.

Related

AngularJS passing data to Javascript variables and opposite

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.

Angular js on input type change render list data from HTTP request

I am stuck with minor issue.
I want to render list of data from api call, on input type change event.
What my app does is, when user start typing in input type, onchange event is triggered, based on that, I want to return data from api call. So that I can give autosuggestion and let user select that data.
Basically its same like how google place autosuggest work. I just want to customize that in my own list view.
<ion-content ng-controller="Googleplacesuggestion">
<h1>Search</h1>
<label class = "item item-input">
<input type ="text" ng-model="search" ng-change="getGooglePlaceSuggestionAutocomplete(search)"
class="search-query" id="address_search" placeholder="Search">
</label>
<ul class="unstyled">
<li ng-repeat="data in returnedData">
<span> {{ data }} </span>
</li>
</ul>
this is my controller
.controller('Googleplacesuggestion',function($scope,$http){
$scope.getGooglePlaceSuggestionAutocomplete = function($scope){
console.log($scope);
$scope.returnedData = [
{text:'learn angular', done:true},
{text:'build an angular app', done:false}
];
Thanks
Better to post your code but here I would suggest based on my understanding. onchange event callback is from Javascript and is an Asynchronous from which Angular is unaware of so in your callback, wrap the code inside the function in the $timeout(function() {}) method call which will tell Angular that something has changed.
For example:
<input type="text" onchange="valueChanged()" />
In your controller:
$scope.valueChanged = function() {
$timeout(function() {
/// your code
});
};
Another best option which is recommended to use ng-change directive of Angular, in this way you don't have to wrap your code inside the $timeout service.
Also, there are various libraries out there which provide your autocomplete code. https://angular-ui.github.io/bootstrap/#/typeahead is one of them if you are using BootStrap.
search something about debouncing this is technique to wait some time before doing some job, or super easy way to check string length

Disabling submit button based on fields added with ng-bind-html

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.

Edge-case using ng-model in angularjs forms

I have an annoying edge case that I'm dealing with and would love some help.
The situation:
I'm working on an angular app that is a 'theme' for a donation platform; the donation platform allows us to host theme files (images, stylesheets, javascript, html markup, etc.)
the platform uses liquid templating to allow use of CSRF tokens as well as to prevent some XSS nastiness.
the interpolation provider for angular was changed to // some.expression // so it wouldn't interfere with the liquid brackets {{}}
so: a 99:1 mix of html/angular and some liquid.
using angular 1.2.27
The problem:
the liquid tags also enable us to read data back from the server if a user submits a form and there's an error in it. e.g.: when a user puts their card number in but forgets a field, we can use liquid tags like {{ user.first_name }} to safely access that info and populate the value attributes of fields when the form gets reloaded. That way, users don't face a blank form with errors at the top.
for example:
<input ng-blur="validateFName=true" ng-required="true" placeholder="First name" type="text" name="donation[first_name]" ng-model="donation.first_name" value="'{{ donation.first_name }}'">
becomes:
<input ng-blur="validateFName=true" ng-required="true" placeholder="First name" type="text" name="donation[first_name]" ng-model="donation.first_name" value="John"> the value attr does get populated, angular is just ignoring it (and that's the intended use, of course)
however, because of the way that an ng-model attribute will work, even though the form elements get their value attrs populated, ng-model ignores it and the fields are effectively blank (to the user, anyways)
So: is there any way around this? Essentially, I'm looking for a way to populate the ng-models with the value attribute of their respective field or have ng-model acknowledge the value attribute. I've looked into ng-bind, -init, -value, and others, but most are for more 'normal' usage of angular. And yes, I know this isn't the best way to utilize angular, but I'm bound in this case by my requirements. Any help would be greatly appreciated. Thanks!
I would try creating a directive that watches the value attribute and updates ng-model on changes.
.directive('value', function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, element, attr, ctrl) {
attr.$observe('value', function(val) {
ctrl.$setViewValue(val);
}
}
}
})
Another option might be to populate the data on the model side in your Javascript instead of on the template?
Instead of
<input ng-blur="validateFName=true"
ng-required="true"
placeholder="First name"
type="text"
name="donation[first_name]"
ng-model="donation.first_name"
value="'{{ donation.first_name }}'">
What if you had in your controller
...
$scope.donation.first_name = "'{{ donation.first_name }}'";
...

Using AngularJS to style label when ng-model changes in a text field

A few days back, I asked the following question:
I've searched for how to do this, and I've not had any luck. I'm fairly inexperienced with web stuff, so perhaps it's so trivial that no one needs to ask how to do it :(
Suppose I have an HTML text input field with a label, like this:
<label for = "stuff">Stuff</label>
<input type = "text" name = "stuffz" id="stuff" value = "hello!">
Now suppose the text input field value is changed. Is there a way to use AngularJS to restyle the label (Like, turn it green, for example) when this change occurs? I've looked into using ng-change and ng-class, but I'm not knowledgeable enough about how these work to use them in this manner.
When I tested the solution provided, which was:
CSS
.marvellous {
color: green;
}
HTML
<div ng-app="demo">
<label for="stuff" ng-class="{ 'marvellous' : !!hasChanged }">Stuff</label>
<input type="text" id="stuff" ng-model="myModel" ng-change="hasChanged = true"></div>
It worked, but only when I manually changed the text field (i.e. I typed stuff in the text field directly). However, in the particular application I'm working on, I need for the labels to be restyled when the value stored in ng-model changes. Unfortunately, I falsely assumed that if this method worked when I manually changed the text field, it must work if ng-model were to change as well. I've come to find out that it doesn't.
What's the reason for this? And how can I make the label re-style when ng-model changes?
Thanks!
EDIT: When I say "ng-model changes," what I mean is..in my controller, there is a variable that is used to populate the text fields of the app that I'm working on. However, when the user clicks an "import changes" button, this variable is changed according to the changes that they are importing, which consequently changes the corresponding text fields linked to that variable. Ultimately, I want all of the labels attached to these changed text fields to be highlighted for the user to see. I'm sorry for my vagueness.
Each input in an angular js form has meta data properties to help you. For example
<form id="form">
<label for="stuff" ng-class="{ 'marvellous' : form.stuff.$dirty}">Stuff</label>
<input type="text" id="stuff" ng-model="myModel" ng-change="hasChanged = true">
</form>
https://docs.angularjs.org/api/ng/type/form.FormController
You can achieve that using $scope.$watch :
function demoCtrl ($scope) {
$scope.$watch('myModel', function (newValue, oldValue) {
if (newValue) {
$scope.hasChanged = true;
}
});
$scope.changeMyModel = function () {
$scope.myModel = 'wonderful';
};
}
.marvellous {
color: green;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app ng-controller="demoCtrl">
<label for="stuff" ng-class="{ 'marvellous' : !!hasChanged }">Stuff</label>
<input type="text" id="stuff" ng-model="myModel" ng-change="hasChanged = true">
<button ng-click="changeMyModel()">change model</button>
</div>
<!--Use ng-style !!! replace your lable with this--->
<label for="stuff" ng-style="hasChanged()">Stuff</label>
and define your function like this below---
$scope.hasChanged = function(){
if($scope.myModel !== initValue){
return { color: "green" }
}
}

Categories

Resources