Is there anyway to do a if condition in the XML file of nativescript? (without angular)
if
<Card:CardView class="cardStyle" margin="10" elevation="40" radius="5">
<Slider value="18" minValue="5" maxValue="30" />
</Card:CardView>
else
<Card:CardView class="cardStyle" margin="10" elevation="40" radius="5">
<Label text="Example" class="h3" margin="10" />
</Card:CardView>
What you could do is use a boolean property (which in its get function has the condition you want) and bind it to the visibility of the CardView.
Looks like you can show/hide from the template file using visibility:
In XML file: ie. 'sample-page.xml'
<Button text="{{ isShowing ? 'Hide Me' : 'Show Me' }}" tap="toggleShowing"/> <!-- Notice missing interpolation in Button tag -->
<Label text="Showing Hidden Element" visibility="{{ isShowing ? 'visible' : 'collapsed' }}"/>
In Page file: ie. 'sample-page.ts'
let model = new ViewModel();
// Event handler for Page 'loaded' event attached in main-page.xml
export function pageLoaded(args: observable.EventData) {
const page = <Page>args.object;
page.bindingContext = model;
}
export function toggleShowing() {
model.set('isShowing', !model.get('isShowing'));
}
In View Model: ie. 'sample-view-model.ts'
isShowing:boolean = false;
The only way I found to do to do this was to use the navigatingTo event of the page:
export function navigatingTo(args: EventData) {
let page = <Page>args.object;
var myLayout = <StackLayout>page.getViewById("myLayout");
if (condition) {
let segmentedBar = new SegmentedBar;
...
myLayout.addChild(segmentedBar);
}
else {
let button: Button = new Button;
...
myLayout.addChild(button);
}
No possibility in the template file :-/
It can be done using ng-container.For example
<ng-container *ngIf="true">Your logic here....</ng-container>
Related
I am trying to get and use index of the list in NativeScript, in this case using Repeater.
Here is my code:
<ScrollView>
<Repeater items="{{ sviArtikli }}" itemTemplateSelector="$index">
<Repeater.itemsLayout>
<WrapLayout orientation="horizontal"/>
</Repeater.itemsLayout>
<Repeater.itemTemplate>
<Card:CardView margin="10" width="45%" height="250" item="{{ id }}" myIndex="{{ $index }}" tap="detaljiArtikla">
<grid-layout rows="250, *" columns="*, 50">
<Image loaded="slikaUcitana" class="lista-katalozi-slika" id="slicice" opacity="1" stretch="aspectFill" colSpan="3" row="0" src="{{ 'https://url_location/' + slika_image }}" />
</grid-layout>
</Card:CardView>
</Repeater.itemTemplate>
</Repeater>
</ScrollView>
The part with myIndex="{{ $index }}" does not work as index can not be calculated in Repeater.
Is there a way I can get index for every item in repeater? - ListView works, but I can not use listview in this case.
I have created a plyground for you here. You can pass the unique ID in your dataprovider and can access in item.
P.S. There is an open Feature-request to support index in repeater, you can also cast your vote there.
Accessing item index is not yet supported in Repeater, but if it's crucial for you, you could extend the Repeater class to support index.
indexed-repeater.ts (tested against tns-core-modules v6.0.1)
import { Repeater } from "tns-core-modules/ui/repeater";
import { CSSType } from "tns-core-modules/ui/layouts/layout-base";
import { parse } from "tns-core-modules/ui/builder";
export module knownTemplates {
export const itemTemplate = "itemTemplate";
}
#CSSType("Repeater")
export class IndexedRepeater extends Repeater {
public refresh() {
if (this.itemsLayout) {
this.itemsLayout.removeChildren();
}
if (!this.items) {
return;
}
const length = this.items.length;
for (let i = 0; i < length; i++) {
const viewToAdd = this.itemTemplate ? parse(this.itemTemplate, this) : (<any>this)._getDefaultItemContent(i);
const dataItem = (<any>this)._getDataItem(i);
viewToAdd.bindingContext = {
item: dataItem,
index: i
};
this.itemsLayout.addChild(viewToAdd);
}
(<any>this)._isDirty = false;
}
}
Sample Usage
<ScrollView xmlns:comps="components/indexed-repeater">
<comps:IndexedRepeater items="{{ items }}">
<comps:IndexedRepeater.itemTemplate>
<Label text="{{ index + ' ' + item.name }}" class="m-10 h3" />
</comps:IndexedRepeater.itemTemplate>
</comps:IndexedRepeater>
</ScrollView>
Playground Sample
Hi guys thanks and it helped. What I did, without changing any code base is pushing locally generated ID's into JSON element and used them for indexes.
Now json has { "my_index": "0" ..... } which I change in for loop when fetch is over. I am using those indexes just to scroll the view on certain element in the list.
http.getJSON("https://....")
.then(function (response){
for(var i = 0; i < response.length; i++) {
response[i].my_index = i; // change response on the fly.
// do something with response....
}
}, function(error) {
console.error(JSON.stringify(error));
})
I guess this could help to someone as well.
I am using 'react-native-material-textfield' and it working well, but I need to show error for empty field when clicking on submit button. I have searched lot-of but didn't find any solution.
Put the error message in your state and fill it with a message after clicking on the submit button, if your validation process fails.
render(){
return (
<View>
<TextField
{...props}
error={this.state.error}
errorColor={'red'}
onFocus={() => this.setState({error: ''})}
/>
<Button {...props} />
</View>)}
Check the example on the developers github repository.
According to the module documentation and examples, whenever your this.state.errors for each field is not empty, its error is shown. So your form should look like this:
class Form extends Component {
// ... Some required methods
onSubmit() {
let errors = {};
['firstname'] // This array should be filled with your fields names.
.forEach((name) => {
let value = this[name].value();
if (!value) {
errors[name] = 'Should not be empty'; // The error message when field is empty
}
});
this.setState({ errors });
}
render() {
let { errors = {}, data } = this.state;
return (
<View>
<TextField
value={data.firstname}
onChangeText={this.onChangeText}
error={errors.firstname}
/>
<Text onPress={this.onSubmit}>Submit</Text>
</View>
);
}
}
CUSTOM COMPONENT
// ...
#Output() submit: EventEmitter < any > = new EventEmitter();
// ...
onFilterSubmit($event): void {
this.submit.emit($event);
this.formData = {
minDate: new Date().toISOString(),
maxDate: new Date().toISOString()
};
}
<form (ngSubmit)="onFilterSubmit(formData)">
<!-- -- -->
<button mat-button
mat-raised-button
[disabled]="reqsForm.form.invalid"
type="submit"
color="primary">
{{labels.submit}}
</button>
</form>
OUTER COMPONENT
// ...
onFilterSubmit($event): void {
console.info("FORM SUBMIT", $event);
}
<custom-component (submit)="onFilterSubmit($event)">
<!-- -- -->
</custom-component>
OUTPUT
FORM SUBMIT > Object
FORM SUBMIT > Object
The reason why this was happening is that an event called "submit"
is catchable already from outside the custom component.
I solved by changing the custom event name to filterSubmit
Note also that the type submit on the button - in this use case - is virtually useless, since as default one button in a form will be of type submit.
i am trying to build a Ui component in Reactjs which combines a checkbox and a text input attched to it (instead of a text label) so that if the checkbox is checked , the user can change the text input , and if its unchecked the user will not be able to do so
the final goal is to render outside of the component all of textinputs valus which left checked as a list or as a menu item.
Its should look like this :
Checkbox with Text input
anyone knows how should i do this ? im new to reactjs and got a bit confused how to pass logic between two components(as in here between the checkbox and the text input and between the "combo" component and the outer rendered list) .
thanks in advance !
EDIT1:
well i managed to build the component but i cant make the children call the parent handler (handlerCheckbox , handlerInput)in order to actually make the magic happen.
anything im doing wrong ?
this is the child:
class CheckboxTxtInput extends React.Component{
constructor(props){
super(props);
console.log(props.isChecked)
}
handleCheckboxChild(e) {
this.props.handleCheckbox(e,this.props.id)
}
handleInputChild(e){
this.props.handleInput(e,this.props.id)
}
render(){
return (
<div>
<input type="checkbox" onChange={this.handleCheckboxChild} defaultChecked={this.props.isChecked} />
<input type="text" value={this.props.inputValue} disabled={!this.props.isChecked} onChange={this.handleInputChild}/>
</div>
)
}
}
This is the parent:
export default class Text extends React.Component {
constructor(props) {
super(props);
this.state = {
textItems: [{id:0,inputValue:'text',isChecked:true},{id:1,inputValue:'text',isChecked:true}
,{id:2,inputValue:'text',isChecked:true},{id:3,inputValue:'text',isChecked:true}]
};
this.handleCheckbox = this.handleCheckbox.bind(this);
this.handleInput= this.handleInput.bind(this);
}
handleCheckbox(e,id) {
var stateCopy = Object.assign({}, this.state);
stateCopy.textItems[id].isChecked = e.target.value;
this.setState(stateCopy);
}
handleInput(e,id){
var stateCopy = Object.assign({}, this.state);
stateCopy.textItems[id].text = e.target.value;
this.setState(stateCopy);
}
render () {
return (
<div>
<hr className="divider-long"/>
<UI.sectionDividerLabeled label="Show/Hide Text"/>
<hr className="divider-long"/>
<p>Here you can show\hide your text</p>
<div>
<CheckboxTxtInput id={this.state.textItems[0].id} isChecked={this.state.textItems[0].isChecked}
inputValue={this.state.textItems[0].inputValue} handleInput={this.handleInput}
handleCheckbox={this.handleCheckbox} />
<CheckboxTxtInput id={this.state.textItems[1].id} isChecked={this.state.textItems[1].isChecked}
inputValue={this.state.textItems[1].inputValue} handleInput={this.handleInput}
handleCheckbox={this.handleCheckbox}/>
<CheckboxTxtInput id={this.state.textItems[2].id} isChecked={this.state.textItems[2].isChecked}
inputValue={this.state.textItems[2].inputValue}
handleInput={this.handleInput} handleCheckbox={this.handleCheckbox}/>
<CheckboxTxtInput id={this.state.textItems[3].id} isChecked={this.state.textItems[3].isChecked}
inputValue={this.state.textItems[3].inputValue} handleInput={this.handleInput}
handleCheckbox={this.handleCheckbox}/>
</div>
<RenderText />
</div>
)
}
}
The simplest, React-like way to do this is to have a parent wrapper component - say LabeledCheckbox which contains your Text input and your Checkbox components.
When either of the child components do something, they call a callback provided by the parent, and the parent maintains the state for the two components, passing that state down into the props of both children.
The children in this case would never maintain their own state, instead simply calling callbacks and being prop-fed.
Create one component with checkbox and input field with the state of the checkbox and text field.
And then you can reuse it where you want.
You can do something like this :
class CheckboxTxtInput extends React.Component{
constructor(){
super();
this.state = {
checkbox: false,
inputValue: ""
}
}
handleCheckbox(e){
this.setState({checkbox: e.target.checked})
}
handleInput(e){
this.setState({inputValue: e.target.value})
}
render(){
return (
<div>
<input type="checkbox" onChange={this.handleCheckbox.bind(this)} checked={this.state.checkbox}/>
<input type="text" value={this.state.inputValue} disabled={this.state.checkbox} onChange={this.handleInput.bind(this)}/>
</div>
)
}
}
class Test extends React.Component {
render(){
return (
<div><CheckboxTxtInput /></div>
)
}
}
React.render(<Test />, document.getElementById('container'));
Here is the fiddle.
Hope this helps.
In my web application I have (let's say) 2 tabs, both are custom knockout components. Their visibility is controlled by the following syntax:
<div id="page" data-bind="component: { name: currentTab }"></div>
where currentTab is an observable with the name of the current tab.
In both tabs I have visualisations with D3.js, using custom bindings. The problem is that these custom bindings are reinitialised after opening a tab. Is there a way to only load them once so that they don't need to be redrawn?
Note that the component's viewmodels aren't reinitialised, as they are created with the { instance: new viewModel() } trick:
define(['knockout', 'text!./tab-one.html', 'jquery'], function(ko, template, $) {
function ViewModel() {
var self = this;
};
return {
viewModel: { instance: new ViewModel() },
template: template
};
});
You may need to change slightly your approach:
<div id="page">
<div id="tab-1" data-bind="visible: (currentTab() == 'drawing-1'), component:{name:'drawing-1'}"></div>
<div id="tab-2" data-bind="visible: (currentTab() == 'drawing-2'), component:{name:'drawing-2'}"></div>
</div>
As alternative, you can add some more flexibility to your components if you provide a viewmodel, for example to encapsulate the logic of the visibility inside the component itself, and moreover - if you can also eventually skip the first redraw at component initialization:
This is just an example, but you can see the idea:
ko.components.register('drawing', {
viewModel: function(params) {
// Behaviors
this.active1 = params.currentTab == 'drawing-1';
this.active2 = params.currentTab == 'drawing-2';
this.draw = ko.computed(function() {
if (!ko.computedContext.isInitial()) {
// do some changes to the drawing
}
});
},
template:
'<div data-bind="visible: active1">'+
// drawing 1
'</div>'+
'<div data-bind="visible: active2">'+
// drawing 2
'</div>'
});