I want to pipe event handler of view1 to event handler of view2. As a result the following code should trigger eventListeners of both: view1 and view2:
define(function(require, exports, module) {
var Engine = require('famous/core/Engine');
var View = require('famous/core/View');
var Surface = require('famous/core/Surface');
var ctx = Engine.createContext();
var surf = new Surface({size: [150,150], properties:{background: 'red'}});
ctx.add(surf);
var view1 = new View();
var view2 = new View();
view1.subscribe(surf);
surf.on('click', function() {
view1._eventOutput.emit('A');
});
view2.subscribe(view1);
view1.on('A', function(){
console.log('A1');
})
view2.on('A', function(){
console.log('A2');
})
});
The problem is that I'm receiving only 'A1' event so view2.subscribe(view1) doesn't do the trick..
How to pipe views correctly?
Event pipe and subscribe must go through the View event handlers view._eventOutput or view._eventInput. Similar to what you did when you sent (emit) the custom event to view1.
In the snippet example below, I changed the code to emit the custom event through the surface to show how this can flow.
The surface piped to view1._eventOutput and view1 piped to view2._eventOutput
define('main', function(require, exports, module) {
var Engine = require('famous/core/Engine');
var View = require('famous/core/View');
var Surface = require('famous/core/Surface');
var ctx = Engine.createContext();
var surf = new Surface({
size: [150, 150],
properties: {
background: 'red'
}
});
var view1 = new View();
var view2 = new View();
ctx.add(surf);
//view1._eventOutput.subscribe(surf);
//view2._eventOutput.subscribe(view1);
surf.pipe(view1._eventOutput);
view1.pipe(view2._eventOutput);
surf.on('click', function() {
//view1._eventOutput.emit('A');
surf.emit('A');
});
view1.on('A', function() {
console.log('A1');
})
view2.on('A', function() {
console.log('A2');
})
});
require(['main']);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="http://requirejs.org/docs/release/2.1.16/minified/require.js"></script>
<script src="http://code.famo.us/lib/requestAnimationFrame.js"></script>
<script src="http://code.famo.us/lib/classList.js"></script>
<script src="http://code.famo.us/lib/functionPrototypeBind.js"></script>
<link rel="stylesheet" type="text/css" href="http://code.famo.us/famous/0.3.5/famous.css" />
<script src="http://code.famo.us/famous/0.3.5/famous.min.js"></script>
Related
Im trying to add a custom widget similar to HeaderButtonWidget, or StatusWidget, but im not able to do it yet:
my first attempt:
odoo.define('my_module.pos', function(require) {
"use strict";
var chrome = require('point_of_sale.chrome');
var _t = core._t;
var _lt = core._lt;
...
var AltHeader = chrome.HeaderButtonWidget.extend({
template : 'AltHeader',
init: function(parent, options){
options = options || {};
this._super(parent, options);
this.action = options.action;
this.label = options.label;
},
renderElement: function(){
var self = this;
this._super();
},
button_click: function(){ console.log("its a hit") },
highlight: function(highlight){
this.$el.toggleClass('highlight',!!highlight);
},
});
but this is not working, and HeaderButtonWidget is not returned by the main method of point_of_sale/static/src/js/screens.js so according to this page:
http://odoo-development.readthedocs.io/en/latest/dev/pos/gui.html so using the screen widget extend is not possible, how do i do this?
EDIT:
Here is what i have attempted:
odoo.define('pos_widget_toggler.pos', function (require) {
"use strict";
var PosBaseWidget = require('point_of_sale.BaseWidget');
var core = require('web.core');
var _t = core._t;
var _lt = core._lt;
var AltHeader = PosBaseWidget.extend({
template: 'AltHeader',
init: function(parent,options){
this._super(parent, options);
this.label = _lt('OFF');
this.action= function(){
// code will be here
} ;
},
});
return{
AltHeader:AltHeader
};
});
and pos_widget_toggler.xml
<templates id="pos_widget_toggler" inherit_id="point_of_sale.template" xml:space="preserve">
<t t-name="AltHeader">
<div class="oe_status">
<t t-esc="widget.label" />
</div>
</t>
</templates>
Im still not able to see anything,
To create new widget,
odoo.define('point_of_sale.chrome', function (require) {
"use strict";
var PosBaseWidget = require('point_of_sale.BaseWidget');
var AltHeader = PosBaseWidget.extend({
template: 'AltHeader',
init: function(parent, options){
//Your code
}
});
return{
AltHeader:AltHeader
};
});
your_custom_module/static/src/xml/file.xml
<t t-name="AltHeader">
<div class="oe_status">
// Your code
</div>
</t>
Add this template file in your manifest.
your_custom_module/__manifest__.py
'qweb': ['static/src/xml/pos.xml'],
Hope it will help you.
How can one access the target of an event, say a click, form the code?
I have:
var eh = new EventHandler();
and
eh.on('click', function(obj) {
rc.setOptions(
{
inTransition: true,
outTransition: false
});
var surfaceProps = obj.origin.getProperties();
as part of a sample app. The surfaces created in a function before in the code pipe click events to this eh event handler as;
surf.pipe(eh);
However, The clicks does not work as expected. In JS console it gives the following error:
TypeError: undefined is not an object (evaluating 'obj.origin.getProperties')
In the sample app, they are referring to a custom event emitted from within the selection within their Scrollview to an EventHandler. The article you refer to was written early in the releases of Famo.us.
The code snippet below will show how this could be done using the 'click' as the custom event. Probably not best practices, because it is an event for the mouse click event (MouseEvent). I would rename it to something else in a production app.
define('main', function(require, exports, module) {
var Engine = require('famous/core/Engine');
var OptionsManager = require('famous/core/OptionsManager');
var Surface = require('famous/core/Surface');
var Modifier = require('famous/core/Modifier');
var EventHandler = require('famous/core/EventHandler');
var StateModifier = require('famous/modifiers/StateModifier');
var RenderNode = require('famous/core/RenderNode');
var View = require('famous/core/View');
var Transform = require('famous/core/Transform');
var SequentialLayout = require("famous/views/SequentialLayout");
var mainContext = Engine.createContext();
var sequentialLayout = new SequentialLayout();
var surfaces = [];
sequentialLayout.sequenceFrom(surfaces);
var eh = new EventHandler();
eh.on('click', function(obj) {
console.log('obj event', obj.event);
var surfaceProps = obj.origin.getProperties();
console.log('obj properties', JSON.stringify(surfaceProps));
});
function _eventHandler(e) {
console.log('event', e, this);
var surfaceProps = this.getProperties();
console.log('surfaceProps', surfaceProps);
this.setContent(JSON.stringify(surfaceProps));
eh.emit('click', {
event: e,
origin: this
});
};
var numberofItems = 10;
var itemHeight = 45;
var itemWidth = 20;
for (var i = 0; i < numberofItems; i++) {
var surface = new Surface({
properties: {
backgroundColor: "hsl(" + (i * 360 / 120) + ", 100%, 50%)",
}
});
surface._mod = new StateModifier({
size: [itemWidth, itemHeight], // <- Changed the width here!!!
origin: [0.5, 0],
align: [0.5, 0],
proportions: [i + 1, 1]
});
var view = new RenderNode();
view.add(surface._mod).add(surface);
view._surface = surface;
surfaces.push(view);
//surface.pipe(eh);
surface.on('click', _eventHandler);
}
mainContext.add(sequentialLayout);
Engine.on('click', function(e) {
for (var i = 0; i < surfaces.length; i++) {
var random = Math.random() * surfaces.length + itemWidth;
surfaces[i]._surface._mod.setProportions([random, 1], {
duration: 1000
});
}
});
});
require(['main']);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="http://requirejs.org/docs/release/2.1.16/minified/require.js"></script>
<script src="http://code.famo.us/lib/requestAnimationFrame.js"></script>
<script src="http://code.famo.us/lib/classList.js"></script>
<script src="http://code.famo.us/lib/functionPrototypeBind.js"></script>
<link rel="stylesheet" type="text/css" href="http://code.famo.us/famous/0.3.5/famous.css" />
<script src="http://code.famo.us/famous/0.3.5/famous.min.js"></script>
<div class="test-container">
<div id="famous-app"></div>
</div>
If I'm understanding your question correctly, this feature just landed in the latest release:Add the famous source object to forwarded events
Now you can just do:
eh.on('click', function(event) {
var famousSurface = event.target;
var domElement = famousSurface._currentTarget;
});
Is there any built-in support for modals in Famous?
I completed the Famous University and looked through the documentation but didn't see anything.
I am about to roll my own, but I figured I should ask first.
You are going to want to use the Lightbox object. Lightbox is like a RenderController that has state. This means you can define, how views come in an out of view both by transition and transform. Here is a very basic example of the modal type you see often in iOS..
Hope it helps!
var Engine = require('famous/core/Engine');
var Surface = require('famous/core/Surface');
var Transform = require('famous/core/Transform');
var Modifier = require('famous/core/Modifier');
var Lightbox = require('famous/views/Lightbox');
var Easing = require('famous/transitions/Easing');
var context = Engine.createContext();
var surface = new Surface({
size:[200,200],
properties:{
backgroundColor:'green',
color:'white',
lineHeight:'200px',
textAlign:'center'
}
});
surface.on('click',function(){ openModal() });
context.add(new Modifier({origin:[0.5,0.5]})).add(surface);
var modal = new Surface({
size:[500,500],
properties:{
backgroundColor:'red'
}
})
modal.on('click',function(){ hideModal() });
modal.lightbox = new Lightbox({
inTransform: Transform.translate(0,500,0),
outTransform: Transform.translate(0,500,0),
inTransition: {duration:1000, curve:Easing.outElastic},
outTransition: {duration:200, curve:Easing.inOutQuad},
});
context.add(new Modifier({origin:[0.5,0.5]})).add(modal.lightbox);
function openModal(){
modal.lightbox.show(modal);
}
function hideModal(){
modal.lightbox.hide();
}
I'm trying to implement the typical swipe left event to trigger some custom action using a scrollview in famo.us. The thing is that I missing something and I can't get it done. I manage to implement a Draggable modifier in each scrollview item, so the items can be dragged (X axis), but now I can't be able to capture the event of the draggable modifier in order to trigger the actions.
Here is my ListView class:
define(function(require, exports, module) {
// Imports
var View = require('famous/core/View');
var Surface = require('famous/core/Surface');
var Utility = require('famous/utilities/Utility');
var ScrollView = require('famous/views/ScrollView');
var ViewSequence = require('famous/core/ViewSequence');
var Draggable = require('famous/modifiers/Draggable');
var RenderNode = require('famous/core/RenderNode');
var EventHandler = require('famous/core/EventHandler');
function ListView() {
View.apply(this, arguments);
this.items = [];
this.scrollView = new ScrollView({
direction: Utility.Direction.Y,
margin: 100000
});
this.viewSequence = new ViewSequence(this.items);
this.scrollView.sequenceFrom(this.viewSequence);
this._add(this.scrollView);
};
ListView.prototype = Object.create(View.prototype);
ListView.prototype.constructor = ListView;
ListView.prototype.setContent = function(data) {
for (var i = 0; i < data.length; i++) {
var item = new Surface({
size: [undefined, 60],
content: 'Item ' + data[i],
classes: ['listview-item']
});
var draggable = new Draggable({
xRange: [-100, 100],
yRange: [0, 0]
});
var node = new RenderNode(draggable);
node.add(item);
draggable.on('click', function() {
console.log('emit swipe')
this._eventOutput.emit('swipe');
}.bind(this)); // This Doesn't work
item.on('click', function() {
console.log('emit swipe')
this._eventOutput.emit('swipe');
}.bind(this)); // Neither this
item.pipe(draggable);
item.pipe(this.scrollView);
this.items.push(node);
}
};
module.exports = ListView;
});
Now App Class where I include my ListView:
define(function(require, exports, module) {
...
// Custom Views
var ListView = require('views/ListView');
function App() {
View.apply(this, arguments);
this.layout = new HeaderFooterLayout({
headerSize: 70,
});
...
this.list = new ListView();
this.list.pipe(this._eventInput);
this._eventInput.on('swipe', this.swipeListItem.bind(this)) // Trying to captute swipe event
this.list.setContent([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]);
this.layout.content.add(this.list);
....
this._add(this.layout);
};
App.prototype = Object.create(View.prototype);
App.prototype.constructor = App;
App.DEFAULT_OPTIONS = {};
App.prototype.swipeListItem = function() {
console.log('Item Swiped!');
};
module.exports = App;
});
I don't know what I missing or if there is a better way to implement a swipe gesture in famo.us, if someone knows something about it would be helpful.
Thanks in advance. =)
It looks like you want to use the 'start' event for the draggable modifier..
draggable.on('start', function() {
console.log('emit drag start')
this._eventOutput.emit('swipe');
}.bind(this));
Draggable also emits 'update', and 'end' and each of these handlers take a parameter that returns the position of the draggable
draggable.on('update', function(e) {
// Do something on update
var pos = e.position;
});
draggable.on('end', function(e) {
// Do something on end
var pos = e.position;
});
Hope this helps!
I need to create an Ember component to select a file.
My page will include multiple "upload component"
I have read a post trying to implement that: (https://stackoverflow.com/questions/9200000/file-upload-with-ember-data) BUT the UploadFileView is directly linked to the controller.
I would like to have something more generic...
I would like to remove the App.StoreCardController.set('logoFile'..) dependency from the view or pass the field (logoFile) from the template...
Any idea to improve this code ?
App.UploadFileView = Ember.TextField.extend({
type: 'file',
attributeBindings: ['name'],
change: function(evt) {
var self = this;
var input = evt.target;
if (input.files && input.files[0]) {
App.StoreCardController.set('logoFile', input.files[0]);
}
}
});
and the template:
{{view App.UploadFileView name="icon_image"}}
{{view App.UploadFileView name="logo_image"}}
I completed a full blown example to show this in action
https://github.com/toranb/ember-file-upload
Here is the basic handlebars template
<script type="text/x-handlebars" data-template-name="person">
{{view PersonApp.UploadFileView name="logo" contentBinding="content"}}
{{view PersonApp.UploadFileView name="other" contentBinding="content"}}
<a {{action submitFileUpload content target="parentView"}}>Save</a>
</script>
Here is the custom file view object
PersonApp.UploadFileView = Ember.TextField.extend({
type: 'file',
attributeBindings: ['name'],
change: function(evt) {
var self = this;
var input = evt.target;
if (input.files && input.files[0]) {
var reader = new FileReader();
var that = this;
reader.onload = function(e) {
var fileToUpload = reader.result;
self.get('controller').set(self.get('name'), fileToUpload);
}
reader.readAsDataURL(input.files[0]);
}
}
});
Here is the controller
PersonApp.PersonController = Ember.ObjectController.extend({
content: null,
logo: null,
other: null
});
And finally here is the view w/ submit event
PersonApp.PersonView = Ember.View.extend({
templateName: 'person',
submitFileUpload: function(event) {
event.preventDefault();
var person = PersonApp.Person.createRecord({ username: 'heyo', attachment: this.get('controller').get('logo'), other: this.get('controller').get('other') });
this.get('controller.target').get('store').commit();
}
});
This will drop 2 files on the file system if you spin up the django app
EDIT (2015.06): Just created a new solution based on a component.
This solution provides an upload button with a preview and remove icon.
P.S. The fa classes are Font Awesome
Component handlebars
<script type="text/x-handlebars" data-template-name='components/avatar-picker'>
{{#if content}}
<img src={{content}}/> <a {{action 'remove'}}><i class="fa fa-close"></i></a>
{{else}}
<i class="fa fa-picture-o"></i>
{{/if}}
{{input-image fdata=content}}
</script>
Component JavaScript
App.AvatarPickerComponent = Ember.Component.extend({
actions: {
remove: function() {
this.set("content", null);
}
}
});
App.InputImageComponent = Ember.TextField.extend({
type: 'file',
change: function (evt) {
var input = evt.target;
if (input.files && input.files[0]) {
var that = this;
var reader = new FileReader();
reader.onload = function (e) {
var data = e.target.result;
that.set('fdata', data);
};
reader.readAsDataURL(input.files[0]);
}
}
});
Usage example
{{avatar-picker content=model.avatar}}
Old Answer
I took Chris Meyers example, and I made it small.
Template
{{#view Ember.View contentBinding="foto"}}
{{view App.FotoUp}}
{{view App.FotoPreview width="200" srcBinding="foto"}}
{{/view}}
JavaScript
App.FotoPreview= Ember.View.extend({
attributeBindings: ['src'],
tagName: 'img',
});
App.FotoUp= Ember.TextField.extend({
type: 'file',
change: function(evt) {
var input = evt.target;
if (input.files && input.files[0]) {
var that = this;
var reader = new FileReader();
reader.onload = function(e) {
var data = e.target.result;
that.set('parentView.content', data);
}
reader.readAsDataURL(input.files[0]);
}
},
});
Marek Fajkus you cannot use JQuery's .serialize, it makes no mention of file uploads in the documentation at JQuery UI docs
However, you could use JQuery Upload Plugin
Actually it does mention it, it says:
". Data from file select elements is not serialized."
In case of uploading multiple files, you may want to use
{{input type='file' multiple='true' valueBinding='file'}}
^^^^
This is a solution that you would use in normal HTML upload.
Additionally, you can use 'valueBinding' which will allow you to set up an observer against that value in your component.