$.getJson won't affect view until event - javascript

Didn't really know how to word the question. Using angular. Anyways, I'm trying to have it so when the user types in the text box a state, once it has been verified that the state exists (comparing to an array of all 50), it will automatically call a getJSon Jquery request for a JSON object. But for some reason, it doesn't execute right away, instead I have to press a key after doing so.
Code:
$scope.checkState = function(team) {
//check for team
console.log("Searching for " + document.getElementById('team').value)
var teamFind = document.getElementById('team').value;
var team = $.inArray(teamFind, $scope.states);
console.log("team: " + team);
if (team == -1)
{
console.log("Not found");
$scope.selectedState = "Not Found";
teamFound = false;
}
//correct team
if (team > -1)
{
$.getJSON('https://api.myjson.com/bins/1ak21r', function (data) {
//console.log(data.bowl);
$scope.items = data;
console.log($scope.items);
console.log("Team Bowl: " + team_bowl)
console.log("Found: " + $scope.states[team]);
$scope.selectedState = $scope.states[team];
teamFound = true;
});
}
}
html code
<p class="w3-large w3-center">
<input type="text" name="team" id="team" value="Whats Your Team?" ng-keyup="checkState(team)">
</p>
<p class="w3-jumbo w3-center">
<span id="bowl">{{ selectedState }}</span>
</p>
<p class="w3-large w3-center">
<span>f{{ items }}</span>
</p>
</div>
I know it might be hard to understand what I want, but I was creating a sample application that would show what bowl game a team was competing in. The user types the team into the text-box id=team below, and I wanted it done without them having to press enter or submit.
On the key-up, it runs the check function. So for example, once Maryland is entered, the getJson will run and correctly logs the data, but the $scope.items isn't updated until after I type one more key AFTER I entered the state
So like typing:
M-A-R-Y-L-A-N-D(CONSOLE LOGS THE JSON OBJECT CORRECTLY, BUT ON THE HTML {{ items }} STILL SHOWS NOTHING)-any_key_here(NOW IT GETS UPDATED)
Thanks for any help.
EDIT:
So I was able to get it by having another function be called with the data inside the JSON function.
myFunction(data);
which calls
function myFunction(items) {
console.log(items);
document.getElementById('bowl2').innerHTML = items.team;
};

You are using jQuery's ajax method which does not integrate with angular to inform angular when it is done. It's best to use angular's built in $http methods which do the same thing expect when they complete they trigger a digest cycle which will update the view.
You will need to inject $http into your controller or service (depending on where this code lives) in order to use it. Once you do that, you should be able to make the following modification to you code.
Change
$.getJSON('https://api.myjson.com/bins/1ak21r', function (data) {
//console.log(data.bowl);
$scope.items = data;
console.log($scope.items);
console.log("Team Bowl: " + team_bowl)
console.log("Found: " + $scope.states[team]);
$scope.selectedState = $scope.states[team];
teamFound = true;
});
to
$http.get('https://api.myjson.com/bins/1ak21r')
.then(function (response) {
var data = response.data;
//console.log(data.bowl);
$scope.items = data;
console.log($scope.items);
console.log("Team Bowl: " + team_bowl)
console.log("Found: " + $scope.states[team]);
$scope.selectedState = $scope.states[team];
teamFound = true;
});

Related

md-autocomplete doesn't work after once

I've made a simple auto complete functionality on text search. There are two functions - querySearch(searchText) and homeresults(item). The querySearch function correctly fetches the results and there is no issue there. Once a result is selected by the user, then it calls homeresults function. This function works fine for the first time, but doesn't work after that unless I return to home page. So once a user is on results page, he can't use the search bar again unless he comes back to home page. There are no errors. Here is my code snippet:
HTML
<form ng-submit="$event.preventDefault()">
<md-autocomplete md-selected-item="selectedItem" md-search-text="searchText" md-items="item in querySearch(searchText)" md-item-text="item.title" md-delay="400" md-min-length="2" md-floating-label="Search for any Query">
<div layout-align="start center">
<span md-highlight-text="searchText" ng-click="homeresult(item)">{{item.title}}</span>
</div>
<md-not-found>No matches found </md-not-found>
</md-autocomplete>
Javascript
$scope.querySearch = function(search) {
var key = $scope.searchText;
console.log('keyword searched by user is ' + key);
return $http.get('/api/searchresult?title=' + key)
.then(function(response) {
return response.data
});
};
$scope.homeresult = function(item) {
console.log(item);
$timeout(function() {
item.title = item.title.replace(/ /g, '-');
$location.url('/results').search({
"title": item.title
})
}, 50);
};
In the homeresult(), code is redirecting to /results. So, instead of redirection, if you show results on the same page where md-autocomplete is defined, it will work. You can add a simple check to hide the results until we get response from $http.

AngularJS - run check against elements as they come back

I just followed this
JSFiddle example to create a little search box from an array object in my javascript. Now after some tweaking and research on search.object and filter:search:strict. Now that I have it filtering correctly, I modified a checkbox in the template that is checked upon loading the document and switched on or off based on a custom data attribute in the html that is updated by the json array.
How do I run this check once someone clears the search and the old items show back up again?
In the HTML I have this template
<div ng-repeat="o in objects | filter:search:strict | orderBy:'obj_name'" class="objectContainer">
<h3>{{o.obj_name}}</h3>
<label class="userToggleSwitch" >
<input class="userToggleInput" data-isactive="{{o.obj_isactive}}" type="checkbox">
<div class="slider round"></div>
</label>
</div>
In the JS
angular.element(document).ready(function(){
angular.module('searchApp',[])
.controller('searchCtrl', ['$scope','objHolder',function ($scope,objHolder) {
$scope.search = '';
$scope.objects = [];
$scope.objects = objHolder.getobjects();
}])
// fake service, substitute with your server call ($http)
.factory('objHolder',function(){
var objects = objList;
return {
getobjects : function(){
return objects;
}
};
});
});
The JS that modifies the items on document load is using jquery like this
$(document).ready(function(){
//console.log($('.userToggleInput'));
$.each($('.userToggleInput'), function(){
if($(this).data("isactive") == 1){$(this).attr('checked', true);}
if($(this).data("isactive") == 0){$(this).attr('checked', false);}
})
$('.userToggleInput').click(function(){
if ($(this).attr('checked') == undefined) {
// THIS IS WHERE WE WILL MAKE AN AJAX CALL TO A PHP CLASS
// TO REQUEST IF THE USER CAN BE TURNED ON - DUE TO LIC RESTRICTIONS
$(this).attr('checked',true);
} else {
$(this).attr('checked',false);
}
//console.log($(this).attr('checked'));
});
});
Created JS Fiddle to assist in Helping in this manner

Conflicts when working with scopes and controllers in AngularJS

I have a simple website that uses AngularJS with a NodeJS backend.
It has multiple pages, like a homepage, a login/register page, etc.
I'd like to implement a "Chat" page where you could send messages to other clients using socket.io. I already got that part working, using a local controller (by local, I mean active on a single page - the Chat page).
The problem is, I would like the chat system to be global (i.e. client can receive messages while being on the homepage, but they'll still only be displayed when going back on the Chat page).
I'm having an issue when setting the Chat controller global (active on all pages).
Here's how I'm including it:
<body ng-controller="AppCtrl"> <!-- include main controller -->
<div ng-include="'header.tpl.html'"></div>
<div ng-controller="ChatCtrl" class="page"> <!-- include global Chat controller -->
<div ng-view class="container"></div>
</div>
<div ng-include="'footer.tpl.html'"></div>
<!-- ...etc. -->
</body>
This works pretty well, but it seems like I can't access a value from my Chat page, though. Functions declared from the Chat controller can still be called, but the "$scope.message" value (which contains the message that's being typed) is always empty.
Here's my Chat controller (which is actually called TravelCtrl)
angular.module('base').controller('TravelCtrl', //['$scope', 'security',
function($rootScope, $scope, security, NgMap, $geolocation, socket){
$scope.messages = [];
// Socket listeners
// ================
socket.on('init', function (data) {
$scope.name = data.name;
$scope.users = data.users;
});
socket.on('send:message', function (message) {
$scope.messages.push(message);
});
socket.on('change:name', function (data) {
changeName(data.oldName, data.newName);
});
socket.on('user:join', function (data) {
$scope.messages.push({
user: 'Server',
text: 'User ' + data.name + ' has joined.'
});
$scope.users.push(data.name);
});
// add a message to the conversation when a user disconnects or leaves the room
socket.on('user:left', function (data) {
$scope.messages.push({
user: 'chatroom',
text: 'User ' + data.name + ' has left.'
});
var i, user;
for (i = 0; i < $scope.users.length; i++) {
user = $scope.users[i];
if (user === data.name) {
$scope.users.splice(i, 1);
break;
}
}
});
// Private helpers
// ===============
var changeName = function (oldName, newName) {
// rename user in list of users
var i;
for (i = 0; i < $scope.users.length; i++) {
if ($scope.users[i] === oldName) {
$scope.users[i] = newName;
}
}
$scope.messages.push({
user: 'Server',
text: 'User ' + oldName + ' has been authenticated as ' + newName + '.'
});
}
// Methods published to the scope
// ==============================
$scope.changeName = function () {
socket.emit('change:name', {
name: $scope.newName
}, function (result) {
if (!result) {
alert('There was an error changing your name');
} else {
changeName($scope.name, $scope.newName);
$scope.name = $scope.newName;
$scope.newName = '';
}
});
};
$scope.sendMessage = function () {
socket.emit('send:message', {
message: $scope.message
});
// add the message to our model locally
$scope.messages.push({
user: $scope.name,
text: $scope.message
});
// clear message box
$scope.message = '';
};
// ================
var init = function () {
$scope.newName = security.currentUser.username;
$scope.changeName();
}
if ($rootScope.hasLoaded() && $scope.name != security.currentUser.username) {
init();
} else {
$rootScope.$on('info-loaded', init);
}
}
//]
);
As well as the Chat page itself. The strange thing is that connected users and messages display correctly, but the controller can't seem to retrieve the typed message.
<div class='col'>
<h3>Users</h3>
<div class='overflowable'>
<p ng-repeat='user in users'>{{user}}</p>
</div>
</div>
<div class='col'>
<h3>Messages</h3>
<div class='overflowable'>
<p ng-repeat='message in messages' ng-class='{alert: message.user == "chatroom"}'>{{message.user}}: {{message.text}}</p>
</div>
</div>
<div class='clr'>
<form ng-submit='sendMessage()'>
Message: {{message}}<br/>
<input size='60', ng-model='message'/>
<input type='submit', value='Send as {{name}}'/>
</form>
</div>
When pressing the "Send" button, AngularJS successfully calls the sendMessage function, but retrieves the "message" value as an empty string, leading it to send an empty socket.io message.
I'm quite new to AngularJS, so my approach might be totally ridiculous. I'm convinced I'm missing something obvious but after re-reading the docs again, I really can't seem to find what.
Is this a proper way to organise an AngularJS app?
Thanks in advance for your help.
Having recently built a large scale Angular/Socket.IO application, I strongly suggest that you put all of your Socket implementation into a Service. This service will maintain all of your socket state, and allow you to inject it into any required controllers. This will allow you to have a main page for Chat, however still be able to display notifications, chat user information, etc in other areas of your application.
It's not about your problem, but I saw something I suspect to be wrong.
When you use another library with angularjs, you should use a bridge to it (angular-socket-io for example).
When you do an $http call with angular, it updates $scope correctly in the callback and the changes are seen in the view.
In your code:
socket.on('send:message', function (message) {
$scope.messages.push(message);
});
There is a problem: "socket" isn't a library included in angularjs, so when the callback is called, your "$scope" modification isn't correctly noticed to angularjs.
You have to do use $scope.$apply(function() { code here which modifies $scope });
Example:
socket.on('send:message', function (message) {
$scope.$apply(function() {
$scope.messages.push(message);
});
});
EDIT:
I would like the chat system to be global (i.e. client can receive messages while being on the homepage, but they'll still only be displayed when going back on the Chat page).
Either store the datas in a global variable, or use $rootScope which is the parent scope of all the $scope you use in the application.
EDIT 2:
In fact it should solve your problem ;)
Another things:
1) use $rootScope instead of $scope for global variables (or a global variable). In any $scope you will access $rootScope variables ($scope is a copy of either $rooScope or a parent $scope).
2) register socket.io only once. Currently, if you change pages, you will register new callbacks at EACH page change.

Stop a For Loop From Repeating in AngularJS

I'm trying to replace some html code with Javascript when a user searches for an email. I have it working correctly, but for some reason the error displays around 20+ times, so it will replace the div and say
"user does existuser does existuser does existuser does existuser does existuser does exist"
instead of just putting the error message once. Any idea how I can fix it?
$scope.checkEmail = function
findUsersMatchingEmail(emailAddress) {
ref.child('users').orderByChild('email').
equalTo($scope.emailAddress).once('value', function (snap) {
var output = '<div>',
myError = document.querySelectorAll('#d');
for (var key in arguments[0]) {
output += (snap.name() +
(snap.val() === null ? ' Does Not' : ' does') + ' exist');
}
output += '</div>';
for (var i = myError.length - 1; i >= 0; i--) {
myError[i].innerHTML = output;
}
});
};
As I wrote in the comments, never manipulate the DOM from anywhere but the link function of a directive. Explaining why is probably out of scope for this answer, and has been answered multiple times here on SO. A very good read is:
“Thinking in AngularJS” if I have a jQuery background?
and A Conceptual Introduction to AngularJS
So to make it short, here's a very simple demo of how you could handle your error messages.
Note that this is by no means the only nor the best way to do it. There's ngMessages or form validation in general which could be used as well.
(function (app) {
'use strict';
app.controller('EmailCtrl', ['$scope', function ($scope) {
$scope.errors = [];
$scope.checkEmail = function findUsersMatchingEmail(emailAddress) {
// clear previous errors
$scope.errors.length = 0;
// check email and add errors if needed
// using your service (ref)
$scope.errors.push({
message: 'email not unique ' + (Math.random()) // random is only used to show that the errors actually change
});
};
}]);
})(angular.module('app', []));
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.3/angular.min.js"></script>
<div data-ng-app="app" data-ng-controller="EmailCtrl">
<ul>
<li data-ng-repeat="error in errors">{{ error.message }}</li>
</ul>
<form data-ng-submit="checkEmail(email)">
<input data-ng-model="email" placeholder="email">
</form>
</div>
Side note: I would normally use the controller as syntax, though I did not want to introduce any more new topics.

How reload the helper once i change the values

Im trying to filter information in my page after the user select an option, so i made a "change" event and passed values ​​through the helper by sessions and its ok console log looks like the values passed ok, but it seems like the helper doesn't refresh or something because dont return the find with the new values, actually don't do anything, obviously there is something wrong, sorry for my ultra basic english, i hope you understand and help me, thanks for your time!
Template
<select id="mostrarTiposMenu" class="form-control">
{{#each mostrarMenus}}
<option value="{{nuevoTipoMenu}}" onchange="getMenu()">{{nuevoTipoMenu}}</option>
{{/each}}
</select>
Events and helpers
Template.main.events({
"change #mostrarTiposMenu": function(evt) {
var newValue = $(evt.target).val();
Session.set("valueMenu", newValue);
console.log("yo estoy en el event " + newValue);
}
});
Template.main.helpers({
mostrarMenus : function(){
return Menu.find();
},
mostrarPrimerDia: function(){
var searchMenu = Session.get("valueMenu")
console.log("Yo estoy en el helper " + searchMenu)
var server = TimeSync.serverTime()
var diaDeHoy = moment(server).locale("es").add(0,'days').format('dddd');
return Promociones.find({'metadata.diaOferta' : { $in: [diaDeHoy] } },{'metadata.tipoMenu' : { $in: [searchMenu] } });
}
});
did you tried using refresh option
$('selectmenu).refresh();

Categories

Resources