.push not working for angularjs and pubnub - angularjs

I am working on pubnub chat. And trying to push new message to the array of messages.
I have an array of message objects like
Object {0: Object, 1: Object, 2: Object, 3: Object, 4: Object, 5: Object, 6: Object, 7: Object, 8: Object, 9: Object, 10: Object, 11: Object, 12: Object, 13: Object, 14: Object, 15: Object, 16: Object, 17: Object, 18: Object, 19: Object}
Each object contains data like
Object {content: "asd", date: "2016-08-29T05:10:41.208Z", sender_username: "siehyvar", sender_uuid: "1294553"}
now I am trying to push another object
Object {content: "hi", sender_uuid: "1294553", sender_username: "siehyvar", date: "2016-08-29T05:47:40.232Z"}
with the following code
$scope.messages: [];
scope.$on(API.getMessageEventNameFor(), function(ngEvent, m) {
scope.$apply(function() {
$scope.messages.push(m);
});
});
And the error I am getting is
messages.push is not a function
I suppose messages here is an array of objects but the .push function is not working. Can somebody please help?
Let me explain the code. So I have one chat Api where I am putting all pubnub code, two controllers(for online users and chat) and two twig templates(for online users and chat)
Messages declaration for current channel ChatApi :
current_channel: {
channel_name: null,
channel_label: null,
messages: []
},
Join chat event from chatApi
joinChat: function (chat_channel,scope) {
var channel = chat_channel.channel;
var channel_name = chat_channel.name;
//Join chatroom Channel with the room name
API.chatrooms.addMe(channel);
//Get online users in chatroom
API.chatrooms.onlineMembers(channel, scope);
//API.current_channel.channel_id = channel;
API.current_channel.channel_name = channel;
API.current_channel.channel_label = channel_name;
console.log(API.current_channel.channel_name);
API.getPresenceEventNameFor();
// Listening to the callbacks for getting messages for current channel
scope.$on(API.getMessageEventNameFor(), function(ngEvent, m) {
scope.$apply(function() {
if (angular.isArray(scope.current_channel.messages)) {
scope.current_channel.messages.push(m);
}else {
console.log("Not is array");
}
});
scroller.down(500);
});
API.getHistory(channel, scope);
Twig for joining chat with user
<ul class="all-chats-list" ng-repeat="onlineUser in online.users">
<li class="col-xs-12 nopadding" ng-click="joinChat(onlineUser.chat_channel); selectMe($event);">
and messages are being displayed as
<li ng-repeat="message in current_channel.messages">
Controller for joining chat
$scope.joinChat = function(chat_channel) {
chatApi.joinChat(chat_channel, $scope);
};
Now when I am calling send message as
Twig:
<form ng-submit="sendMessage()">
<div class="col-xs-9 col-md-10 form-group nopadding">
<input style="width: 100%" ng-model="message.content" class="form-control" type="text" placeholder="Type your message" />
</div>
<div class="col-xs-3 col-md-2 form-group nopadding">
<button type="submit" class="form-control btn site-btn" id="send-message">Send</button>
</div>
</form>
chatApi:
sendMessage: function() {
console.log('PUBLISH TO: ' + API.current_channel.channel_name);
// Don't send an empty message
if (!API.message.content ||
API.message.content === '') {
return;
}
Pubnub.publish({
channel: API.current_channel.channel_name,
message: {
content: API.message.content,
sender_uuid: models.user.uuid,
sender_username: models.user.username,
date: new Date()
},
callback: function(m) {
//console.log(m)
}
});
// Reset the messageContent input
API.message.content = '';
},
Chat controller:
$scope.sendMessage = chatApi.sendMessage;
I am getting the error messages.push is not a function.
I guess it is because it is treating it as an object and not an array.
Hope it is clear now.

Adding to what others have mentioned,
The obvious issue is here:
$scope.messages: [];
am getting the error messages.push is not a function.
Just replace the declaration of $scope.messages with:
$scope.messages = [];
Now you will be able to call push method on $scope.messages array.
In your example $scope.messages was of type object (it should've been of type array).
You can read more about what methods are available to array and object types in javascript here.
The reason why you can't call push on an object an JS is
In case you want to take the tacky road and NOT define an array, then be my guest and create a prototype method push for object types. That way you can call push on objects. That'll be really silly though and you'd run in loads of issues like when trying to pop() or unshift() elements in your object (that you want to treat like an array).

Replacing the line
$scope.messages: [];
by
$scope.messages = [];
may fix your issue.

Related

Filtering an array of JSON

Hey I am following another guide and really struggling to get it working for me. Somewhat new to Angular so I am sure this is a simple issue. Can anyone help me?
The front end shows all the JSON objects at the page load but when I type anything they all disappear.
_ninjaFilter:string
get ninjaFilter():string{
return this._ninjaFilter;
}
set ninjaFilter(value:string){
this._ninjaFilter = value
console.log(this.filteredNinjas, this.ninjaFilter)
this.filteredNinjas = this.ninjaFilter ? this.performFilter(this.ninjaFilter) : this.ninjas
}
performFilter(filterBy: string): any{
filterBy = filterBy.toLocaleLowerCase();
console.log(filterBy)
return this.ninjas.filter(ninja=>{
ninja.name.toLocaleLowerCase().includes(filterBy)
//tried a if statement here to console log match and it does log out match
//also have tried .indexOf(filterby) !== -1
})
}
filteredNinjas: any
ninjas=[{
'name':'yoshi',
'belt':'red'
},
{
'name':'luigi',
'belt':'black'
},
{
'name':'Ryu',
'belt':'green'
}]
constructor(private route: ActivatedRoute) {
this.filteredNinjas = this.ninjas //create new array to filter on
this.ninjaFilter='' //set initial filter string to null
}
and the view:
<h2>Ninja Listing</h2>
<input type='text' id="filter"
[(ngModel)]='ninjaFilter' />
<ul id="ninja-listing">
<li *ngFor="let ninja of filteredNinjas">
<div class='single-ninja'>
<span [ngStyle]="{background: ninja.belt}">{{ninja.belt}} belt</span>
<h3>{{ninja.name}}</h3>
</div>
</li>
</ul>
Here is console log (first page load and then me typing)
(3) [{…}, {…}, {…}] "r"
directory.component.ts:23 r
directory.component.ts:17 [] "ry"
directory.component.ts:23 ry
directory.component.ts:17 [] "ryu"
directory.component.ts:23 ryu
You don't return anything inside your filter function. You should return a condition there:
return this.ninjas.filter(ninja => {
return ninja.name.toLocaleLowerCase().includes(filterBy);
});

AngularJs Auto Complete Search

So this works with static data, but when I push data with a $http this autocomplete does not work. The data pushes to the empty array of airport_list but something is happening when I try to use airport_list in for the autocomplete. Not sure what is is. I can only find answers which pertain to static data.
This is updated per everyones help.
Here is the controller
app.controller('selectCtrl', function($scope, $http) {
$scope.airport_list = null;
$http({
url: 'someUrl.com',
method: 'GET'
})
.then((response) => {
angular.forEach(response.data.airports, function(value, key) {
$scope.airport_list = response.data.airports;
})
$scope.airports = $scope.airport_list;
});
$scope.selectAirport = function(string) {
$scope.airport = string;
$scope.hidelist = true;
};
})
Here is the template
<div class="control">
<div>
<input
type="text"
name="airport"
id="airport"
ng-model="airport"
ng-change="searchFor(airport)"
placeholder="From..."
/>
<div class="airport-container-dropdown" ng-hide="hidelist">
<div
class="airport-list"
ng-repeat="airport in airports"
ng-click="selectAirport(airport)"
>
{{ airport.name }}
</div>
</div>
</div>
</div>
I really would like to do this without using bootstrap typeahead.
Thank you for looking at this.
I have made changes as recommended by below answers and the $http request is feeding into the autocomplete as a whole list but searching by name does not work and clicking on name sets [object, object]
this would be the code which is specific to that functionality.
$scope.searchFor = function(string) {
$scope.hidelist = false;
const output = [];
angular.forEach($scope.airport_list, function(airport) {
if (airport[0].toLowerCase().indexOf(string.toLowerCase(airport)) >=
0) {
output.push(airport);
}
});
$scope.airports = output;
};
$scope.selectAirport = function(string) {
$scope.airport = string;
$scope.hidelist = true;
};
Try this:
$scope.airport_list = response.data.airports;
What I am seeing is that you have an array: $scope.airport_list = [];
When you make your http request, you push what I would understand to be an array of airports into that array. So you end up with your airport array from the backend at the first position of $scope.airport_list, vs. $scope.airport_list being the actual list.
For your search method, you should change the following:
In your HTML:
ng-change="searchFor(airport.name)"
In your JS:
I've renamed your function and changed the input variable to be more clear. You were passing in a full airport, but treating it as a string. You need to compare your provided airport name to that of the airports in the array. So you iterate over the array, and compare each element's name property to what you pass in.
$scope.searchFor = function(airportName) {
$scope.hidelist = false;
const output = [];
angular.forEach($scope.airport_list, function(airport) {
if (airport.name.toLowerCase() === airportName) {
output.push(airport);
}
});
$scope.airports = output;
console.log($scope.airports);
};
I have provided minimal changes to your code to implement this, however I suggest you look at this SO post to filter drop down data more appropriately.
Angularjs Filter data with dropdown
If you want to simply filter out what is displayed in the UI, you can try this in your HTML template. It will provide a text field where you supply a partial of the airport name. If at least one character is entered in that box, the list will display on the page, with the appropriate filtering applied. This will avoid having to call functions on change, having a separate array, etc.
<input type="text" name="airport" id="airport" ng-model="airportSearch.name" placeholder="From..." />
<div class="airport-container-dropdown" ng-hide="!airportSearch.name">
<div class="airport-list"
ng-repeat="airport in airport_list | filter:airportSearch"
ng-click="selectAirport(airport)">
{{ airport.name }}
</div>
</div>

In angularJs Assign a scope variable containing JSON Array of Objects to an Object property inside another JSON Array

My need is to create multiple rows with 2 fields one dropdown and one textbox.
I created a Json array for holding the each row as an object.I want to add the dropdown as an json array of object to my json array of object containing the rows.
My code is below:
//Declare empty array to add new rows
$scope.searchList ={
sources: [{
id: $scope.rowId,
srcCd: '',
srcId:''
//want to add sourceCdMap property and assign the json array which is in another scope variable $scope.sourceCodeMap
}]
}
<table>
<tr ng-repeat="source in searchList.sources track by source.id">
<td>
<div >
<label >Source Name</label>
<div >
<select class="form-control selectWidth" autofocus ng-model="source.srcCd"
ng-change="changeSourceNew(source.id,source.srcCd)"
ng-options="source.value for source in source.sourceCdMap"></select>
</div>
</div>
</td>
<td>
<div >
<label >Source ID </label>
<div><textarea id="source.textarea" autofocus ng-model="source.srcId" rows="4" cols="50" placeholder="enter SrcIds"></textarea>
</div>
</div>
</td>
</tr>
</table>
I want to add a "sourceCdMap" property to the json array of object $scope.searchList which will contain the dropdown values from another json array of object stored inside $scope.sourceCodeMap.
I tried like below but the dropdown is null :
initializeSourceCodeMap();
var initializeSourceCodeMap = function () {
$searchUtilityRemoteService.getSourceCode().then(function (resultMap) {
$scope.sourceCodeMap = resultMap;
$scope.sourceCodeMap.sort(sortSrcCodeBy("value"));
$scope.selected = $scope.sourceCodeMap[0];
$scope.searchIdForm.srcCd = $scope.selected.code;
}, function (reason) {
$scope.sourceCodeMap = null;
//log error
})
};
//Declare empty array to add new rows
$scope.searchList ={
sources: [{
id: $scope.rowId,
srcCd: '',
srcId: '',
sourceCdMap: $scope.sourceCodeMap
}]
}
FYI the "$scope.sourceCodeMap" is working in other parts of the code or when I try to log it in my console.
Any help is appreciated on how to assign this scope variable "$scope.sourceCodeMap" (which is actually a json array of objects in itself) to the new property "sourceCdMap" in the Json Array "$scope.searchList"
Try logging inside this function
function (reason) {
$scope.sourceCodeMap = null;
//log error
}
to see if maybe your $scope.sourceCodeMap is always null because of it.
If that's not the case, try using $scope.apply();, so that the html can see your changes. Beware of asyncronously calling it.

How to get a single item from a GoInstant collection?

How do you get a single item from a GoInstant GoAngular collection? I am trying to create a typical show or edit screen for a single task, but I cannot get any of the task's data to appear.
Here is my AngularJS controller:
.controller('TaskCtrl', function($scope, $stateParams, $goKey) {
$scope.tasks = $goKey('tasks').$sync();
$scope.tasks.$on('ready', function() {
$scope.task = $scope.tasks.$key($stateParams.taskId);
//$scope.task = $scope.tasks.$key('id-146b1c09a84-000-0'); //I tried this too
});
});
And here is the corresponding AngularJS template:
<div class="card">
<ul class="table-view">
<li class="table-view-cell"><h4>{{ task.name }}</h4></li>
</ul>
</div>
Nothing is rendered with {{ task.name }} or by referencing any of the task's properties. Any help will be greatly appreciated.
You might handle these tasks: (a) retrieving a single item from a collection, and (b) responding to a users direction to change application state differently.
Keep in mind, that a GoAngular model (returned by $sync()) is an object, which in the case of a collection of todos might look something like this:
{
"id-146ce1c6c9e-000-0": { "description": "Destroy the Death Start" },
"id-146ce1c6c9e-000-0": { "description": "Defeat the Emperor" }
}
It will of course, have a number of methods too, those can be easily stripped using the $omit method.
If we wanted to retrieve a single item from a collection that had already been synced, we might do it like this (plunkr):
$scope.todos.$sync();
$scope.todos.$on('ready', function() {
var firstKey = (function (obj) {
for (var firstKey in obj) return firstKey;
})($scope.todos.$omit());
$scope.firstTodo = $scope.todos[firstKey].description;
});
In this example, we synchronize the collection, and once it's ready retrieve the key for the first item in the collection, and assign a reference to that item to $scope.firstTodo.
If we are responding to a users input, we'll need the ID to be passed from the view based on a user's interaction, back to the controller. First we'll update our view:
<li ng-repeat="(id, todo) in todos">
{{ todo.description }}
</li>
Now we know which todo the user want's us to modify, we describe that behavior in our controller:
$scope.todos.$sync();
$scope.whichTask = function(todoId) {
console.log('this one:', $scope.todos[todoId]);
// Remove for fun
$scope.todos.$key(todoId).$remove();
}
Here's a working example: plunkr. Hope this helps :)

Backbone.js create method not sending parameters to sinatra

I'm new to backbone and trying to set it up in Sinatra, but I can't seem to get a simple create working.
I've set up my model/collection as so:
var TEAM_ID = window.location.pathname.split('/')[1]; // From url
$(function () {
var TeamMember = Backbone.Model.extend({
defaults: {
name : ""
}
});
var TeamMembers = Backbone.Collection.extend({
model: TeamMember,
url: "/" + TEAM_ID + "/team-members.json"
});
var teamMembers = new TeamMembers;
var TeamMemberView = Backbone.View.extend({
events: {
"click #new-team-member-form .submit-button" : "handleNewTeamMember"
},
handleNewTeamMember: function(data) {
var inputField = $('input[name=new_team_member_name]');
console.log("Pre create");
// This doesn't get sent to the server!!
var teamMember = teamMembers.create({name: inputField.val());
console.log("Post create");
return false; // Don't submit form
},
render: function() {
console.log("Render team member");
return this;
}
});
// ...
var teamMemberView = new TeamMemberView({el: $('#week-view')});
});
The html looks like:
<table id="week-view">
<!-- ... -->
<form id="new-team-member-form" action="/some-add-url" method="post">
<fieldset class="new-object-fieldset" title="New team member">
<legend>New team member</legend>
<label for="new_team_member_name">Add new</label>
<input type="text" name="new_team_member_name" title="Add member" class="new-object-text-box" />
<button type="submit" name="new_team_member" value="new_team_member" class="submit-button">+</button>
<div id="help-new"></div>
</fieldset> <!-- New team member -->
</form>
<!-- ... -->
and the ruby looks like:
post '/:team_id/team-members.json' do
logger.info("Add team member (json): #{params}")
end
However, the sinatra server only shows params[:team_id], without the name parameter on the teamMembers.create line. Am I doing something stupid in backbone? Not initialising something properly?
I've looked at http://documentcloud.github.com/backbone/#Collection-create,
http://documentcloud.github.com/backbone/docs/todos.html, http://liquidmedia.ca/blog/2011/01/backbone-js-part-1/, http://liquidmedia.ca/blog/2011/01/an-intro-to-backbone-js-part-2-controllers-and-views/ and https://gist.github.com/1655019, but I can't seem to find any answers there. I feel like I've done something stupid, but just can't see it!
It turns out, it was me not knowing how to extract json parameters in sinatra properly. From this site: http://mini.softwareas.com/posting-json-to-a-sinatra-mongodb-service, I found out I had to use request.body.read.to_s instead of the params hash ie,
post '/:team_id/team-members.json' do
request_body = JSON.parse(request.body.read.to_s)
team_member_name = request_body["name"]
# ...
end
I had the same problem. I am on PHP, though. Since Backbone sends POST data not in a query string, but rather in a plain JSON string, the data is not available thru $_POST. To read the Backbone POST data:
// the 'true' param returns an array rather than an object
$post = json_decode(file_get_contents('php://input'), true);
You can also read it directly from $HTTP_RAW_POST_DATA.

Resources