Update react component using postal.js - javascript

I am trying to use postal.js subscribe/publish data in my reactJs site, I am currently doing this. Can anyone tell me how to push the selected id, I think the loadContacts method is resetting the value to false:
This is my top level page:
// load initial contacts into page
loadContacts: function() {
var page = this;
ContactDirectoryService.getContacts(this.state.pageNumber, function(response) {
var contacts = response.contacts.map(function(contact){
contact.isSelected = false;
return contact;
});
page.setState({ contacts: contacts });
});
},
// postal subscribe to receive publish
componentDidMount: function() {
this.loadContacts();
var page = this;
contactChannel.subscribe("selectedContact", function(data, envelope) {
page.handleSelectedContact(data.id, page);
});
},
handleSelectedContact: function(id, page) {
var page = this;
// service to add contact using api call
BasketService.addPerson(id, function () {
console.log(id);
var arrayPush = [];
var arrayPush = page.state.selectedContacts.slice();
// push selected id to selectedContacts array
arrayPush.push(id);
page.setState({selectedContacts: arrayPush})
//add is selected to contacts
page.setState({ contacts: contacts });
// push selected id which isn't working
for(var i=0;i<page.state.contacts.length;i++)
{
var idAsNumber = parseInt(id);
if (page.state.contacts[i].id === idAsNumber) {
page.state.contacts[i].isSelected = true;
break;
}
}
basketChannel.publish({
channel: "basket",
topic: "addContactToBasket",
data: {
id: id,
newTotal: arrayPush.length
}
});
});
},
addContactToBasket: function(selectedId) {
console.log('Add ID');
console.log('Add ID');
BasketService.addPerson(selectedId, function () {
var arrayPush = [];
var arrayPush = this.state.selectedContacts.slice();
arrayPush.push(selectedId);
this.setState({selectedContacts: arrayPush})
person.isSelected = true;
basketChannel.publish({
channel: "basket",
topic: "addContactToBasket",
data: {
id: selectedId,
newTotal: arrayPush.length
}
});
});
},
Checkbox component page, to select id and publish to selectedContact channel
handler: function(e) {
e.target.value;
e.preventDefault();
channel.publish({
channel: "contact",
topic: "selectedContact",
data: {
id: e.target.attributes['data-ref'].value
}
});
},
render: function() {
return (
<div className="contact-selector">
<input type="checkbox"
checked={this.props.data.isSelected}
onChange={this.handler} />
</div>
);
},

You're passing a cached context as page, however in the first line of handleSelectedContact() you're also reinitialising the argument page into a fresh local copy of this.

Related

Cannot access data from component method

I tried components methods in vue js. My code like this.
const Thread = Vue.component('threadpage', function(resolve) {
$.get('templates/thread.html').done(function(template) {
resolve({
template: template,
data: function() {
return {
data: {
title: "Data Table",
count: this.GetData
}
};
},
methods: {
GetData: function() {
var data = {
username : "newshubid",
data : {
page : 0,
length : 10,
schedule : "desc"
}
};
var args = {"data" : JSON.stringify(data)};
var params = $.param(args);
var url = "http://example-url";
var result;
DoXhr(url, params, function(response){
result = JSON.parse(response).data;
console.log("load 1", result);
});
setTimeout(function () {
console.log("load 2", result);
return result;
}, 1000);
}
},
created: function(){
this.GetData();
}
});
});
});
But, when I trying to use {{ data.count }} in template. Not showing result what i want. Even I tried return result in GetData.
Whats my problem ? And how to access data from methods ? Please help me, i'm a beginner. Thanks
See the edited code and comments I added below.
You tried to return the result by using return in the function from setTimeout, which won't help you return value from GetData.
Instead, You can just set the value in the callback function of your ajax request.
const Thread = Vue.component('threadpage', function(resolve) {
$.get('templates/thread.html').done(function(template) {
resolve({
template: template,
data: function() {
return {
data: {
title: "Data Table",
// NOTE just set an init value to count, it will be refreshed when the function in "created" invoked.
count: /* this.GetData */ {}
}
};
},
methods: {
GetData: function() {
var data = {
username : "newshubid",
data : {
page : 0,
length : 10,
schedule : "desc"
}
};
var args = {"data" : JSON.stringify(data)};
var params = $.param(args);
var url = "http://example-url";
var result;
var vm = this;
DoXhr(url, params, function(response){
result = JSON.parse(response).data;
// NOTE set data.count to responsed result in callback function directly.
vm.data.count = result;
});
// NOTE I think you don't need code below anymore.
// setTimeout(function () {
// console.log("load 2", result);
// return result;
// }, 1000);
}
},
created: function(){
this.GetData();
}
});
});
});

Jasmine react component, test keeps failing

I'm trying to pass a jasmine test for my react component, I would like to test the select all checkbox unchecks when the publish event is fired from one of the checked checkboxs. As not all all of the checkboxes will then be checked, so the select all needs to uncheck. Below is my jasmine test and component, I'm new to testing react components anyhelp much appreciated:
Currently get error, undefined is not an object this.stubs.subscribe
var React = require('react/addons');
var postal = require('postal');
var contactChannel = postal.channel("contact")
var SelectAll = require('../../../components/controls/SelectAll');
var channelStub = require('../../stub/channelStub');
// When subscribe event check checkbox
describe("Select All Checkbox Specification", function() {
describe("Checking deselect response", function () {
it("On receive subscribe event uncheck the select all box", function () {
// when the select all is created it will create the channel
// publish an event to the channel (de-select event)
instance = TestUtils.renderIntoDocument(<SelectAll />);
// publish event to check all checkboxes
contactChannel.publish({
channel: 'contact',
topic: 'selectAll',
data: {
selectAll: true
}});
var checkbox = TestUtils.findRenderedDOMComponentWithTag(instance, "input");
// publish event to uncheck select all checkbox
contactChannel.publish({
channel: "basket",
topic: "deselectedContact",
data: {}
});
// Checking the module - to see if the checkbox inside is unchecked
var checkbox = TestUtils.findRenderedDOMComponentWithTag(instance, "input");
var data = this.stubbed.subscribe();
// check box checked should be false
expect(data.topic === 'deselectedContact');
expect(input.checked === false);
});
React component:
var postal = require('postal');var postal = require('postal');
var contactChannel = postal.channel("contact");
var React = require('react');
var SelectAll = React.createClass({
getInitialState: function() {
return {
checked:false
};
},
setUnChecked: function(){
this.setState({checked: false});
},
handler: function(e) {
var updatedContacts = [],
contacts = this.props.data.contacts,
topic = 'selectAll',
checked = false,
channel = 'contact';
contactChannel.publish({
channel: channel,
topic: topic,
data: {
selectAll: event.target.checked
}});
this.setState({checked: event.target.checked});
},
render: function() {
return (
<div className="contact-selector">
<input type="checkbox" checked={this.state.checked}
onChange={this.handler} ref="checkAll" />
</div>
);
},
componentDidMount: function() {
var self = this;
contactChannel.subscribe("deselectedContact", function(data) {
self.setUnChecked();
});
}
});
module.exports = SelectAll;
ChannelStub:
var lastPublished = {},
lastTopicSubscribed = '',
lastCallbackSubscribed;
var ChannelStub = {
publish: function(data) {
lastPublished = data;
},
getLastPublished: function() {
return lastPublished;
},
subscribe: function(topic, callback) {
lastCallbackSubscribed = callback;
lastTopicSubscribed = topic;
}
};
module.exports = ChannelStub;

Windows Azure + DevExrpess (PhoneJs) getting ToDoList (Standart Sample)

I'm starting to learn and azure phonejs.
Todo list get through a standard example:
$(function() {
var client = new WindowsAzure.MobileServiceClient('https://zaburrito.azure-mobile.net/', 'key');
var todoItemTable = client.getTable('todoitem');
// Read current data and rebuild UI.
// If you plan to generate complex UIs like this, consider using a JavaScript templating library.
function refreshTodoItems() {
var query = todoItemTable.where({ complete: false });
query.read().then(function(todoItems) {
var listItems = $.map(todoItems, function(item) {
return $('<li>')
.attr('data-todoitem-id', item.id)
.append($('<button class="item-delete">Delete</button>'))
.append($('<input type="checkbox" class="item-complete">').prop('checked', item.complete))
.append($('<div>').append($('<input class="item-text">').val(item.text)));
});
$('#todo-items').empty().append(listItems).toggle(listItems.length > 0);
$('#summary').html('<strong>' + todoItems.length + '</strong> item(s)');
}, handleError);
}
function handleError(error) {
var text = error + (error.request ? ' - ' + error.request.status : '');
$('#errorlog').append($('<li>').text(text));
}
function getTodoItemId(formElement) {
return $(formElement).closest('li').attr('data-todoitem-id');
}
// Handle insert
$('#add-item').submit(function(evt) {
var textbox = $('#new-item-text'),
itemText = textbox.val();
if (itemText !== '') {
todoItemTable.insert({ text: itemText, complete: false }).then(refreshTodoItems, handleError);
}
textbox.val('').focus();
evt.preventDefault();
});
// Handle update
$(document.body).on('change', '.item-text', function() {
var newText = $(this).val();
todoItemTable.update({ id: getTodoItemId(this), text: newText }).then(null, handleError);
});
$(document.body).on('change', '.item-complete', function() {
var isComplete = $(this).prop('checked');
todoItemTable.update({ id: getTodoItemId(this), complete: isComplete }).then(refreshTodoItems, handleError);
});
// Handle delete
$(document.body).on('click', '.item-delete', function () {
todoItemTable.del({ id: getTodoItemId(this) }).then(refreshTodoItems, handleError);
});
// On initial load, start by fetching the current data
refreshTodoItems();
});
and it works!
Changed for the use of phonejs and the program stops working, even mistakes does not issue!
This my View:
<div data-options="dxView : { name: 'home', title: 'Home' } " >
<div class="home-view" data-options="dxContent : { targetPlaceholder: 'content' } " >
<button data-bind="click: incrementClickCounter">Click me</button>
<span data-bind="text: listData"></span>
<div data-bind="dxList:{
dataSource: listData,
itemTemplate:'toDoItemTemplate'}">
<div data-options="dxTemplate:{ name:'toDoItemTemplate' }">
<div style="float:left; width:100%;">
<h1 data-bind="text: name"></h1>
</div>
</div>
</div>
</div>
This my ViewModel:
Application1.home = function (params) {
var client = new WindowsAzure.MobileServiceClient('https://zaburrito.azure-mobile.net/', 'key');
var todoItemTable = client.getTable('todoitem');
var toDoArray = ko.observableArray([
{ name: "111", type: "111" },
{ name: "222", type: "222" }]);
var query = todoItemTable.where({ complete: false });
query.read().then(function (todoItems) {
for (var i = 0; i < todoItems.length; i++) {
toDoArray.push({ name: todoItems[i].text, type: "NEW!" });
}
});
var viewModel = {
listData: toDoArray,
incrementClickCounter: function () {
todoItemTable = client.getTable('todoitem');
toDoArray.push({ name: "Zippy", type: "Unknown" });
}
};
return viewModel;
};
I can easily add items to the list of programs, but from the server list does not come:-(
I am driven to exhaustion and can not solve the problem for 3 days, which is critical for me!
Specify where my mistake! Thank U!
I suggest you use a DevExpress.data.DataSource and a DevExpress.data.CustomStore instead of ko.observableArray.
Application1.home = function (params) {
var client = new WindowsAzure.MobileServiceClient('https://zaburrito.azure-mobile.net/', 'key');
var todoItemTable = client.getTable('todoitem');
var toDoArray = [];
var store = new DevExpress.data.CustomStore({
load: function(loadOptions) {
var d = $.Deferred();
if(toDoArray.length) {
d.resolve(toDoArray);
} else {
todoItemTable
.where({ complete: false })
.read()
.then(function(todoItems) {
for (var i = 0; i < todoItems.length; i++) {
toDoArray.push({ name: todoItems[i].text, type: "NEW!" });
}
d.resolve(toDoArray);
});
}
return d.promise();
},
insert: function(values) {
return toDoArray.push(values) - 1;
},
remove: function(key) {
if (!(key in toDoArray))
throw Error("Unknown key");
toDoArray.splice(key, 1);
},
update: function(key, values) {
if (!(key in toDoArray))
throw Error("Unknown key");
toDoArray[key] = $.extend(true, toDoArray[key], values);
}
});
var source = new DevExpress.data.DataSource(store);
// older version
store.modified.add(function() { source.load(); });
// starting from 14.2:
// store.on("modified", function() { source.load(); });
var viewModel = {
listData: source,
incrementClickCounter: function () {
store.insert({ name: "Zippy", type: "Unknown" });
}
};
return viewModel;
}
You can read more about it here and here.

Backbone object fields are from previous item

I've just started using Backbone.js and my test cases are churning up something pretty weird.
In short, what I am experiencing is -- after I call a Backbone Model's constructor, some of the fields in my object seem to come from a previously item. For instance, if I call:
var playlist = new Playlist({
title: playlistTitle,
position: playlists.length,
userId: user.id
});
playlist.get('items').length; //1
however if I do:
var playlist = new Playlist({
title: playlistTitle,
position: playlists.length,
userId: user.id,
items: []
});
playlist.get('items').length; //0
Here's the code:
define(['ytHelper', 'songManager', 'playlistItem'], function (ytHelper, songManager, PlaylistItem) {
'use strict';
var Playlist = Backbone.Model.extend({
defaults: {
id: null,
userId: null,
title: 'New Playlist',
selected: false,
position: 0,
shuffledItems: [],
history: [],
items: []
},
initialize: function () {
//Our playlistItem data was fetched from the server with the playlist. Need to convert the collection to Backbone Model entities.
if (this.get('items').length > 0) {
console.log("Initializing a Playlist object with an item count of:", this.get('items').length);
console.log("items[0]", this.get('items')[0]);
this.set('items', _.map(this.get('items'), function (playlistItemData) {
var returnValue;
//This is a bit more robust. If any items in our playlist weren't Backbone.Models (could be loaded from server data), auto-convert during init.
if (playlistItemData instanceof Backbone.Model) {
returnValue = playlistItemData;
} else {
returnValue = new PlaylistItem(playlistItemData);
}
return returnValue;
}));
//Playlists will remember their length via localStorage w/ their ID.
var savedItemPosition = JSON.parse(localStorage.getItem(this.get('id') + '_selectedItemPosition'));
this.selectItemByPosition(savedItemPosition != null ? parseInt(savedItemPosition) : 0);
var songIds = _.map(this.get('items'), function(item) {
return item.get('songId');
});
songManager.loadSongs(songIds);
this.set('shuffledItems', _.shuffle(this.get('items')));
}
},
//TODO: Reimplemnt using Backbone.sync w/ CRUD operations on backend.
save: function(callback) {
if (this.get('items').length > 0) {
var selectedItem = this.getSelectedItem();
localStorage.setItem(this.get('id') + '_selectedItemPosition', selectedItem.get('position'));
}
var self = this;
console.log("Calling save with:", self);
console.log("my position is:", self.get('position'));
$.ajax({
url: 'http://localhost:61975/Playlist/SavePlaylist',
type: 'POST',
dataType: 'json',
contentType: 'application/json; charset=utf-8',
data: JSON.stringify(self),
success: function (data) {
console.log('Saving playlist was successful.', data);
self.set('id', data.id);
if (callback) {
callback();
}
},
error: function (error) {
console.error("Saving playlist was unsuccessful", error);
}
});
},
selectItemByPosition: function(position) {
//Deselect the currently selected item, then select the new item to have selected.
var currentlySelected = this.getSelectedItem();
//currentlySelected is not defined for a brand new playlist since we have no items yet selected.
if (currentlySelected != null && currentlySelected.position != position) {
currentlySelected.set('selected', false);
}
var item = this.getItemByPosition(position);
if (item != null && item.position != position) {
item.set('selected', true);
localStorage.setItem(this.get('id') + '_selectedItemPosition', item.get('position'));
}
return item;
},
getItemByPosition: function (position) {
return _.find(this.get('items'), function(item) {
return item.get('position') == position;
});
},
addItem: function (song, selected) {
console.log("this:", this.get('title'));
var playlistId = this.get('id');
var itemCount = this.get('items').length;
var playlistItem = new PlaylistItem({
playlistId: playlistId,
position: itemCount,
videoId: song.videoId,
title: song.title,
relatedVideos: [],
selected: selected || false
});
this.get('items').push(playlistItem);
this.get('shuffledItems').push(playlistItem);
this.set('shuffledItems', _.shuffle(this.get('shuffledItems')));
console.log("this has finished calling");
//Call save to give it an ID from the server before adding to playlist.
songManager.saveSong(song, function (savedSong) {
song.id = savedSong.id;
playlistItem.set('songId', song.id);
console.log("calling save item");
$.ajax({
type: 'POST',
url: 'http://localhost:61975/Playlist/SaveItem',
dataType: 'json',
data: {
id: playlistItem.get('id'),
playlistId: playlistItem.get('playlistId'),
position: playlistItem.get('position'),
songId: playlistItem.get('songId'),
title: playlistItem.get('title'),
videoId: playlistItem.get('videoId')
},
success: function (data) {
playlistItem.set('id', data.id);
},
error: function (error) {
console.error(error);
}
});
});
return playlistItem;
},
addItemByVideoId: function (videoId, callback) {
var self = this;
ytHelper.getVideoInformation(videoId, function (videoInformation) {
var song = songManager.createSong(videoInformation, self.get('id'));
var addedItem = self.addItem(song);
if (callback) {
callback(addedItem);
}
});
},
//Returns the currently selected playlistItem or null if no item was found.
getSelectedItem: function() {
var selectedItem = _.find(this.get('items'), function (item) {
return item.get('selected');
});
return selectedItem;
}
});
return function (config) {
var playlist = new Playlist(config);
playlist.on('change:title', function () {
this.save();
});
return playlist;
};
});
basically I am seeing the property 'items' is populated inside of initialize when I've passed in a config object that does not specify items at all. If I specify a blank items array in my config object, then there are no items in initialize, but this seems counter-intuitive. Am I doing something wrong?
The problem is with using reference types (arrays) in the defaults object. When a new Playlist model is created without specifying an items value, the default is applied. In case of arrays and objects this is problematic, because essentially what happens is:
newModel.items = defaults.items
And so all models initialized this way refer to the same array. To verify this, you can test:
var a = new Playlist();
var b = new Playlist();
var c = new Playlist({items:[]});
//add an item to a
a.get('items').push('over the rainbow');
console.log(b.get('items')); // -> ['over the rainbow'];
console.log(c.get('items')); // -> []
To get around this problem, Backbone supports defining Model.defaults as a function:
var Playlist = Backbone.Model.extend({
defaults: function() {
return {
id: null,
userId: null,
title: 'New Playlist',
selected: false,
position: 0,
shuffledItems: [],
history: [],
items: []
};
}
});

Trigger Backbone Event from another js function

Looked around SO but couldn't find anything useful, so..
I have a Backbone.js contacts model with a contact card view. This view has many inputs where you can edit the contacts information.
I have many other forms on the page that are NOT backbone models, so they use a 'save button' to save. I basically want this save button to also trigger Contacts.CardView.saveCard(); (which could possibly be FileApp.cardView.saveCard as well? -- some of my code is below.
Is there any way to do this? I thought I could just use the following, but it seems it won't bind an event to anything outside the view?:
events: {
"change input": "change",
"click #save": "saveCard"
},
$('#save').click(function() {
FileApp.cardView.saveCard;
_SAVE.save();
})
CardView
window.Contacts.CardView = Backbone.View.extend({
events: {
"click #save": "saveCard" // doesnt work because #save is outside the view?
},
saveCard: function(e) {
this.model.set({
name:$('#name').val()
});
if (this.model.isNew()) {
var self = this;
FileApp.contactList.create(this.model, {
success:function () {
FileApp.navigate('contacts/' + self.model.id, false);
}
});
} else {
this.model.save();
}
return false;
}
}
Router:
var FileRouter = Backbone.Router.extend({
contactCard:function (id) {
if (this.contactList) {
this.cardList = new Contacts.CardCollection();
var self = this;
this.cardList.fetch({
data: {
"id":id
},
success: function(collection, response) {
if (self.cardView) self.cardView.close();
self.cardView = new Contacts.CardView({
model: collection.models[0]
});
self.cardView.render();
}
});
} else {
CONTACT_ID = id;
this.list();
}
}
});
var FileApp = new FileRouter();
One option is to create your own Events object for this case:
// Before initializing views, etc.
var formProxy = {};
_.extend(formProxy, Backbone.Events);
// Add the listener in the initialize for the CardView
window.Contacts.CardView = Backbone.View.extend({
initialize : function() {
formProxy.on('save', this.saveCard, this);
},
saveCard: function() {
this.model.set({
name:$('#name').val()
});
if (this.model.isNew()) {
var self = this;
FileApp.contactList.create(this.model, {
success:function () {
FileApp.navigate('contacts/' + self.model.id, false);
}
});
} else {
this.model.save();
}
return false;
}
}
// Save
$('#save').click(function() {
formProxy.trigger('save');
});
See: http://documentcloud.github.com/backbone/#Events

Categories