how to go from angular event to jquery selector - angularjs

I know this is bad design but would like to introduce angular to a current project. I would like sayHello to be able to determine whether the element has the class 'is-a-favorite'
<div ng-click="sayHello(29, $event)" class="is-a-favorite" data-type="location" data-global-id="29" data-make-disappear="false"> </div>
$scope.sayHello=function(global_id,event){
//var selector=???
if(selector.hasClass('is-a-favorite')){
console.log("this is-a-favorite");
}
};
How would (or could) I get a reference to current DOM element to query via hasClass?
thx

The clicked element is available as $event.target, so you could check $($event.target).attr('class') or something similar.
EDIT: actually, what you'd want is to check $($event.target).hasClass('is-a-favorite')

Related

AngularJS - Validate a part of a form

I'm using Angular 1.5.9
I have a big form, which I scattered through different Bootstrap Accordion.
When there is an error in the form, I want to be able to change the class of my accordions to show in which accordions the error is located.
To check for errors in a whole form, I can check
myFormName.$error
And to check errors for an element, I can simply do
myFormName.myInputName.$error
But I don't know if there is a way to do this for multiple element at once, without having to check each element individually.
My first thought was to change the name of my inputs like so:
<input name="accordion1.fieldName">
But this didn't give me the expected result: I don't have myFormName.accordion1.$error, actually, I don't even have myFormName.accordion1.fieldName, since my data is actually stored in myFormName['accordion1.fieldName'] which is pretty much useless.
Has anyone found an answer to this problem? I think I'll have to check each field, which is kinda ugly, and a mess to maintain whenever we add / remove fields...
Maybe there is a directive out there that could do that for me, but as a non-native English speaker, I can't find which key words to use for my search in this situation.
One approach is to nest with the ng-form directive:
<form name=form1>
<div ng-form=set1>
<input name=input1 />
<input name=input2 />
</div>
</form>
{{form1.set1.$error}}
You could name the fields with a prefix such as 'accordion1_' then add a controller function that will filter your fields.
ctrl.fieldGroup = function(form, fieldPrefix) {
var fieldGroup = {};
angular.forEach(form, function(value, key) {
if (key.indexOf(prefix) === 0) {
fieldGroup[key] = value;
}
});
return fieldGroup;
}
Then ctrl.fieldGroup('accordion1') will return an object with the appriopriate fields on it. You could extend the function further to add an aggregate $error property to the resulting fieldGroup object.

Using contenteditable with ng-model inside ng-repeat?

Here is my issue:
I am using ng-repeat to make a list of spans.
Each span has the contenteditable attribute and ng-model directive.
Everything works as expected (including two-way data binding), until I try to add a new item to the list.
<div ng-repeat="item in list">
<span ng-model="item.text" contenteditable></span>
</div>
<button ng-click="addItemToList"></button>
The methods look like this:
$scope.addItemToList = function () {
$scope.list.push({text: 'dummy text'});
}
or
$scope.addItemToList = function () {
$scope.list.splice(1, 0, {text: 'dummy text'});
}
When adding the new item to the list (push or splice), the DOM updates, but the last item is initialised empty, with no text whatsoever. The last item in the model list also empties out, even if I specifically push an element with text in it.
After a few tests, I've noticed that this only happens if the list's length is bigger after modifying it:
if I try to replace/modify/remove (not add) an element in the list, it works well.
I believe this has to do with the way contenteditable elements initialise in the DOM (I think they initialise empty, and somehow, the model empties out as well).
Has anyone encountered this problem before? If yes, how did you solve it / what workaround have you found?
Based on the angular docs related to ngModelController, it seems that there is not built-in support for two-way data binding with contenteditable elements, seeing as in the plunkr example they wrote their own contenteditable directive. You might be able to use a modified version of that as a workaround.
It looks to be a similar problem as this question and the contenteditable directive there looks similar to the contenteditable directive in the angular docs example.
I also found this directive on github that might accomplish what you are trying to do.
Edit: I did a new version of the plunk I posted in the comment above:
https://plnkr.co/edit/v3elswolP9AgWHDIPwCk
In this version I added a contenteditable directive that appears to be working correctly. It is basically a spin off of how the input[type=text] directive is written in angular, but I took out the places where it handles different types of input (since in this case it will just be text) and the places where it handles events that contenteditable elements don't even fire. I also changed it so that the $viewValue gets updated based on element.html() instead of element.val(). You might be able to use something like this as a workaround
The issue is old but that was the same problem for me today. (angular 1.5). My workaround was to add on blur update option: <td contenteditable data-ng-model="position.value" ng-model-options="{updateOn: 'blur'}"></td> somehow then model stopped getting be empty on initialize. I like using update on blur in many places (solves some performaces issues) so this is good for me, however it's some kind of trick.

How can I hide a component in AngularJS yet still get an external library to bind to it?

In my code I have something similar to
<div ng-if="variableThatEvaluatesToFalse">
<input id="location">
</div>
What I'm trying to do is hide a component and then show it in response to user input. However, upon my page loading I want Google Maps to attach to my input. Google Maps relies on document.getElementById('location') which is null presumably because AngularJS is 'hiding' it. I can I get document.getElementById('location') to return my input field even if it is initially hidden by the ngIf directive?
The ngIf directive prevents elements from being included in the DOM at all. What you would need in this case is ngHide, which would keep the element in the DOM.
See: https://docs.angularjs.org/api/ng/directive/ngHide

How to add dynamic text fields with AngularJS

My question is more about what would be the best practice in this scenario:
I have a form that has to allow a user to input n number of ideas, each idea in an independent text field. Ideally there would be a couple of buttons next to the last text input to allow the user to create a new text input or to erase the latest one.
I know DOM manipulation is not the way to go with Angular but due to requirements, I have to do something that requires creating elements dynamically. Is there a best practice, service or directive in Angular that could allow me to do this or I should just inject the elements with jQuery?
The only thing you need is proper use of ng-repeat. No DOM manipulation with jQuery is necessary. Nor would it be good practice. Behold, the power of ng-repeat.
Working plunker here.
Something like this?
<div ng-repeat="idea in ideas">
<input ng-model="idea">
</div>
<button ng-click="AddNew()">Add New Idea</button>
<button ng-click="DeleteLast()">Delete Last Idea</button>
In controller:
$scope.AddNew = function() {
$scope.ideas.push("");
}
$scope.DeleteLast= function() {
$scope.ideas.splice($scope.ideas.length-1, 1);
}

Auto-scrolling multiple chat logs using AngularJS

In this simplified example, I have:
$scope.channels = [channel1, channel2, ...]
and a channel is an object containing:
chatLog: ["msg1", "msg2", ...]
In the .html, I have something similar to:
<div ng-repeat="channel in channels" ng-show="channel.isActiveWindow">
<div class="chatlog" style="height:500px;">
<div ng-repeat="msg in channel.msgLog">
<div>{{msg}}</div>
</div>
</div>
</div>
I'd like to implement automatic scrolling when a new message is added to channel.chatLog. What would be a proper way to do this with AngularJS? I can compute where to scroll to, but I need an event to fire when the chatLog is appended.
I was thinking of setting up a watch on the chatLog of every channel, but this becomes way too manual and I need to be careful about removing the watch as well. Another way would be to not rely on watches but make sure every place in the code that will append to a chatLog will also fire the event and provide some info about which channel the event relates to.
Also, when this event is fired, is there a way to access the div with the class "chatlog" in the code above for the given channel without assigning it a unique 'id' and looking it up by that?
Thanks in advance :)
You could create a chatlog directive and add it to your markup like this:
<div chatlog ng-repeat="msg in channel.msgLog">
Then in the linking function of your directive you can do something like:
link: function(scope, element, attrs) {
if (scope.$last) {
// this will run each time the channel.msgLog array changes
// more precisely, every time the last element is linked
}
}

Resources