I have a List component using Checkbox as ItemRenderer. Now the problem is there is no synch between checkbox and List selection.
When the checkbox is selected/desected, I want to update selectedIndices property of the List and vice versa. The list is allowing multiple selection.
Any sample code for this?
My code is as below:
Assign Itemrenderer in List component:
_list.itemRenderer=new ClassFactory(CheckBoxRenderer);
CheckBoxRenderer.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:CheckBox xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
label="{data.name}"
selected="{data.isSelected}">
</mx:CheckBox>
If I understood your problem correctly, here is my solution and the running example.
I added a data grid to let you see that the data provider is changed after user interaction.
//Application
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600">
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.events.CollectionEvent;
[Bindable]private var dp:ArrayCollection = new ArrayCollection([
{name: "record01", isSelected: false},
{name: "record02", isSelected: true},
{name: "record03", isSelected: false}]);
protected function onClick(event:MouseEvent):void
{
myDG.dataProvider.dispatchEvent( new CollectionEvent(CollectionEvent.COLLECTION_CHANGE));
}
]]>
</fx:Script>
<s:VGroup x="10" y="10">
<s:List dataProvider="{dp}" itemRenderer="renderers.CheckBoxRenderer" click="onClick(event)"/>
<s:Spacer height="20"/>
<s:DataGrid id="myDG" height="120" dataProvider="{dp}">
<s:columns>
<s:ArrayList>
<s:GridColumn dataField="name" headerText="Name" width="70"/>
<s:GridColumn dataField="isSelected" headerText="IsSelected" width="90"/>
</s:ArrayList>
</s:columns>
</s:DataGrid>
</s:VGroup>
</s:Application>
//Renderer
<?xml version="1.0" encoding="utf-8"?>
<s:ItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
autoDrawBackground="true">
<fx:Script>
<![CDATA[
protected function onChangeEvent(event:Event):void
{
data.isSelected = !data.isSelected;
}
]]>
</fx:Script>
<s:CheckBox label="{data.name}" selected="{data.isSelected}" change="onChangeEvent(event)"/>
</s:ItemRenderer>
EDIT
As I mentioned the data grid was added only to show the changes in the data provider.
Here is the code without additional components.
You need to control the data provider through the debuger to see that it is being changed.
The button was added just to invoke the debugger on trace() line.
//Application without the grid
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600">
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import renderers.CheckBoxRenderer;
[Bindable]private var dp:ArrayCollection = new ArrayCollection([
{name: "record01", isSelected: false},
{name: "record02", isSelected: true},
{name: "record03", isSelected: false}]);
protected function onBtnClick(event:MouseEvent):void
{
trace();
}
]]>
</fx:Script>
<s:VGroup x="10" y="10">
<s:List dataProvider="{dp}" itemRenderer="renderers.CheckBoxRenderer"/>
<s:Button click="onBtnClick(event)"/>
</s:VGroup>
</s:Application>
EDIT2
If you want to restrict the max number of selected items have a look at this code. It works well by me. This time I inserted the itemRenderer in the List definition.
Here is the working example.
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600"
creationComplete="{reculcSelectedCount()}">
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
private var _selectedCount:int = 0;
[Bindable]private var dp:ArrayCollection = new ArrayCollection([
{name: "record01", isSelected: false},
{name: "record02", isSelected: true},
{name: "record03", isSelected: false},
{name: "record04", isSelected: false}]);
[Bindable]public function get selectedCount():int
{
return _selectedCount;
}
public function set selectedCount(value:int):void
{
_selectedCount = value;
}
protected function onClick(event:MouseEvent):void
{
reculcSelectedCount();
}
protected function reculcSelectedCount():void
{
selectedCount = 0;
for each (var item:Object in dp)
{
if (item.isSelected)
selectedCount++;
}
}
]]>
</fx:Script>
<s:VGroup x="10" y="10">
<s:List dataProvider="{dp}" click="onClick(event)">
<s:itemRenderer>
<fx:Component>
<mx:HBox>
<fx:Script>
<![CDATA[
protected function onChangeEvent(event:Event):void
{
data.isSelected = !data.isSelected;
}
]]>
</fx:Script>
<s:CheckBox id="cb" label="{data.name}" selected="{data.isSelected}" enabled="{data.isSelected || (!data.isSelected && (outerDocument.selectedCount < 2))}" change="onChangeEvent(event)"/>
</mx:HBox>
</fx:Component>
</s:itemRenderer>
</s:List>
</s:VGroup>
</s:Application>
Related
I am facing one issue in react native with nativebase.
<Content>
<List>
<ListItem>
<Left style={{ flex: 0 }}>
<Icon name="user" type="SimpleLineIcons"></Icon>
</Left>
<Body>
<Text> Profile </Text>
</Body>
<Right>
<Switch value={this.state.profile} />
</Right>
</ListItem>
....
</List>
</Content>
When i update state(rerender component) list automatically scroll to top/first :
this.setState({profile: true });
How to prevent autoscroll for better user experience?
Please try this prop inside <Content> component:
<Content enableResetScrollToCoords={false} />
You can try this solution too:
handleScroll(event) {
this.setState({ scrollY: event.nativeEvent.contentOffset.y });
}
render() {
if (this.contentRef) {
if (this.contentRef._scrollview) {
this.contentRef._scrollview.resetScrollToCoords({ x: 0, y: this.state.scrollY });
}
}
return (
<Content
ref={(c) => this.contentRef = c}
onScroll={event => this.handleScroll(event)}
>
);
}
I've worked around this issue just by passing the y offset from the scrollview to the parent using the following. By using this.offsetY, an update to the value won't cause a re-render, but it will be passed to the child component when you need it:
In your parent component:
setScrollOffset = (y) => (this.offsetY = y);
return (
<MyChildComponent
data={yourData}
setScrollOffset={this.setScrollOffset}
yOffset={this.offsetY}>
</MyChildComponent>
);
In your child component:
onScrollEndDrag = ({
nativeEvent: {
targetContentOffset: {y},
},
}) => {
this.props.setScrollOffset(y);
};
...
render() {
return (
<ScrollView
contentOffset={{
x: 0,
y: this.props.yOffset !== undefined ? this.props.yOffset : 0,
}}
contentContainerStyle={styles.scrollViewContent}
onScrollEndDrag={this.onScrollEndDrag}>
...
</ScrollView>
)
}
I should add that if you wish for to reset the yOffset to 0 (start at the top of the ScrollView) then you should reset that somewhere else in your parent component if a specific state change occurs.
I created functional list component and render in drawer. In drawer there text and switch component.
My list component was inside renderer method so whenever i toggle switch render method fire and list autocamatilly go to up.
Now, I put list component out of render method. This resolve my issue.
Thanks all guys for your quick response and suggestion.
Example :
render(){
const drawerContent = ()=>(
<Content>
<List>
<Switch value={this.state.flag) />
</List>
</Content>
)
return(
<Drawer content={drawerContent}>
<Container>
....
</Container>
</Drawer>
)
}
To
drawerContent = ()=>(
<Content>
<List>
<Switch value={this.state.flag) />
</List>
</Content>
)
render(){
return(
<Drawer content={this.drawerContent}>
<Container>
....
</Container>
</Drawer>
)
}
I'm just creating my first custom component, and I'm really struggling with the basics. My component:
<template>
<StackLayout>
<Label :text="title" />
<Label :text="slate.description" />
</StackLayout>
</template>
<script>
var slate;
export default {
name: "SlateComponent",
props:
['slate', 'title'],
data() {
return {
slate: slate,
};
},
}
</script>
This component is to be updated regularly, and occupy a good chunk of the app home page:
<template>
<Page class="Page" actionBarHidden="true" backgroundSpanUnderStatusBar="true" >
<StackLayout>
<StackLayout row="0">
...
</StackLayout>
<StackLayout row="1">
<SlateComponent :title="title" :slate="slate" />
</StackLayout>
...
</Page>
</template>
<script>
...
import SlateComponent from "./SlateComponent";
var slateTitle;
var title;
var gameSlates;
var currentSlate;
var slate;
data() {
return {
events: events,
title: title,
slate: slate,
};
},
async created() {
this.gameSlates = await getGameSlates();
this.currentSlate = this.gameSlates[2];
this.title = this.currentSlate.description;
console.info("The title is: " + this.title);
this.slate = this.currentSlate;
}
};
Result: No matter what I do, no props object passes to the component.
If I comment out the
the app compiles and runs fine, logs currentSlate or its property, description and displays the component, including title.
But, when I include that line, it blows up, with the error: slate is undefined.
(I know that
props:
['slate', 'title'],
is not proper according to the style guide. But I couldn't get the preferred format to work either.)
What am I missing here?
When accessing props anywhere outside of the template, this is required
data() {
return {
slate: slate,
};
}
Should be
data() {
return {
slate: this.slate
};
},
I use odoo 9 is I noticed that it does not have a control for the input format for example for the email field I can enter any character and it will consider it as an email address. So I wanted to know how I can control the input format for a field for example for the email field the presence of "# and the" are mandatory or for the code field VAT for partners the presence of "/" is mandatory .
template.xml
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<template id="assets_backend" name="stock assets mask ext" inherit_id="web.assets_backend">
<xpath expr="." position="inside">
<link rel="stylesheet" href="/field_mask/static/src/css/mask.css"/>
<script type="text/javascript" src="/field_mask/static/src/js/widgets.js"></script>
<script type="text/javascript" src="/field_mask/static/src/lib/jquery.inputmask.bundle.js"></script>
</xpath>
</template>
</data>
partner_view.xml
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="res_partner_view_purchase_buttons_TVA_RC" model="ir.ui.view">
<field name="name">num.TVA.RC.res.partner.view.purchase.</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_form"/>
<field name="arch" type="xml">
<xpath expr="//field[#name='website']" position="after">
<field name="CodeTVA" select="1" placeholder="Code TVA" widget="mask" data-inputmask-mask="9999999/A/A/A/999" />
<field name="RC" select="1" placeholder="Num RC"/>
</xpath>
</field>
</record>
</data>
widgets.js
function openerp_field_mask_widgets(instance) {
instance.web.form.FieldMask = instance.web.form.FieldChar.extend({
template : "FieldMask",
render_value: function() {
var show_value = this.get_value();
var field = this;
if (!field.get("effective_readonly")) {
field.$el.find('input').val(show_value);
var mask = field.node.attrs.mask;
field.$el.find('input').inputmask(mask);
} else {
field.$(".oe_form_char_content").text(show_value);
}
},
get_value: function() {
val = this.get('value');
if (!val) {
return '';
}
return val;
},
});
instance.web.form.widgets.add('mask', 'instance.web.form.FieldMask');
}
openerp.field_mask = function(openerp) {
openerp.field_mask = openerp.field_mask || {};
openerp_field_mask_widgets(openerp);
}
You can make your own widget with inputMask by inheriting FieldChar and you can find usefull mudules at odoowidgets or field_mask
I want to add groups attribute in qweb template like this:
<t t-extend="UserMenu">
<t t-jquery=".dropdown-menu" t-operation="replace">
<ul class="dropdown-menu">
<li>Preferences</li>
<li>My Odoo.com account</li>
<li groups="custom_preference_menu.group_yook_about_menu">About Odoo</li>
<li>Help</li>
<li>Log out</li>
</ul>
</t>
</t>
but it'is not working.
how can i solve this problem?
Well the syntax is right. Are you sure that you selected the right group with module_where_the_group_is_created.group_name?
Here is an incomplete answer. It will hide the Preferences menu item for non-admin users:
xml:
<t t-extend="UserMenu">
<t t-jquery="div.dropdown-menu.dropdown-menu-right" t-operation="replace">
<div class="dropdown-menu dropdown-menu-right" role="menu">
<a role="menuitem" href="#" data-menu="shortcuts" class="dropdown-item d-none d-md-inline-block">Shortcuts</a>
<a role="menuitem" href="#" data-menu="settings" class="dropdown-item" id="preference_menuitem">Preferences</a>
<a role="menuitem" href="#" data-menu="logout" class="dropdown-item">Log out</a>
</div>
</t>
</t>
js:
odoo.define('pos_custom_ui.custom_menuitems', function (require) {
"use strict";
var config = require('web.config');
var UserMenu = require('web.UserMenu');
UserMenu.include({
start: function () {
var self = this;
var session = this.getSession();
if (!session.is_admin){
this.$('#preference_menuitem').hide();
}
this.$el.on('click', '[data-menu]', function (ev) {
ev.preventDefault();
var menu = $(this).data('menu');
self['_onMenu' + menu.charAt(0).toUpperCase() + menu.slice(1)]();
});
return this._super.apply(this, arguments).then(function () {
var $avatar = self.$('.oe_topbar_avatar');
if (!session.uid) {
$avatar.attr('src', $avatar.data('default-src'));
return Promise.resolve();
}
var topbar_name = session.name;
if (config.isDebug()) {
topbar_name = _.str.sprintf("%s (%s)", topbar_name, session.db);
}
self.$('.oe_topbar_name').text(topbar_name);
var avatar_src = session.url('/web/image', {
model:'res.users',
field: 'image_128',
id: session.uid,
});
$avatar.attr('src', avatar_src);
});
},
});
});
Include the js file in web.assets_backend:
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="custom_dashboard" inherit_id="web.assets_backend">
<xpath expr="." position="inside">
<script type="text/javascript" src="/pos_custom_ui/static/src/js/custom_menuitems.js"></script>
</xpath>
</template>
</odoo>
I am using google chart for the first time now...
My requirement is to read xml content to form a chart..
I need to pass the xml file name so that it reads value from specific tag...Following is what I hve tried so far...But no success...
XML file:(FlowChart.xml)
<?xml version="1.0" encoding="utf-8" ?>
<Flow>
<Node>
<Id>AN001</Id>
<Type>Annc</Type>
<Description>Welcome msg</Description>
<Next>MN001</Next>
</Node>
<Node>
<Id>MN001</Id>
<Type>Menu</Type>
<Description>Language Selection</Description>
<Next>AN002</Next>
</Node>
</Flow>
Script:
google.load('visualization', '1', {packages:['orgchart']});
$(document).ready(function(){
$.ajax({
type: "GET",
url: "FlowChart.xml",
dataType: "xml",
success: xmlParser
});
});
function xmlParser(xml) {
$('#load').fadeOut();
$(xml).find("Node").each(function () {
title = $(this).find("Id").text();
alert('Hi');
});
}
google.setOnLoadCallback(drawChart);
I have used the same code without googleJSAPI and found the required result...
Please help on this...
Try this:
function xmlParser(xml) {
$('#load').fadeOut();
var id, type, description, next;
var data = new google.visualization.DataTable();
data.addColumn('string', 'Id');
data.addColumn('string', 'Type');
data.addColumn('string', 'Description');
data.addColumn('string', 'Next');
// parse the XML
$(xml).find("Node").each(function () {
id = $(this).find("Id").text();
type = $(this).find("Type").text();
description = $(this).find("Description").text();
next = $(this).find("Next").text();
data.addRow([id, type, description, next]);
});
// do something with data
}
function drawChart() {
$.ajax({
type: "GET",
url: "FlowChart.xml",
dataType: "xml",
success: xmlParser
});
}
google.load('visualization', '1', {packages: ['orgchart'], callback: drawChart});