How do I return data to a template with Knockout and Requirejs modules? - templates

I'm having a difficult time returning data from a module using RequireJS and Knockout to populate my markup for my single page app. Knockout can't seem to find my data binding observables.
I'm trying to keep each view in a separate js file, but I'm failing to identify where I've gone wrong. Here's what I have so far:
/app/app.js
define(function(require) {
require('simrou');
var $ = require('jQuery'),
ko = require('knockout'),
videoView = require('videoView');
var init = function() {
var viewModel = function() {
var self = this;
self.currentPage = ko.observable();
self.videoView = new videoView();
}
var view = new viewModel();
ko.applyBindings( view );
_router = new Simrou({
'/video/:id': [ view.videoView.getVideo ]
});
_router.start();
};
return {
init: init
};
});
/app/videoView.js
define(function(require) {
"use strict";
var $ = require('jQuery'),
ko = require('knockout');
return function() {
var self = this;
self.currentPage = ko.observable( 'showVideo' );
self.currentVideo = ko.observable();
self.videoData = ko.observableArray([]);
self.videoList = ko.observableArray([]);
var getVideo = function( event, params ) {
// ajax pseudo code
$.ajax({});
self.videoData( dataFromAjaxCall );
}
return {
getVideo: getVideo
};
};
});
index.html
When I browse to /#/video/14 I receive the following error:
Uncaught ReferenceError: Unable to parse bindings.
Bindings value: attr: { 'data-video-id': videoData().id }
Message: videoData is not defined
Here's the markup:
<section id="showVideo" data-bind="fadeVisible: currentPage()=='showVideo', with: $root">
<div class="video" data-bind="attr: { 'data-video-id': videoData().id }></div>
</section>
Like I said, I'm trying to keep each view separated, but I would love some enlightenment on what I'm doing wrong, or if this is even possible? Is there a better more efficient way?

videoData is a property of $root.videoView, not of the root model (the one you passed to applyBindings). It's also an observableArray, so videoData() is just a plain array and even if you get the context right, you won't be able to access its id property, since, being an array, it doesn't have.named properties.

Related

Read content of SP.File object as text using JSOM

as the title suggests, I am trying to read the contents of a simple text file using JSOM. I am using a Sharepoint-hosted addin for this, the file I am trying to read resides on the host web in a document library.
Here's my JS code:
function printAllListNamesFromHostWeb() {
context = new SP.ClientContext(appweburl);
factory = new SP.ProxyWebRequestExecutorFactory(appweburl);
context.set_webRequestExecutorFactory(factory);
appContextSite = new SP.AppContextSite(context, hostweburl);
this.web = appContextSite.get_web();
documentslist = this.web.get_lists().getByTitle('Documents');
var camlQuery = new SP.CamlQuery();
camlQuery.set_viewXml('<View><ViewFields><FieldRef Name="Name"/></ViewFields></View>');
listitems = documentslist.getItems(camlQuery);
context.load(listitems, 'Include(File,FileRef)');
context.executeQueryAsync(
Function.createDelegate(this, successHandler),
Function.createDelegate(this, errorHandler)
);
function successHandler() {
var enumerator = listitems.getEnumerator();
while (enumerator.moveNext()) {
var results = enumerator.get_current();
var file = results.get_file();
//Don't know how to get this to work...
var fr = new FileReader();
fr.readAsText(file.get);
}
}
function errorHandler(sender, args) {
console.log('Could not complete cross-domain call: ' + args.get_message());
}
}
However, in my succes callback function, I don't know how I can extract the contents of the SP.File object. I tried using the FileReader object from HTML5 API but I couldn't figure out how to convert the SP.File object to a blob.
Can anybody give me a push here?
Once file url is determined file content could be loaded from the server using a regular HTTP GET request (e.g. using jQuery.get() function)
Example
The example demonstrates how to retrieve the list of files in library and then download files content
loadItems("Documents",
function(items) {
var promises = $.map(items.get_data(),function(item){
return getFileContent(item.get_item('FileRef'));
});
$.when.apply($, promises)
.then(function(content) {
console.log("Done");
//print files content
$.each(arguments, function (idx, args) {
console.log(args[0])
});
},function(e) {
console.log("Failed");
});
},
function(sender,args){
console.log(args.get_message());
}
);
where
function loadItems(listTitle,success,error){
var ctx = SP.ClientContext.get_current();
var web = ctx.get_web();
var list = web.get_lists().getByTitle(listTitle);
var items = list.getItems(createAllFilesQuery());
ctx.load(items, 'Include(File,FileRef)');
ctx.executeQueryAsync(
function() {
success(items);
},
error);
}
function createAllFilesQuery(){
var qry = new SP.CamlQuery();
qry.set_viewXml('<View Scope="RecursiveAll"><Query><Where><Eq><FieldRef Name="FSObjType" /><Value Type="Integer">0</Value></Eq></Where></Query></View>');
return qry;
}
function getFileContent(fileUrl){
return $.ajax({
url: fileUrl,
type: "GET"
});
}

ReactJS Simulate change value / unit testing

i try to simulate a test of a value change on my InputText component. I really don't know how to make that. I just know I must use the ref and the onChange method. But when I put a ref on my test I got an error like "you might be adding a ref to a component that was not created inside a component's render method".
Edit = I give a ref in the render of my InputText component
This is the render of my InputText component
render: function () {
console.log('passerender');
var attrs = this.generateAttributes();
var type = this.props.area ? "textarea" : "text";
return (
<Input
className={this.props.menuClassName}
type={type}
{...attrs}
{...this.props.evts}
className={this.props.menuClassName}
onChange = {this.handleChange}
onBlur = {this.handleBlur}
value={this.state.value}
ref = "InputField"
hasFeedback
/>
);
}
});
This is my test page of my InputText component:
var React = require('react'),
InputText = require('../resources/assets/js/testcomponents/InputText.js').InputTextEditable,
TestUtils = require('react-addons-test-utils'),
ReactDOM = require('react-dom');
describe('InputText', function () {
var InputElement = TestUtils.renderIntoDocument(
<InputText
area={false}
//evts={{onChange: handleChange}}
attributes={{
label:'Test Input Isole',
name:'InputTextArea',
value: '',
wrapperClassName: 'col-md-4',
labelClassName: 'col-md-2',
groupClassName: 'row'
}}
//ref="InputField"
editable={true}/>);
var DomElement = ReactDOM.findDOMNode(InputElement);
var inputV = ReactDOM.findDOMNode(InputElement.refs.InputField);
var input = DomElement.getElementsByTagName('input')[0];
var inputspan = DomElement.getElementsByTagName('span')[1];
it('updates input value on key press', function () {
inputV.value = 'test';
expect(input.getAttribute('value')).toEqual('');
TestUtils.Simulate.change(inputV);
TestUtils.Simulate.keyDown(inputV, {key: "Entrer", keyCode: 13, which: 13});
expect(input.getAttribute('value')).toEqual('test');
});
You can use findRenderedComponentWithType or findRenderedDOMComponentWithTag
You don't need to call findDOMNode explicitly, because TestUtils has done this for you.
var InputElement = TestUtils.renderIntoDocument(
<InputText {...yourProps}/>
);
// Assuming there is only one <input /> DOM element in your Input
var input = TestUtils.findRenderedComponentWithType(InputElement, Input)
// or you can just find <input /> directly
var input = TestUtils.findRenderedDOMComponentWithTag(InputElement, 'input');
TestUtils.Simulate.change(input);
TestUtils.Simulate.keyDown(input, {key: "Entrer", keyCode: 13, which: 13});
Ok I find the problem of the syntax error. It was on my html5validator on my input mixin. I put a try/catch to solve this :
var html5Validity = true;
if (DOM !== undefined) {
try {
html5Validity = $(DOM).find(':invalid').length == 0;
console.log('passe');
} catch (e) {
console.log('html5Validity = [catch]');
html5Validity = true;
}
}
attrs = _.extend({'data-valid': validation.isValid && html5Validity}, attrs);
Now it's OK ! Thank you ! :)

Sitecore 8 SPEAK - Calling Custom components Javascript method

My question is somewhat similar to followin unanswered question. (Not sure though)
Sitecore 8 SPEAK: Getting an Error When calling a Method in JS File
I am using Sitecore8
On my page there is a button and on its click event I want to call add() of custom datasource component.
Layout:
JS Code for the Page:
define(["sitecore"], function (Sitecore) {
var JsonListPage = Sitecore.Definitions.App.extend({
initialized: function () {
alert('Inside Json PageList Init');
},
loadData: function () {
alert('Button clicked');
app.add();
}
});
return JsonListPage;
});
JS Code for the custom datasource component:
define(["sitecore"], function (Sitecore) {
var model = Sitecore.Definitions.Models.ControlModel.extend({
initialize: function (options) {
this._super();
this.set("json", null);
alert('Inside Jsondatasource Init');
},
add: function (data) {
var json = this.get("json");
if (json === null)
json = new Array();
// this is done because array.push changes the array to an object which then do no work on the SPEAK listcontrol.
var newArray = new Array(json.length + 1);
for (var i = json.length - 1; i >= 0; i--)
newArray[i + 1] = json[i];
newArray[0] = data;
this.set("json", newArray);
}
});
var view = Sitecore.Definitions.Views.ControlView.extend({
initialize: function (options) {
this._super();
this.model.set("json", null);
}
});
Sitecore.Factories.createComponent("JsonDatasource", model, view, ".x-sitecore-jsondatasource");
});
.cshtml for Custom component:
#using Sitecore.Mvc
#using Sitecore.Mvc.Presentation
#using Sitecore.Web.UI.Controls.Common.UserControls
#model RenderingModel
#{
var userControl = Html.Sitecore().Controls().GetUserControl(Model.Rendering);
userControl.Requires.Script("client", "JsonDatasource.js");
userControl.Class = "x-sitecore-jsondatasource";
userControl.Attributes["type"] = "text/x-sitecore-jsondatasource";
userControl.DataBind = "Json: json";
var htmlAttributes = userControl.HtmlAttributes;
}
<div #htmlAttributes>
am here again
</div>
When the page loads:
It shows alert from Custom components Init
Then shows alert from host page's Init
On button click it shows the alert and after that gives error on "app".
There is some bit which I am missing.. any help would be appreciated.. Please let me know if you need anymore inputs.
Thanks in advance!
app is only available in debug mode so id avoid using that, use "this" instead.
From your code example it appears that you are calling app.Add(), There is no Add function on your pageCode, this is what your code is doing. Instead you need to access your components's Add Method.
Instead to access events within your component you want to call the function like this:
this.ComponentID.Add();
I have an example of a custom SPEAK component here you can refer to for how to create the component. https://github.com/sobek1985/MikeRobbinsSPEAKRichTextEditor
From the code is seems your creating a JSON datasource, there is an example by Anders here http://laubplusco.net/creating-simple-sitecore-speak-json-datasource/

How to test nested callbacks with Mocha/Sinon?

What is the/one correct way to test this piece of JavaScript code using, e.g, Mocha/Sinon:
var App = function(endPoint, successCallback) {
var channel = new WebSocket(endPoint);
channel.onopen = function(ev) {
successCallback();
};
};
I'm thinking of something like this:
describe('App', function() {
it('test should create instance and call success', function(done) {
var app = new App('ws://foo.bar:123/', done);
var stub = sinon.stub(app, 'channel');
stub.yield('onopen');
});
});
Apparently, that does not work as channel is not accessible from outside the constructor. How would you test this?
Why not create a factory for Websocket such as:
var myApp = {
createWebsocket: function () {
return new Websocket;
}
};
This would make spying on the myApp.createWebsocket return value channel very easy:
sinon.spy(myApp, 'createWebsocket);
var channel = myApp.createWebsocket.firstCall.returnValue;
var stub = sinon.stub(channel, 'onopen');
stub.yield('onopen');
// Clean up
myApp.createWebsocket.restore();

Famo.us: Slideshow tutorial with the Picasa API is out of date

I've been working on the famo.us Slideshow tutorial and realised that the API is no longer working.
Any ideas how can I still make this to work without the Picasa API?
You can keep the API by using the following code for SlideData.js
You can make it work by creating the objects returned by the url + parameters below and returning them as the JSON object if you do not want to use the API.
define(function(require, exports, module) {
var SlideData = {
picasaUrl: 'https://picasaweb.google.com/data/feed/api/all',
queryParams: '?kind=photo&q=puppy&max-results=5&imgmax=720&alt=json',
defaultImage: 'https://lh4.googleusercontent.com/-Roszbra0TlI/VB-fE83NAXI/AAAAAAAAACU/ITmhyZMHZrk/s720/cute%252520looking%252520white%252520and%252520black%252520french%252520Bulldog%252520Puppy.jpg'
};
SlideData.getUrl = function() {
return SlideData.picasaUrl + SlideData.queryParams;
};
SlideData.parse = function(data) {
var urls = [];
data = JSON.parse(data);
var entries = data.feed.entry;
for (var i = 0; i < entries.length; i++) {
var media = entries[i].media$group;
urls.push(media.media$content[0].url);
}
return urls;
};
module.exports = SlideData;
});