Angular 1.5 Radio Button Model - javascript

I have a radio button on a page whose ng-value is being set to an "answer" object. The model for that variable is being set to a particular element of an affiliations array. The structure of data looks something like this:
var user = { affiliations: [ {question: ..., answers: ... }, ... ] }
In this way, each "affiliation" has a question and one or more answers. I'm trying to create a UI for this in AngularJS like so:
<form name='affiliationsForm'>
<div ng-repeat="(i, affiliation) in DS_OrgAffiliations">
<h6>{{affiliation.question.name}}</h6>
<div ng-if="affiliation.question.questionType=='MultipleAnswer'">
...
</div>
<div ng-if="affiliation.question.questionType=='SingleAnswer'">
<label ng-repeat="answer in affiliation.answers">
<div>
<input type="radio" ng-model="$parent.DS_User.affiliations[i].answers" name="{{affiliation.question.name}}" ng-value="answer"> {{answer.answer}} </input>
</div>
</label>
</div>
</div>
<button data-ng-click="submitAffiliations()">save</button>
On my model, this actually works quite well. I have a little glue code that handles the differences between multiple choice questions and single questions, and if I make a change to this form, the model reflects the change without issue.
The problem is that when the form is initialized on the page, the correct radio button is never checked. The model will show an answer object inside the 'answers' property, but the form doesn't reflect that value. What's more, selecting a radio button assigns the correct answer to our model, and if I select what should be the active radio button, the model appears to not change.
I think what is happening is that the model is being initialized with a deep copy of the correct 'answer' object, rather than a shallow copy, and the fact that this reference is different is causing an issue, despite the objects having the same content.
Is this correct? To test this theory, I did this:
<input type="radio" ng-model="$parent.DS_User.affiliations[i].answers" name="{{affiliation.question.name}}" ng-value="answer" ng-checked="$parent.DS_User.affiliations[i].answer.answer==answer.answer"> {{answer.answer}} </input>
Notice that the ng-checked attribute is checking a member of the answers to ensure similarity. Unfortunately, this also fails to default the correct radio button on page load.
Any insight on this? I've spent about an hour and a half playing with this, and my references explanation seemed likely, but I haven't been able find a thread to pull on since the ng-checked attribute failed me.

So I spent a little more time with this and put together a simple fiddle. As it turns out, Angular only considers references when storing objects in ng-value, so my suggestion was actually right on. When storing primitives, it's possible to initialize the model with a different value, but when storing objects, you need to store a reference to the actual model entry!

Related

Setting a dynamic drop down value in JavaScript

I've been working on this issue for days and feel like I'm at a dead end so hoping someone can help out.
I have a form that's used to log calls. The form has two drop downs Reason and Resolution which are created using an array.
When a call is dropped for whatever reason I want the user to click a button called Lost Call and have it fill out the form with specific information.
It works for every field but the Resolution field. I can't get that one to populate.
The lost call button calls a function using onClick.
<Input type="button" value="Lost Call" onClick="LostCall()" />
All my code is HTML5 and JavaScript.
Here is my HTML code:
<select id="Reason"><option value=" "></option></select>
<select id="Resolution"><option value=" "></option></select>
The script I use to create the drop downs I got from here:
http://jsfiddle.net/bdhacker/eRv2W/
Other then changing the Variable names to suit my form and less options the code is the same.
The Question is how can I make it to where someone clicks lost call the form is filled out including the Reason and Resolution with specific values when the Resolution values are dynamically generated?
Here is the script for the Lost Call Button:
function LostCall() {
var Reason = document.getElementById("Reason");
Reason.Value = 'Misc/Other';
var Resolution = document.getElementById("Resolution");
Resolution.Value = 'Lost Call';
Using the above Reason is populated but not Resolution. Also note both Misc/Other and Lost Call are options available in the array I'm using.
EDIT: Updated fiddle.
Hmm, your code is working, as I've tried it in this quick fiddle
Are you simply missing a closing } or was that just a simple mistake when typing this question?
if the value you are assigning in the list of options, try this approach:
function LostCall() {
document.getElementById("Resolution").selectedIndex = "2";
}
check out the following resource (press the "try it yourself" button) : http://www.w3schools.com/jsref/prop_select_selectedindex.asp

Controls Webpage with Knockout: Visible changes not saved

Trying to edit a website with Excel VBA. The edits appear to work, but when I use the save button, nothing is saved. Why isn't updated data, which is visible on the screen, being saved?
This code opens a web page in internet explorer, navigates where I want, fills out data, all which show on the screen, using various methods, such as:
For Each objElement In objElementColl
ExtractedName = objElement.outerHTML
If InStr(ExtractedName, "NewPermit") > 0 Then
objElement.Checked = True
and
Set DropDown = objHTML.getElementById("ProjectFile-AccreditedCertifierId")
DropDown.selectedIndex = 1
or
objHTML.getElementsByName(ElementName)(0).Value = ValueCheck
All work and changes appear on the screen. I click save by using:
Set objElementColl = objHTML.getElementsByClassName("btn")
For Each objElement In objElementColl
ExtractedName = objElement.outerHTML
If InStr(ExtractedName, "click: save, enable:") > 0 Then
objElement.Click
ExtractedName = 1
Exit For
End If
Next
Which runs. The issue is it doesn't save the changes from the three pieces above.
What I have tried
Pause my code and manually click save (same issue)
Pause my code, manually change a checkbox and run the code to save (does save the manual change, but not the coded ones
Pause the code and manually change a box and manually save (only manually changed box is saved)
From above, it appears my save click works, but although the boxes are visibly changed and filled out using the code, there is a gap between the visible and the background.
Some HTML source code. Is what Chrome shows me when Inspecting an element I am changing:
<fieldset>
<legend>Proposal</legend>
<div class="col-xs-12 col-sm-8 col-md-6">
<div class="row">
<div class="col-xs-2 form-group">
<label for="ProjectFile_ProposalLot">Lot</label><input class="form-control" data-bind="textInput: ProjectFile().ProposalLot" maxlength="100" name="ProjectFile-ProposalLot" type="text" />
</div>
<div class="col-xs-2 form-group" data-bind="visible: ProjectFile().StateId() != 7 && ProjectFile().StateId() != 5">
<label data-bind="text: ProjectFile().ProposalDpLabel()"></label>
<input class="form-control" data-bind="textInput: ProjectFile().ProposalDp" maxlength="100" name="ProjectFile-ProposalDp" type="text" />
</div>
I searched the source code for the page. I believe this might be important, but I am not a HTML coder. I have shortened it a bit
var ProjectFileEditViewModel=(function(){__extends(ProjectFileEditViewModel,ViewModel.Model);function ProjectFileEditViewModel(){ProjectFileEditViewModel.__super__.constructor.apply(this,arguments);};ProjectFileEditViewModel.prototype.fields=function(){return {"Id":new ViewModel.NumberField(0),"StateId":new ViewModel.NumberField(0),"DefaultOfficeAddressId":new ViewModel.ObservableField(),"Name":new ViewModel.ObservableField(),"ExistingApprovalDate":new ViewModel.DateField("DD/MM/YYYY"),"ProjectClosed":new ViewModel.ObservableField(),"ProposalAddress":new ViewModel.ObservableChildField(exports.AddressViewModel,this),"Zoning":new ViewModel.ObservableField(),"ProposalLot":new return ProjectFileEditViewModel;})();if(exports.ProjectFileEditViewModel==null)exports.ProjectFileEditViewModel=ProjectFileEditViewModel;
There is also this:
Buildaform.model=new Buildaform.ProjectPageViewModel({ ... ,"ProposalLot":null .... }
I think this last one has something to do with it. I do not know if I can change it.
I cannot release the website address or source code publicly.
As the regarding web site can not be shared, I can come up with a just set of hints to try out:
If the web site would implement a simple (pure) HTML form to send the POST request, your solution would be fine. But looking at the HTML you shared
<label data-bind="text: ProjectFile().ProposalDpLabel()"></label>
the data-bind is already suggesting that the data is getting collected/sent by a library. (E.g. Knockout is using that attribute). This library might now collect the data somewhere, and it might get triggered by a "click" or a "key" event in JavaScript. The collected information can then be stored in a hidden DOM element as suggested by GCSDC or directly in a JavaScript variable.
What I would suggest now is to find out which JavaScript framework is used on this page by inspecting the HTML source. At some point there should be a
<script src="<fancy js framework>.js"></script>
tag in the HTML, which should give you the name of the framework. (There can actually be multiple tags of this kind, including custom JavaScript files. These tags do not have to be at the beginning of the HTML document, and can be scattered all over it, so you might have to search for script in the HTML document. One of them should be the main framework, which is sending the request. If you are not sure which one it would be, you have to google all of them and find out.)
Then, research how the the POST (maybe Ajax) request is sent in the JavaScript code on this page, with help from the documentation of the Framework. And then, send the request by executing custom JavaScript from VBA on this page; how this could be done is shown in this post.
Alternatively, you could try to trigger a click (or key) event on the form inputs to make the framework believe you actually typed it in; how this could be done is shown in this post, but this might not work in all cases.
Per your comment that:
Pause my code, manually change a checkbox and run the code to save
(does save the manual change, but not the coded ones
It seems that the problem is with the code setting form controls and not with the code clicking the save button.
This seems to be a problem not related to VBA but with the behaviour of knockout - see this SO post. The pertinent comment is:
Your problem is that ko subscribes on the click event inside the checked binding:
The questioner in that post is having a similar problem to you - they are trying to check a checkbox (to change the view) but it is not updating either the viewmodel, or the underlying model itself. Knockout is a MVVM framework.
The give-away in your question is that your manual changes commit because you perform a click-and-change when performing the action via point-and-click in the browser, but your programmatic method only does the change to the form control, but not the click first.
So, how to solve this via VBA automation through IE?
Based on the solution in the post I referenced above, plus the method here I will hazard the code below as a possible solution, but please note it is untested ...
Basically you need to 'click' on the form element you want to change - and then update the control value. Hopefully the 'clicking' bit will mean that the knockout viewmodel updates per the 'change', and from there, the model data will be written to the database (or whatever):
Your checkbox example:
If InStr(ExtractedName, "NewPermit") > 0 Then
// hopefully this will get knockout to apply the required binding before your change the value
objElement.Click
objElement.Checked = True
Your dropdown example:
Set DropDown = objHTML.getElementById("ProjectFile-AccreditedCertifierId")
// hopefully this will get knockout to apply the required binding before your change the value
Dropdown.Click
DropDown.selectedIndex = 1
Hope that helps - quite the 3-pipe problem! Good luck.

AngularJS - Setting value of radio button built using ng-repeat

Here's the HTML :
<div>
<ul>
<li ng-repeat="answer in answers">
<input type="radio" ng-model="$parent.selectedAnswer" name="answerText" value="{{answer.answerID}}"/>
<label>{{answer.answerText}}
</label>
</li>
</ul>
</div>
The need is to store the selected answer into LocalStorage and whenever the question shows up again, mark the radio button corresponding to that answer.
I'm able to store it in LocalStorage, retrieve it, but when I update the model, it doesn't select the radio button on the UI.
In my controller, I'm simply calling -
$scope.selectedAnswer = value.answerID;
where value points to the answer stored in the LocalStorage.
Please help.
EDIT:
A little more detail :
I'm managing the entire quiz page using a single route and controller. When one question is done, I fetch the next question and its corresponding answers. While populating the answers array in the controller ($scope.answers), I'm checking to match their IDs with the answer stored in the LocalStorage. Between each question, the answers array is emptied and populated again.
So you should be using ng-value to do this since it is an angular expression and not a text value. Using just value as you did worked in Plnkr but I know that sometimes it does not.
Also in general I have found that using the ng-controller="<ControllerName> as <ThisRef>" works a lot better in most cases. There are times when you still need the $scope variable (Like for form validation) but normally I have fewer scoping issues when using the controllers "this" variable over the $scope.
I made an example using the method I described:
http://plnkr.co/edit/XV3we6SDmmLmUGSwdVVT?p=preview
If your question has to do with HTML 5 Local Storage let me know, I wasn't sure
EDIT:
Alternatively you can try using $scope.$apply(); to accomplish this. If the scope is not in sync with the view then apply should fix that.
Here is another plnkr showing how I would generally do something like that. Note that the $apply is 100% not needed in that example but likely is needed in yours.
Here is my example:
$scope.updateModel = function(value) {
$timeout(function() {
$scope.$apply(function() {
$scope.selectedAnswer = value;
});
});
};
Notice I am using $timeout to make sure it doesn't happen while there is a digest in progress.
http://plnkr.co/edit/O7CBfTiWPrsbthFKlAOZ?p=preview
I hope this edit helps =)
Since you only have one controller, you don't need to specify $parent.
<div>
<ul>
<li ng-repeat="answer in answers">
<input type="radio" ng-model="selectedAnswer" name="answerText" value="{{answer.answerID}}"/>
<label>{{answer.answerText}}
</label>
</li>
</ul>
</div>

Prefilled HTML forms FROM buttons

I hope this question has an obvious answer to anyone who knows his way around JS and HTML :)
I have a very specific problem. I am trying to add to the header on a site buttons that will function like 'quick searches' which will basically on click send pre-filled form values to my search page and have the search page also populate these values in the ajax form inside.
Here is a sample search page that's outside of the results page:
http://www.thediamondsexperts.com/index.php?route=product/diamondsearch/jewelry
You'll notice that when you change the values there and click Search, the values also appear in the ajax form on the sidebar of the search results page.
What I simply want to do is create different variations for pre-set searches, and put them as buttons in the header.
When I try to put a few invisible forms in it won't work because of the multiple form values with the same ids but in general I think there must be a simple way to do this server side.
For instance, copy the current function that accepts the search, have it with pre-set values instead of populating the values from the form and then simply calling that function onClick. Does that make sense?
I need to create something simple enough though that would be easy for the admin to later change and customize more buttons so a client-side solution would be best.
Your help is much appreciated!
All you need is a form with hidden inputs and a submit button:
<form>
<input type="hidden" name="param1" value="Predefined value 1" />
<input type="hidden" name="param2" value="Predefined value 2" />
<input type="hidden" name="param3" value="Predefined value 3" />
<button type="submit">Search!</button>
</form>
This will only show the button, but the values will still be sent to your form's action.
...there must be a simple way to do this
server side
...a client-side solution would be best
...copy the current function that accepts
the search, have it with pre-set
values instead of populating the
values from the form and then simply
calling that function onClick. Does
that make sense?
Not really, not to me at least. If you can clarify I'd be glad to help more.
When you say "multiple form values with the same ids", I fear you may be confused: There is no requirement for a form input to have an "id", I think you mean "name", and there's no need to have multiple inputs with the same name in a form unless you want to send an array of values.
I didn't want to go overboard and talk about how the ajax works on that site, because that's another thing altogether and all you seemed to be concerned about was the preset search buttons. Hopefully this helps you figure it out, GL.
EDIT: I'm having a tough time figuring out what you're really asking, if you are trying to duplicate the behavior on that site, please tell us what server side language is available to you, if you're using or open to using any javascript libraries, and what you have tried so far. A full fledged spoon-feeding tutorial is really out of scope, you will get better, clearer help if you share the code from your current attempts.
If you want to pass values from one page to another and handle it client-side, use "get" for the form submit method, and use the handy "gup()" function to grab the param values. You can get more info on gup() here:
function gup( name )
{
name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
var regexS = "[\\?&]"+name+"=([^&#]*)";
var regex = new RegExp( regexS );
var results = regex.exec( window.location.href );
if( results == null )
return "";
else
return results[1];
}
On your initial page, you can either use inputs with type="hidden" or just get the values from the visible inputs (as on your sample page). Then submit to your results page. Given an initial page with something like this...
<form method="get" action="results.html">
<input type="text" name="caratFrom" value="0.7" />
<input type="submit"/>
</form>
... here's sample usage for the results page:
var caratFrom = gup('caratFrom');
// etc.
Then simply assign those values to whatever elements you want, e.g. an input:
<!-- HTML -->
<input type="text" name="caratFromResult" value="" />
// Javascript
document.getElementById('caratFromResult').value = caratFrom;

dojox.form.Rating Not Appearing in Post Data

I have a included a Dojo star rating widget (dojox.form.Rating) in a Dojo form but when submitted, it doesn't appear.
<div dojoType="dojox.form.Rating" numStars="5" id="field_3177" value="3"></div>
The documentation doesn't mention adding a name attribute, but even if I add one, it doesn't help.
<div dojoType="dojox.form.Rating" name="field_3177" numStars="5" id="field_3177" value="3"></div>
Examining the rendered HTML in Firebug, it seems the hidden input field has no name attribute - which would explain why it doesn't appear in the POSTed data.
<input type="hidden" dojoattachpoint="focusNode" value="3" id="field_3177" tabindex="0">
Is there something I should do before submitting?
You just need to add a name to the widget, i.e.
<div dojoType="dojox.form.Rating" numStars="5" id="field_3177" name="field_3177" value="3"></div>
This is nothing special to Dojo. All input elements must have a name in order to be submitted back to the server, see http://www.w3schools.com/tags/att_input_name.asp.
UPDATE:
Sorry, didn't see that you'd already tried adding a name param. I'd argue this is a bug in either the Form or (more likely) the rating widget. If you submit your form via XHR using dijit.form.Form.getValues() then you'll get the rating widget included - if you have a name. But if you use the native form submit then you don't.
I've created a test case at hhttp://telliott.net/dojoExamples/dojo-ratingInFormExample.html. You can get this to work for non-XHR form submission by quickly iterating through the values returned by getValues() and building the query string yourself. Not ideal. I suspect the template for the rating should be updated to put the name attribute onto the input node rather than the top level node.
Silly question:
have you added dojo.require("dojox.form.Rating"); to your code?
Hope it helps you.
//Daniel

Categories