I get these error messages upon loading the page:
Assertion failed: The value that #each loops over must be an Array.
You passed (generated snippets.index controller) ember-1.0.0.js:394
Uncaught TypeError: Object [object Object] has no method 'addArrayObserver'
Here is my template code:
<script type="text/x-handlebars" data-template-name="snippets/index">
{{#each}}
{{title}}
{{/each}}
</script>
My 'Snippet' model is quite simple:
TSLibrary.Snippet = DS.Model.extend({
title: DS.attr('string')
});
TSLibrary.Snippet.FIXTURES = [{id: 1, title: 'Learn Ember.js'}];
My app and my router:
window.TSLibrary = Ember.Application.create();
TSLibrary.ApplicationAdapter = DS.FixtureAdapter.extend();
// ---
TSLibrary.Router.map(function () {
this.resource('snippets', { path: '/' }, function () {
});
});
TSLibrary.SnippetsRoute = Ember.Route.extend({
model: function () {
// My guess is that this does not return the expected array
//
// This logs 'Class {toString: function, constructor: function, reason: null, isPending: undefined, isSettled: undefined…}'
console.log(this.store.find('snippet'));
return this.store.find('snippet');
}
});
So my guess is that this.store.find('snippet') is not returning the right data.
I've also installed the Ember debug extension in Chrome and it shows me all the right data in my model.
Ember version: 1.0.0
Ember Data version: v1.0.0-beta.1-140-ga51f29c a51f29c (2013-09-07 16:34:55 -0700)
Handlebars version: 1.0.0
jQuery version: 1.10.2
TSLibrary.Router.map(function () {
this.resource('snippets', { path: '/' }, function () {
});
});
This way creates the 'snippets.index' path, which requires a Route called SnippetsIndexRoute.
TSLibrary.Router.map(function () {
this.resource('snippets', { path: '/' });
});
This one is just 'snippets', which correctly uses the SnippetsRoute that you have defined.
I found the solution by accident, now:
TSLibrary.Router.map(function () {
this.resource('snippets', { path: '/' }, function () {
});
});
has to be
TSLibrary.Router.map(function () {
this.resource('snippets', { path: '/' });
});
Related
I have been pretty much beginner at this part of javascript and I would appreciate any ideas how could be solved this problem.
I use requirejs to define my own modules where I also use backbone.js.
Let say I have the main module where I initialize my Backbone view which is rendered without any problem. Also, the click event where is calling method createSchemeForm creates the form correctly. The problem raises up in a situation when I call cancel method by click and the modules which are defined for Backbone view (e.g. "unicorn/sla/dom/helper"...) are undefined but when I called method createSchemeForm at the beginning the modules were executed without any problem.
Thank you in advance for any suggestions.
Backbone view
define("unicorn/sla/view/scheme", [
"unicorn/sla/dom/helper",
"unicorn/soy/utils",
"unicorn/sla/utils"
], function (DOMHelper, soyUtils, jsUtils) {
return Backbone.View.extend({
el: 'body',
inputData: {},
btnSaveScheme: 'btn-save-sla-scheme',
btnCancel: 'btn-cancel-sla-scheme',
btnCreate: 'btn-create-sla-scheme',
btnContainer: '#sla-scheme-buttons-container',
schemeContent: '#sla-scheme-content-section',
btnSpinner: '.button-spinner',
events: {
'click #btn-create-sla-scheme' : "createSchemeForm",
'click #btn-cancel-sla-scheme' : "cancel"
},
initialize: function(){
console.log("The scheme view is initialized...");
this.render();
},
createSchemeForm: function () {
this.spin();
DOMHelper.clearSchemeContent();
DOMHelper.clearButtonsContainer();
//Get button
$btnSave = soyUtils.getButton({isPrimary: 'true', id: this.btnSaveScheme, label: 'Save'});
$btnCancel = soyUtils.getButton({isPrimary: 'false', id: this.btnCancel, label: 'Cancel'});
//Append new created buttons
DOMHelper.addContent(this.btnContainer, AJS.format("{0}{1}", $btnSave, $btnCancel));
//Call service to get entry data for scheme creation form
AJS.$.ajax({
url: AJS.format('{0}={1}',AJS.I18n.getText('rest-url-project-scheme-input-data'), jsUtils.getProjectKey()) ,
type: "post",
async: false,
context: this,
global: false,
}).done(function (data) {
this.inputData = data;
$slaSchemeForm = soyUtils.getSchemeCreateForm({slaScheme : data, helpText: AJS.I18n.getText("sla-time-target-tooltip-text")});
DOMHelper.addContent(this.schemeContent, $slaSchemeForm);
jsUtils.scroll(this.schemeContent, 'slow');
}).fail(function () {
jsUtils.callFlag('error', AJS.I18n.getText("message-title-error"), AJS.I18n.getText("sla-error-load-scheme-input-data"));
}).always(function () {
this.stopSpin();
});
},
spin: function () {
AJS.$('.button-spinner').spin();
},
stopSpin: function () {
AJS.$('.button-spinner').spinStop();
},
cancel: function () {
jsUtils.clearButtonsContainer();
jsUtils.clearSchemeContent();
$btnCreateScheme = soyUtils.getButton({isPrimary: 'false', id: this.btnCreate, label: 'Create SLA Scheme'});
DOMHelper.addContent(this.btnContainer, $btnCreateScheme);
DOMHelper.addContent(this.schemeContent, soyUtils.getSchemesTable(new Array())); // TODO - get current data from server instead of empty array
}
});
});
Main module where is Backbone view initialize
define("unicorn/sla/project/batch", [
"unicorn/sla/utils",
"unicorn/sla/data/operations",
"unicorn/sla/data/validator",
"unicorn/sla/dom/helper",
"unicorn/sla/model/confirm/message",
"unicorn/sla/view/scheme",
"exports"
], function (jsUtils, operations, validator, DOMHelper, ConfirmMessage, SchemeView, exports) {
//Load project batch
exports.onReady = function () {
$schemeView = new SchemeView();
$schemeView.render();
}
});
AJS.$(function () {
AJS.$(document).ready(function () {
require("unicorn/sla/project/batch").onReady();
});
});
Learning Vue and stuck. I have a brand new Laravel 5.4 project that I am using to learn Vue concepts, using Pusher/Echo. All is working in terms of message broadcasting, and the messages are fetched from the server and displayed on page load as expected. I want to programatically (from somewhere else in the project) send a message into the queue.
I am using this example as guide to accessing the Vue method outside the instance.
Why can I not access the instance method from my main JS file? The project is compiled with webpack FYI.
My Vue.js file:
$(document).ready(function()
{
Vue.component('chat-system', require('../components/chat-system.vue'));
var chatSystem = new Vue({
el: '#system-chat',
data: {
sysmessages: []
},
created() {
this.fetchMessages();
Echo.private(sys_channel)
.listen('SystemMessageSent', (e) => {
this.sysmessages.unshift({
sysmessage: e.message.message,
player: e.player
});
});
},
methods: {
fetchMessages() {
axios.get(sys_get_route)
.then(response => {
this.sysmessages = response.data;
});
},
addMessage(sysmessage) {
this.sysmessages.unshift(sysmessage);
this.$nextTick(() => {
this.$refs.sysmessages.scrollToTop();
});
axios.post(sys_send_route, sysmessage)
.then(response => {
console.log(response.data);
});
},
sendMessage(sysmessage) {
if (sysmessage !== '') {
this.$emit('systemmessagesent', {
player: this.player,
message: sysmessage
});
}
}
}
});
});
My Vue.js component:
<template>
<div id="round-status-message" class="round-status-message">
<div class="row">
<div class="col-xs-12" v-for="sysmessage in sysmessages">
{{ sysmessage.message }}
</div>
</div>
</div>
</template>
<script>
export default {
props: ['player', 'sysmessages'],
data() {
return {
newSysMessage: ''
}
},
methods: {
scrollToTop () {
this.$el.scrollTop = 0
},
sendMessage() {
this.$emit('systemmessagesent', {
player: this.player,
message: this.newSysMessage
});
this.newSysMessage = ''
}
}
};
</script>
I want to send a message into the queue programatically, so in my app.js, to test, I do:
// TESTING SYSTEM MESSAGES - DELETE
window.setInterval(function(){
var resp = {};
resp.data = {
id: 1,
message: "She hastily put down yet, before the end of half.",
progress_id: 1,
created_at: "2017-08-17 14:01:11",
updated_at: "2017-08-17 14:01:11"
};
chatSystem.$refs.sysmessages.sendMessage(resp);
console.log(resp);
}, 3000);
// TESTING SYSTEM MESSAGES - DELETE
But I get Uncaught ReferenceError: chatSystem is not defined
All I needed was to make the method name available to the global scope?
global.chatSystem = chatSystem; // App variable globally
This seems to work now...
I've just developed this JavaScript/Backbone module as a part of a web page I am developing. I would like to create a Jasmine test for it, but I am brand new to Jasmine, therefore I am not sure what should I be testing in this class. What should be the "skeleton" of the test? In order to avoid redundancy in tests, what parts will you test?
editdestinationview.js:
define([
'common/jqueryex',
'backbone',
'marionette',
'handlebars',
'text!education/eet/templates/editdestination.hb',
'text!common/templates/validationerror.hb',
'lang/languageinclude',
'common/i18nhelper'
], function ($, Backbone, Marionette, Handlebars, templateSource, errorTemplateSource, i18n) {
'use strict';
var errorTemplate = Handlebars.compile(errorTemplateSource),
EditDestinationView = Marionette.ItemView.extend({
initialize: function (options) {
this._destinationTypes = options.destinationTypes;
},
onRender: function () {
this.stickit();
this._bindValidation();
},
_bindValidation: function () {
Backbone.Validation.bind(this, {
valid: this._validAttributeCallback,
invalid: this._invalidAttributeCallback,
forceUpdate: true
});
},
_validAttributeCallback: function (view, attr) {
view.$('#error-message-' + attr).remove();
},
_invalidAttributeCallback: function (view, attr, error) {
view.$('#error-message-' + attr).remove();
view.$('#destinationTypes').parent('div').append(errorTemplate({
attr: attr,
error: error
}));
},
template: Handlebars.compile(templateSource),
ui: {
saveAnchor: '#ed_eetSaveDestinationAnchor',
deleteAnchor: '#ed_eetDeleteDestinationIcon'
},
triggers: {
'click #ui.saveAnchor': 'click:saveDestination',
'click #ui.deleteAnchor': 'click:deleteDestination'
},
bindings: {
'select#destinationTypes': {
observe: 'destinationTypeId',
selectOptions: {
collection: function () {
return this._destinationTypes;
},
labelPath: 'description',
valuePath: 'destinationTypeId',
defaultOption: {label: i18n.EDUCATION_EET_SELECT_INTENDED_DESTINATION, value: null}
}
}
}
});
return EditDestinationView;
});
Thanks everyone!
UPDATE:
After thinking a lot about it, I think that I should try these aspects:
-Triggers: Check if they can be clicked.
-"_validAttributeCallback" and "_invalidAttributeCallback": Check if they behave accordingly to the code.
-Template: Spy on it to check if it is performing it's mission. (Optional test)
So, the test skeleton will be:
define([
'education/eet/views/editdestinationview'
], function (EditDestinationView) {
describe('description...', function () {
beforeEach(function () {
//EditDestinationView.triggers
});
describe('blablabla', function () {
beforeEach(function () {
// ...
});
it('blablabla', function () {
// blablabla
});
});
});
});
Any help on how to test this please?
One common pattern is to use two describe statements, one for the class and one for the method being tested, and then an it statement for each thing you want to test about that method. The rspec people have a convention (which I use in my JS tests) of using a '#' on the method describe for an instance method, and a "." for a describe of a static method.
Now, if you adopt all of the above, and you want to test (for instance) that your View's click-handling method triggers a certain event on the View's Model, it would look something like this:
define([
'education/eet/views/editdestinationview'
], function (EditDestinationView) {
describe('EditDestinationView', function () {
var view;
beforeEach(function () {
// do setup work that applies to all EditDestinationView tests
view = new EditDestinationView({model: new Backbone.Model()});
});
describe('#handleClick', function () {
beforeEach(function () {
// do setup work that applies only to handleClick tests
});
it('triggers a foo event', function () {
var wasTriggered;
view.model.on('foo', function() {
wasTriggered = true;
});
view.handleClick();
expect(wasTriggered).toBe(true);
});
});
});
});
P.S. Instead of creating a fake "foo" handler like I did, most people use a mocking library like Sinon. Using that library our "it" statement could instead be:
it('triggers a foo event', function () {
var triggerStub = sinon.stub(view.model, 'trigger');
view.handleClick();
expect(triggerStub.calledOnce).toBe(true);
expect(triggerStub.args[0][0]).toBe('foo');
//NOTE: args[0][0] == first arg of first call
});
I'm having a issue where transitioning is not occurring on a page reload/refresh. When I start the application and click on the links, everything works perfectly, but when I reload the route - I get an empty page (blank). This is happening for me on the MovieIndexRoute below.
// Router
MediaUi.Router.map(function () {
this.resource('movies', { path: '/'}, function() {
this.resource('movie', { path: 'movie/:id' }, function() {
this.route('edit', { path: '/edit' });
});
});
});
// Movies Route
MediaUi.MoviesRoute = Ember.Route.extend({
model: function() {
var media;
media = MediaUi.Media.find();
return media;
}
});
// Movie Route
MediaUi.MovieRoute = Ember.Route.extend({
serialize: function(model) {
return { id: model.get('_id') };
}
});
// Movie Index Route
MediaUi.MovieIndexRoute = Ember.Route.extend({
model: function(params) {
return this.modelFor('movie');
}
});
You can also access the repo here: https://github.com/alvincrespo/media-ui/tree/nested-resources on the nested-resources branch.
I've also added the following screenshot, showing the page and console.
Any help with this would be greatly appreciated. Thank You!
I'm new to backbone and I'm trying to send and receive data from the server in Json format. It just won't work. Here's my code (BTW, I'm using backbone aura):
Collection
define(['sandbox', '../models/message'], function(sandbox, Message) {
'use strict';
var Messages = sandbox.mvc.Collection({
model: Message,
url: '/messagelist.php',
localStorage: new sandbox.data.Store('messages-backbone-require'),
parse: function(response){
return response.rows;
}
});
return Messages;
});
Model
define(['sandbox'], function(sandbox) {
'use strict';
var Message = sandbox.mvc.Model({
defaults: {
opened: '',
messageid: '',
phonenumber: '',
numbername: '',
text: ''
},
parse: function(data){
return data;
}
});
return Message;
});
View
define(['sandbox', '../models/message', 'text!../templates/incoming_messages.html'], function(sandbox, Message, incomingMessagesTemplate) {
'use strict';
var AppView = sandbox.mvc.View({
widgetTemplate: sandbox.template.parse(incomingMessagesTemplate),
events: {
'click .refresh': 'refresh'
},
initialize: function() {
this.$el.html(this.widgetTemplate);
sandbox.events.bindAll(this);
this.collection.bind('createMessageList', this.createMessageList);
},
createMessageList: function() {
// Will work with the received data here
},
render: function() {
var handle = 'h4';
this.$el.draggable({handle: handle});
this.createMessageList();
},
refresh: function() {
this.createMessageList();
}
});
return AppView;
});
Main
define(['sandbox', './views/app', './collections/messages'], function(sandbox, AppView, Messages) {
'use strict';
return function(options) {
var messages = new Messages();
new AppView({
el: sandbox.dom.find(options.element),
collection: messages
}).render();
messages.fetch({
data: {
type: 'incoming',
offset: 0,
offsetcount: 25
},
type: 'GET',
success: function() {
console.log(messages.models); // Shows an empty array.
}
});
};
});
I've check logs and it seems that the ajax request (collection.fetch()) is not firing or is not able to communicate with the server. How can I fix this?
The problem is with the Backbone.LocalStorage plugin. When you assign Collection.localStorage, the plugin takes over the fetch command and reads the data from local storage instead of the server.
See my answer in this SO question on some options on how to solve this.