How to get the DOM element that triggered ng-change? - javascript

I'm using angularJS. I have a few <select> elements on my page, each with its own ng-change, for example:
<select id="hairColorComponent" ng-model="hairColor"
ng-options="option.name for option in hairColorData"
ng-change="updateUserData()">
I want to be able to determine which DOM element got updated from within the updateUserData function, without having to manually specify it as a parameter for each ng-change attribute.
Is there an event, or caller or something similar that I can use within the context of updateUserData?
Hopefully something like ng-change="updateUserData(caller)"

There's no (easy) way to do this by design. Angular controllers are supposed to be completely separate of the DOM, so if you find yourself needing to reference the DOM in them you're probably approaching things the wrong way.
If your HTML is
<select id="hairColorComponent" ng-model="hairColor"
ng-options="option.name for option in hairColorData"
ng-change="updateUserData()">
Then changing the select will change the value of $scope.hairColor in your controller. In updateUserData() just read its value and act accordingly.
If in your situation there's really no way to do it except referencing the DOM, you could do it by writing a custom directive. In general, direct DOM manipulation in Angular should be a last resort kind of measure though.

Found this on google, I eventually solved my problem so here's my solution.
If you just need the ID, you could always just pass that as a parameter.
<select id="hairColorComponent" ng-model="hairColor"
ng-options="option.name for option in hairColorData"
ng-change="updateUserData('hairColorComponent')">
Still not super clean, but it solved my problem. Since I actually needed the dom element, I then used jQuery to get the element.
$scope.updateUserData = function (id) {
var element = jQuery('#'+id);
};
(For those wondering, I'm doing my best with converting legacy code that "magically" saves user settings on pages all over the website.)

Related

jQuery caches elements selected?

So I'm using this code to:
$('.something').on('click', function () {
console.log($(this).data('id'));
}
And for some reason, if I modify the data-id using the inspector, jQuery still sees the id that was there in the beginning. However, I tried the same thing using JS and it does see the changes. This makes me wondering if jQuery caches in some way the elements selected and uses them instead of the actual DOM.
Can someone please explain what happens and how jQuery does the event binding in the background?
Later edit: I want to specify that I'm talking about the "data-" attribute that I put in the HTML, not about the '.data()' provided by jQuery. Not sure if it's the same thing.
jQuery caches elements selected?
No. But the data managed by data is stored in an object cache maintained by jQuery, keyed by a unique identifier jQuery adds to the element (so it can look up the data). data is only initialized from data-* attributes, it is not an accessor for them. It's both more and less than that.
If you're interested, you can see that as an "expando" property on the element instance, it'll start with "jquery" and have a long number attached to it (currently; it's undocumented — for good reason — so this may change):
var foo = $("#foo");
console.log(foo.data("info")); // hi there
console.log("Expando name: " + Object.getOwnPropertyNames(foo[0]).find(name => name.startsWith("jQuery")));
<div id="foo" data-info="hi there"></div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

angular js input bind value

I have an input like this :
<input ng-model="mysearch.myfield" id="myid"/>
that is bound to a filter
<table><tr ng-repeat="row in list|filter:mysearch">...</tr></table>
If I modify the input value in the GUI, it works perfectly, but if I try to modify its value via javascript/jquery
$("#myid").val("newvalue")
The input value is updated but the mysearch.myfield is not modified
Actually, I have a list that appears on user actions (it does not exist on page load):
<li onclick="changeTheInputValue('newvalue1')">newvalue1</li>
<li onclick="changeTheInputValue('newvalue2')">newvalue2</li>
...
with
function changeTheInputValue(v) {
$("#myid").val(v);
}
And it does not work when I click on an "li" (the input value is updated, ut the mysearch.myfield is not modified)
I also tried
<li ng-click="mysearch.myfield = 'newvalue1'">newvalue1</li>
<li ng-click="mysearch.myfield = 'newvalue2'">newvalue2</li>
...
but it does not work :(
Any idea ?
Thanks,
any javascript executing outside the angulars event loop won't be efective in angular untill you apply it. in order to do that you need to get the relevant scope and $apply the changes, since is not clear, how and why you are modifying the value outside the angular scope there is nothing much i can say except you could do something like
function changeTheInputValue(v) {
$("#myid").val(v);
angular.element($("#myid")).scope().$apply();
}
that should let angular know about the changes, how ever this is a bad design if using angular. there are far better ways to accomplish this same thing w/o having mixed execution scopes. (angular/the rest);
You are modifying the value of the input "#myid" using direct DOM manipulation. AngularJS is not aware of this. If you want both the html and the value of mysearch.myfield to update correctly, you must do so by modifying the mysearch.myfield property directly, either in a controller or via an ng-model binding or something similar.
The main reason this isn't working for you has to do with how AngularJS modifies the DOM. When you use jQuery to modify the DOM, you are circumventing angular. Angular has no way of knowing if you have changed something in the DOM except if you do it directly through Angular itself. In particular, if you are curious, read about the $compile and $digest services.
Hope this helps shed some light on the subject!

Issue while accessing element by dojo.byId dojo 1.8

I am trying to access a div element text by using dojo.byId but it is returning the value which is set at first time the value is selected.It is somehow binding the value initially selected to the id of div and hence returning the same value even after I change the value to some other value.
var startDateLabel = dojo.byId("startDateLabel");
<label class="secondaryColor bold75Font floatRight" id="startDateLabel">${startDate} </label>
I tried to use registry.ById but since it is in a widget that is created more than once , it gives "id already registered error".For removing that , I also used destroyRecursive method but that also doesn't work.
Earlier, I used the id of container in which the widget is loaded and traversed to the children hierarchy to get the label value and it worked fine. but the child traversal code made it a bit messy.Something like
var startDateCont = registry.byId("startDateContainer");
var startDateLabel = startDateCont.domNode.children[1].children[1].children[1].innerHTML;
Is there any other way in dojo to achive this????
If you're using this inside a widget you should not even use IDs (or at least use generated IDs). The best way to get a reference to a DOM node while using a widget is by using attach points. An example, consider the following HTML:
<label class="secondaryColor bold75Font floatRight" data-dojo-attach-point="startDateLabel">${startDate} </label>
As you can see I introduced an attribtue called data-dojo-attach-point which allows me to give a name to the label (similar to an ID).
Then inside the widget you can now easily get a reference to that DOM node by using:
this.startDateLabel;
Just some extra information, you can also define events in a similar way by using data-dojo-attach-event. Make sure to read this part of the Writing your own widget article.

Get name attribute of selectbox

I never used name attribute, but in MVC looks like i must.
I have
<select name="testname" onChange="Alert()">
<option value="8">Test</option>
</select>
JS
function Alert(){
alert(this.name);
}
Result should be alert("testname")
Its simple, but i never used, and its hard to find on google, since everyone asking about getting value or index :D
The problem is that due to the way you call Alert, this refers to window, not to the DOM element. You could pass it to the function as argument:
<select name="testname" onchange="Alert(this)">
and
function Alert(element){
alert(element.name);
}
There are better ways to attach event handlers than using HTML attributes. I recommend to read the various articles about event handling on quirksmode.org.
Also make sure you understand how this works.
Of course you could also write
<select name="testname" onchange="alert(this.name)">
If you are using jQuery, then you should use it to bind the event handler. For example:
$('select[name=testname]').change(function() {
alert(this.name);
});
Have a look at a jQuery tutorial and take the time to go through the API documentation, it's worth it.

I can't get onChange to fire for dijit.form.Select

I seem unable to correctly attach the onchange event to a dijit.form.Select widget. However, I am new to web development, so I could be doing something completely idiotic (although, as best I can tell (and I've read all the docs I could find) I'm not). I made sure the body class matches the dojo theme, that I dojo.require() for all the widgets I use (and dojo.parser), and still, nada. The code I'm using is:
dojo.addOnLoad(function () {
var _query = dojo.query('.toggle');
for (var i in _query) {
dojo.connect(_query[i], 'onchange', function (ev) {
console.log(ev + ' fired onchange');
});
}
});
Any help at all would be appreciated.
Addition: after digging more into the internals of how dijit renders widgets, I discoverd that when I add the dojoType='dijit.form.Select' attribute-value pair to my html element (doing this declaratively), dijit actually renders a one-row two-col table. The table's first element is a span (with the class dijitSelectLabel) that I'm assuming just displays the selected (or default) element. It's second element appears to be a button rendered as a down arrow that toggles the display of the menu itmes in response to certain DOM events. Also (and I thought this was pretty nifty), dijit doesn't actually place the select options in the DOM tree until one of those events is triggered. I looked at the HTML in firebug right after a fresh pageload (before i clicked on anything), and the second option isn't anywhere to be found. Then, once I click on the arrow button, there's a dijit.Menu widget, dijit sticks a dijit.Menu to the end of the body node; after I click somewhere else, the Menu widget is still the body's lastChild, now its just hidden and not attached to the form.Select widget.
Should it really be this complicated if all I want to do is place a different dijit.form widget in the DOM tree depending on what item the user selects?
Conclusion:
Turns out it was a capitalization issue.
dojo.connect(widget_obj, 'onChange', function_obj);
works, whereas
dojo.connect(widget_obj, 'onchange', function_obj);
doesn't.
So I was right that I was being completely stupid. I assumed that because the all lowercase version works when putting placing in it an html tag as an attribute, that Dojo would treat it the same. It makes sense, because dijit.form.Select has no .onchange attribute, but does have a .onChange attribute. (I ended up sticking with a .Select over a .FilteringSelect because I don't my users to be given any impression that they can type in something.) So, which one of you guys do I give the answer to (because you both had onChange in your posts, I guess I was just too inexperienced to realize that the case mattered)?
For anyone else finding this page through a web search, you may have made the same mistake I did .. copy-pasting your markup such that each has the same 'value'.
e.g.
<select dojoType='dijit.form.Select' onChange="fn">
<option value='foo'>Foo 1</option>
<option value='foo'>Foo 2</option>
<option value='foo'>Foo 3</option>
</select>
fn() will never be called, because the change handler code checks the new value against the previously selected value and does not fire onChange unless it's changed.
Try the following when doing a dojo.connect:
var inputEvents = []; //Global var
inputEvents.push(dojo.connect(dijit.byId(inputFldStr), "onChange", eventFuncObj));
Store the connection in a global var.
In your code you connect a handler to 'onchange' event of dom nodes, not dojo widgets. dojo.query returns you a NodeList object - a collection of nodes that match the query.
In this case it's more reliable to connect to a widget's 'onChange' event, as GoinOff showed. Just a little addition to his answer to make sure you're doing this right.Assume this is your html (in later versions of Dojo dijit.form.Select has been replaced with dijit.form.FilteringSelect):
<input dojoType="dijit.form.FilteringSelect" id="stateInput" store="stateStore" searchAttr="name" name="state"/>
Then you would connect to 'onChange' this way (you also can store the connection in some array to be able to disconnect it later, as GoinOff suggested):
dojo.addOnLoad (function () {
dojo.connect(dijit.byId("stateInput"), "onChange", function(){});
}
But it's another story if you don't know your widget's id and want to use dojo.query to connect to multiple widgets.

Categories