Angularjs autocomplete directive in 2 places in same dom - angularjs

Hi,
I am building an autocomplete directive and I need it at two places in the same form. The autocomplete feature works fine but it shows the same directive at both the places. I have attached a image with this question, referring the Image even if I type in teh to colum the country autocomplete is also activated, how to I bind it to only the element where user is interacting ?
following is my code for the directive
angular.module('xyz').directive('autoComplete',['$rootScope',function($rootScope){
return {
restrict:'AE',
transclude: true,
replace: true,
scope:{
selectedTags:'=model',
userdata:'=',
name:'#'
},
templateUrl: function(elem,attr){ return 'views/rad/layout/autocomplete.html' },
link:function(scope,elem,attrs){
scope.suggestions=[];
scope.selectedTags=[];
scope.selectedIndex=-1; //currently selected suggestion index
scope.search=function(){
var newSuggestions = [];
// if(attrs.userdata.indexOf(scope.searchText)===-1){
// //dataUser.unshift(scope.searchText);
// }
for(var i=0; i < scope.userdata.length;i++)
{
if(dataUser[i].toLowerCase().indexOf(scope.searchText.toLowerCase()) !== -1){
newSuggestions.push(dataUser[i]);
}
}
scope.suggestions = newSuggestions;
scope.selectedIndex = -1;
}
scope.checkKeyDown=function(event){
console.log(event.keyCode);
if(event.keyCode===40){//down key, increment selectedIndex
event.preventDefault();
if(scope.selectedIndex+1 !== scope.suggestions.length){
scope.selectedIndex++;
}
}
else if(event.keyCode===38){ //up key, decrement selectedIndex
event.preventDefault();
if(scope.selectedIndex-1 !== -1){
scope.selectedIndex--;
}
}
else if(event.keyCode===13){ //enter pressed
scope.addToSelectedTags(scope.selectedIndex);
}
}
scope.addToSelectedTags=function(index){
if(scope.selectedTags.indexOf(scope.suggestions[index])===-1){
scope.selectedTags.push(scope.suggestions[index]);
scope.searchText='';
scope.suggestions=[];
}
}
scope.$watch('selectedIndex',function(val){
if(val!==-1) {
scope.searchText = scope.suggestions[scope.selectedIndex];
}
});
scope.removeTag=function(index){
scope.selectedTags.splice(index,1);
}
}
}
}])
following is the auto complete template
<div class="tags-wrapper">
<div id="tagsList" class="tags-cloud">
<div class="suggestionListing">
<div ng-repeat="selectedTag in selectedTags track by $index" class="tag">
<span class="tagName">{{selectedTag}}</span>
<span> <b ng-click="removeTag($index)" class="fa fa-times cross"></b></span>
</div>
</div>
<div class="input-group">
<input type="text" name="{{inputname}}"placeholder="" aria-describedby="basic-addon2" id="searchInput" class="form-control no-border" ng-model="searchText" ng-keydown="checkKeyDown($event)" class="form-control" ng-model="searchText" ng-change="search()"/>
<div class="suggestions-list">
<div ng-repeat="suggestion in suggestions track by $index" class="blockSpan" ng-click="addToSelectedTags($index)" ng-mouseover="$parent.selectedIndex=$index" ng-class="{active : selectedIndex===$index}">
<div class="userAvatar">
<i class="fa fa-user"></i>
</div>
<div class="userInfo">
{{suggestion}}
</div>
<br clear="all" />
</div>
</div>
</div>
</div>

Related

Form name could not identify on Angular JS validation

I have a problem on my code. After submitting the form I have a function for validation checking but it seems that the condition on my function won't identify my form name. Here's my code:
Template form part:
<div class="modal-body">
<form name="form.addeditcoursewareModal">
<div class="row">
<div class="small-12 medium-12 large-12 columns">
<label class="stacked">
Question
<input name="question" type="text" class="form-field" placeholder="Question" ng-model="$ctrl.question.data.attributes.question" >
</label>
</div>
</div>
<div class="row">
<div class="small-12 medium-6 large-6 columns">
<label class="stacked">
Question Type: {{ $ctrl.question.data.attributes.question_type }}
<select name="question_type" class="form-field" id="questionType" ng-model="$ctrl.question.data.attributes.question_type" ng-change="$ctrl.getSelectedQuestionType()">
<option value="single">Single</option>
<option value="multiple">Multiple</option>
<option value="true_or_false">True or False</option>
<option value="free_text">Free Text</option>
</select>
</label>
</div>
Validation part:
validateForm(){
this.validatingForm = true;
if (this.question.data.attributes.question_type === 'true_or_false'){
this.question.data.attributes.choices.data[0].attributes.choice = 'True';
this.question.data.attributes.choices.data[1].attributes.choice = 'False';
}
if (this.modalOptions.actionType == 'duplicate'){
this.question.data.attributes.choices.data.forEach((value) => {
delete value.id;
});
}
if (this.modalOptions.type === 'multiple'){
this.validateCheckbox();
}
console.log(this);
if (this.form.addeditcoursewareModal.$invalid || this.invalidMultiple) { // ERROR DETECTED
// Check for an invalid field
angular.forEach(this.form.addeditcoursewareModal.$error, (field) => {
angular.forEach(field, (errorField) => {
if (errorField.$name === 'dynamicForm'){
errorField.dynamicInput.$setTouched();
} else {
// Set field as touched to display invalid field message
if (errorField.$name === 'single') {
this.invalidRadio = true;
}
errorField.$setTouched();
}
});
});
this.validatingForm = false;
} else {
Here's the error I got:
angular.js:14791 TypeError: Cannot read property 'addeditcoursewareModal' of undefined
I already tried renaming my form but the error is the same.
It seems that you are using the controller as syntax. You would need to add $ctrl in front of form.addeditcoursewareModal.
<form name="$ctrl.form.addeditcoursewareModal">

Angularjs cannot perform crud operations from accordion

I am trying to add a form to a bootstrap accordion and post a text value. Without accordion the form works alright. When i added the form to the accordion i am not able to pass text box value to angularjs controller. Basically i am not able to perform the CRUD operations as i am not able to pass the values. Minimum code related to the issue.
main.html
<div ng-controller="myController">
<accordion class="accordion" close-others="oneAtATime">
<accordion-group ng-repeat="group in groups" is-open="status.isOpen[$index]" >
<accordion-heading>
{{group.groupTitle}} <i class="fa chevron-icon" ng-class="{'fa-chevron-down': status.isOpen[$index], 'fa-chevron-right': !status.isOpen[$index]}"></i>
</accordion-heading>
<div ng-include="group.templateUrl"></div>
</accordion-group>
</accordion>
</div>
controller.js
App.controller('myController', ['$scope', 'Parts', function($scope, Parts) {
var original = $scope.parts;
$scope.submit = function() {
if ($scope.parts.a_id == null) {
$scope.createNewPart();
} else {
$scope.updatePart();
}
};
$scope.createNewPart = function() {
Parts.resource1.create($scope.parts);
};
$scope.updatePart = function() {
Parts.resource2.update($scope.parts)
};
$scope.oneAtATime = true;
$scope.groups = [{
groupTitle: "ADD 1",
templateUrl: "file1.html"
}, {
groupTitle: "ADD 2",
templateUrl: "file2.html"
}];
$scope.status = {
isOpen: new Array($scope.groups.length)
};
for (var i = 0; i < $scope.status.isOpen.length; i++) {
$scope.status.isOpen[i] = (i === 0);
}
} ]);
file1.html
<div>
<form name="myForm" ng-submit="submit()">
<div class="row">
<label class="col-md-2 control-lable" for="part">Add1</label>
<input type="text" ng-model="parts.part" id="part" required />
</div>
<div class="row">
<input type="submit" value="{{!parts.id ? 'Add' : 'Update'}}" ng-disabled="myForm.$invalid">
</div>
</form>
//inside table
<tr ng-repeat="a in availableparts >
<td>{{a.id}}</td>
<td>{{a.apart}}</td>
<td><button type="button" ng-click="editPart(a.id)" >Edit</button>
<button type="button" ng-click="deletePart(a.id)">Remove</button>
</td>
</tr>
</div>
Initially, you need to set $scope.parts to empty object {}, so when you access $scope.parts.id you will have null, instead of error saying id of undefined

How to popup chat window using node.js

I am working on node.js and began to create a chat application I have built it successfully but the problem is that I want the chat window should be open when the client click on the name of the sender(who sent the message to the client).
I will show you the example what I have done till now.
Here you can see that the chat box is already open but I want it should open when a user is selected from "List of users" and the content of chat box should be replaced whenever a new user is selected and previous content should be removed.
Here is my index.html code :
<div class="col-md-4 user-list">
<h2>List of Users</h2>
<ul class="list-group">
<li class="list-group-item"
ng-repeat="user in userList"
ng-class="{ 'active' : user.id == selectedUser.uid}"
ng-click = seletedUser(user.id,user.userName);
ng-style="{
'cursor': user.id === socketId ? 'not-allowed' :'pointer'
}"
>
<span id='{{user.id}}' >{{ user.id === socketId ? 'You': user.userName }}</span>
<span id='{{user.id + "notify"}}' style="color:black; opacity:0.5; font:2px; padding:5px; visibility:hidden;"> {{'typing...'}}</span>
</li>
</ul>
</div>
</div>
<div class="container" id="messages">
<div class="row">
<div class="col-md-8">
<div class="panel panel-primary">
<div class="panel-heading">
<span class="glyphicon glyphicon-comment"></span> {{'Chat -' + selectedUser.uname}}
</div>
<div class="panel-body">
<ul class="chat col-md-7"
ng-repeat = "message in messages"
ng-style ="{'float':message.fromid == socketId ? 'left' : 'right'}">
<li>
<div class="clearfix">
<div class="direct-chat-text"
ng-style = "{'background-color': message.fromid == socketId ? '#d9edf7' : '#62f3bc'}"
>
{{message.msg}}
</div>
</div>
</li>
</ul>
<br></br>
</div>
<div class="panel-footer">
<div class="input-group">
<textarea elastic type="text" class="form-control custom-control" ng-model='message' ng-keyup="keyup()" ng-keydown="keydown()" ng-change="change()" placeholder="Type your message here..."></textarea>
<span class="input-group-addon btn btn-primary" ng-click="sendMsg()"><i class="glyphicon glyphicon-send"></i></span>
</div>
</div>
</div>
</div>
</div>
</div>
If you would require any other information related to code please comment.
As I am newbie to node.js so help me to solve my problem.
UPDATE
My script.js code which have sufficient details:
app.controller('app', ($scope,$timeout,socket) => {
$scope.socketId = null;
$scope.selectedUser ={ uid: null, uname: null};
$scope.messages = [];
$scope.msgData = null;
$scope.userList = [];
var TypeTimer;
var TypingInterval = 1000;
$scope.username = window.prompt('Enter Your Name');
if ($scope.username === '') {
window.location.reload();
}
$scope.seletedUser = (id,name) => {
if(id === $scope.socketId)
{
alert("Can't message to yourself.")
}
else
{
$scope.selectedUser =
{
uid: id,
uname: name
}
}
};
$scope.sendMsg = () => {
// const keyCode = $event.which || $event.keyCode;
if ($scope.message !== null && $scope.message !== '' && $scope.selectedUser.uid !== null) {
socket.emit('getMsg',{
toid : $scope.selectedUser.uid,
fromid: $scope.socketId,
msg : $scope.message,
name : $scope.username
});
$timeout.cancel(TypeTimer);
TypeTimer=$timeout( function(){
socket.emit('getTypingNotify',{
toid : $scope.selectedUser.uid,
fromid: $scope.socketId,
msg:"hidden"
});
});
$scope.message = null;
console.log($scope.selectedUser.uid);
}
else
{
alert("enter a message or select a User to send message");
}
};
socket.emit('username',$scope.username);
socket.on('userList', (userList,socketId) => {
if($scope.socketId === null){
$scope.socketId = socketId;
}
$scope.userList = userList;
});
socket.on('exit', (userList) => {
$scope.userList = userList;
});
socket.on('setMsg',(data) => {
document.getElementById(data.fromid+'notify').style.visibility= data.msg;
});
socket.on('sendMsg', (data) => {
//console.log('send');
$scope.messages.push(data);
});
my understanding is u want to show a pop up on click of a button or some text box u can use angular bootstarp model as shown here
"https://plnkr.co/edit/?p=preview"
and control the close and open in angularjs contoller and send the chat details too server side
After searching some tutorials and questions(on StackOverflow) I found a way and going to share with you all.
I created a directory name "open" and an attribute visible of this "open" directive to show and hide the dialog.
app.directive('open',function() {
return {
restrict: 'E',
template: `<div class="container">
<div class="row">
<div class="col-md-8">
<div class="panel panel-primary">
<div class="panel-heading">
<span class="glyphicon glyphicon-comment"></span>{{"Chat -" + selectedUser.uname}}
</div>
<div class="panel-body">
<div ppp-chat></div>
<br></br>
</div>
<div class="panel-footer">
<div class="input-group">
<textarea msd-elastic type="text" class="form-control custom-control" ng-model="message" ng-keyup="keyup()" ng-keydown="keydown()" ng-change="change()" placeholder="Type your message here..."></textarea>
<span class="input-group-addon btn btn-primary" ng-click="sendMsg()"><i class="glyphicon glyphicon-send"></i></span>
</div>
</div>
</div>
</div>
</div>
</div>`,
replace:true,
scope:false,
link: function postLink(scope,element,attrs) {
scope.$watch(attrs.visible,function(value){
if(value == true)
{
$(element).show();
}
else
$(element).hide();
});
$(element).on('show.bs.open',function() {
scope.$apply(function(){
scope.$parent[attrs.visible] = true;
});
});
$(element).on('hidden.bs.open',function() {
scope.$apply(function(){
scope.$parent[attrs.visible] = false;
});
});
}
};
});
And a method in controller to toggle the chat window(hide or show)
$scope.toggleChat = () => {
$scope.showChat = false;
$scope.showChat = !$scope.showChat;
};
this toggleChat is used to change the value of showChat scope variable that is used to turn on(off) the visibility of chat box by alterating the showChat value as true or false.
In html I have replaced my id="messages" element( with its child and subchild) with
<open visible="showChat">
</open>

Angular filter wont return a true value

<input type="text" ng-model="searchBuddy" placeholder="search" />
<div class="square">
<div class="list no-padding" ng-repeat='buddy in buddyList | filter : search'>
<a class="item item-avatar" href="#">
<img src="{{buddy.photoURL}}">
<h2>{{buddy.firstname}}</h2>
<p>{{buddy.lastname}}</p>
</a>
</div>
</div>`
this is my HTML code
$scope.search = function (item) {
if($scope.searchBuddy == undefined || $scope.searchBuddy == '' ) {
return false;
} else {
if(item.firstname.toLowerCase().indexOf($scope.searchBuddy.toLowerCase())!= -1) {
return true;
}
}
return false;
}
this is my controller code
In the example that I saw , if the input searchbuddy is empty, it will return false which causes the html div with ng-repeat to now show anything. Else if something is inputted and it somehow got something from firstname, it will return true and show the data which corresponds to the search input. But in my case it won't and I don't know why.
There is an error in your 2nd if statement. You are referring to $scope.searchText when it should be $scope.searchBuddy as that is the model value you are using.
Use your ng-model with filter
<input type="text" ng-model="searchBuddy" placeholder="search"/>
<div class="square">
<div class="list no-padding" ng-repeat="buddy in buddyList |searchBuddy'>
<a class="item item-avatar" href="#">
<img src="{{buddy.photoURL}}">
<h2>{{buddy.firstname}}</h2>
<p>{{buddy.lastname}}</p>
</a>
</div>
</div>
if(item.firstname.toLowerCase().indexOf($scope.searchBuddy.toLowerCase())!= -1) {
return true;

Why do I need $parent to enable the function in ng-click when using ion-scroll?

I am using the following versions:
Ionic, v1.0.0-beta.14
AngularJS v1.3.6
Route configuration:
myApp.config(function ($stateProvider) {
$stateProvider
.state('manager_add', {
url: '/managers/add',
templateUrl: 'app/components/mdl.role_members/views/add.html',
controller: 'ManagerAddController'
});
});
Controller configuration:
myApp.controller('ManagerAddController', function ($scope, $state, $ionicLoading, $filter, ContactService, ManagersService, RoleRequest, RoleRequestsSentService, ToastrService) {
$scope.role_request = RoleRequest.new();
$scope.showContactSearch = false;
$scope.managers = ManagersService.collection();
$scope.$watchCollection("managers", function( newManagers, oldManagers ) {
if(newManagers === oldManagers){ return; }
$scope.managers = newManagers;
$scope.contactsToBeInvited = getNotInvitedContacts();
});
$scope.contacts = ContactService.collection();
$scope.$watchCollection("contacts", function( newContacts, oldContacts ) {
if(newContacts === oldContacts){ return; }
$scope.contacts = newContacts;
$scope.contactsToBeInvited = getNotInvitedContacts();
});
$scope.contactsToBeInvited = getNotInvitedContacts();
function getNotInvitedContacts() {
var notinvited = [];
angular.forEach($scope.contacts, function(contact) {
if(angular.isObject($scope.managers)) {
var results = $filter('filter')($scope.managers, {member_id: Number(contact.contact_id)}, true);
if (results.length == 0) {
this.push(contact);
}
} else {
this.push(contact);
}
}, notinvited);
return notinvited;
}
$scope.search_contact = "";
$scope.search = function(contact) {
if($scope.search_contact === "" || $scope.search_contact.length === 0) {
return true;
}
$scope.showContactSearch = true;
var found = false;
if(contact.display_name) {
found = (contact.display_name.toLowerCase().indexOf($scope.search_contact.toLowerCase()) > -1);
if(found) { return found; }
}
if(contact.contact.getFullName()) {
found = (contact.contact.getFullName().toLowerCase().indexOf($scope.search_contact.toLowerCase()) > -1);
if(found) { return found; }
}
if(contact.contact.email) {
found = (contact.contact.email.toLowerCase().indexOf($scope.search_contact.toLowerCase()) > -1);
if(found) { return found; }
}
return found;
}
$scope.selectContact = function(contact) {
$scope.search_contact = contact.contact.getFullName();
// TODO: Make dynamic role
$scope.role_request.role_id = 4;
$scope.role_request.email = contact.contact.email;
};
$scope.addRoleMember = function(valid) {
if($scope.role_request.email === "") { return; }
if(!valid) { return; }
$ionicLoading.show({
template: 'Please wait...'
});
RoleRequestsSentService.add($scope.role_request).then(function(roleJsonResponse){
ToastrService.toastrSuccess('Request send', 'We have send an invite to '+ $scope.search_contact +'.');
$ionicLoading.hide();
$state.go('managers');
});
}
});
View configuration:
<ion-view view-title="ManagerAdd" >
<ion-content class="has-header scroll="true">
<div class="content">
<div class="list">
<div class="item item-border">
<p>Some text</p>
</div>
</div>
<form name="managerForm">
<div class="list">
<div class="item item-divider">
Some text
</div>
<div class="item item-border">
<form name="fillForm">
<div class="form-group">
<label class="item item-input item-stacked-label item-textarea">
<span class="input-label border-none">Personal message: <span class="text-red required">*</span></span>
<textarea name="message" ng-model="role_member.message" required></textarea>
</label>
<p ng-show="managerForm.message.$dirty && managerForm.message.$error.required"
class="error-message">Message required!</p>
</div>
<div class="form-group">
<label class="item item-input">
<span class="input-label">Search on name <span class="text-red required">*</span></span>
<input type="text" name="search_contact" ng-model="$parent.search_contact">
</label>
<div class="searchResultBox" ng-show="showContactSearch">
<ion-scroll direction="y" class="scrollArea">
<div class="list">
<a class="item item-border item-avatar pointer" ng-repeat="contact in $parent.filteredContacts = (contactsToBeInvited | filter:search:false)" ng-click="$parent.selectContact(contact)">
<img src="{{ contact.getImage('thumbnail') }}">
<h2>{{contact.getIconName()}}</h2>
<p>City: {{contact.contact.city}}</p>
</a>
</div>
</ion-scroll>
<div class="notFound pointer" ng-hide="filteredContacts.length">
<h3>Nobody found</h3>
<p>You can only search through existing contacts</p>
</div>
</div>
</div>
</form>
</div>
</div>
<div class="form-actions">
<button type="submit" class="button button-block regie-button" ng-click="addRoleMember(registerForm.$valid)">
Sent request
</button>
</div>
</form>
<p class="text-red" style="text-align:center; font-size:14px; font-weight: 400;">* required</p>
</div>
</ion-content>
</ion-view>
As you can see in the view I need to use $parent to the following fields to get it to work:
ng-model="$parent.search_contact"
ng-repeat="contact in $parent.filteredContacts = (contactsToBeInvited | filter:search:false)"
ng-click="$parent.selectContact(contact)"
I really don't understand why this is necessary because the complete view is using the same controller. Does anyone have an idea?
This is a very typical angular scoping issue. Ion-view creates a new child scope and uses prototypical inheritance: https://github.com/angular/angular.js/wiki/Understanding-Scopes
Three are several ways to solve:
always use a dot: https://egghead.io/lessons/angularjs-the-dot
use the 'controller as' syntax: http://www.johnpapa.net/angularjss-controller-as-and-the-vm-variable/
The problem is the inheritance. Between your controller's scope and those fields, there are several new scopes (ion-content, ion-scroll and probably others).
So for example the ng-model="search_content". When you write in there, it is going to create a search_content variable inside the ion-content scope (or an intermediary scope if there is any that I didn't see). Since search_content is being created inside ion-content, your controller won't see it.
If you do $parent.search_content it will create it in the parent content (AKA the controller's scope).
You shouldn't do that, what $parent is for you today, tomorrow it can point to anything else (because you could add a new scope in between without knowing it so $parent will then point to the ion-content.
So instead of doing that, you need to use objects and no primitives, for example:
ng-model="form.search_contact"
Thank to that, it will look for the form object through the prototype chain until it finds it on the controller's scope and use it (just what you need).
Read this which is hundred times better than my explanation.

Resources