Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Arbitrary positioning of a view #3812

Closed
3rror404 opened this issue Mar 16, 2017 · 5 comments
Closed

Arbitrary positioning of a view #3812

3rror404 opened this issue Mar 16, 2017 · 5 comments
Labels

Comments

@3rror404
Copy link

I've been struggling with something for a few days now. I can't seem to find an example of what I am trying to do anywhere so I'm not really sure I'm taking the right approach.

I need to be able to position an element/layout (created with JS) at an arbitrary position within another element.

In this example, I need to position the Label within the StackLayout

XML

<Page xmlns="http://schemas.nativescript.org/tns.xsd" loaded="onLoaded" navigatedTo="onNavigatedTo" class="page">
    <StackLayout id="test">

    </StackLayout>
</Page>

JS

var labelModule = require("ui/label");

var page;

exports.onLoaded = function(args) {
    page = args.object;
}

exports.onNavigatedTo = function(args) {
    var test = page.getViewById('test');

    positionEl(test);    
}

function positionEl(el) {
    var myLabel = new labelModule.Label();
    myLabel.text = 'My label';

    el.addChild(myLabel);
}

I've made a number of attempts at solving this myself but can't seem to find anything that works.

  1. Using an AbsoluteLayout as a wrapper.. This won't work because I need to be able to interact with other elements below the AbsoluteLayout.
  2. Setting the frame.origin of the native view.
  3. I've also tried using myLabel.layout(x, y, x, y) but this doesn't seem to have any effect.

I'm actually trying to produce a text input validation message like the image below. the green box is the AbsoluteLayout that prevents interaction with the other page elements.
ns-absolute-layout

I can't use option 1 because of the reasons stated in that post. I've no idea if options 1 or 2 can be used to achieve this as I can't find any examples of using either.

Can anyone help me out with this?

@tsonevn
Copy link
Contributor

tsonevn commented Mar 17, 2017

Hi @3rror404,
For your case the easiest solution is to use AbsoluteLayout, which will cover the whole screen, however, the limitations of the scenario is that the interaction with te other components will be lost.
Regarding that, you could also try to use GridLayout and to verify, whether will be applicable for you.
I am attaching sample code:

main-page.xml

<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="navigatingTo" class="page">

    <Page.actionBar>
        <ActionBar title="My App" icon="" class="action-bar">
        </ActionBar>
    </Page.actionBar>

    <StackLayout class="p-20">
        <Button text="Show labels" tap="onTap" />
        
        <GridLayout horizontalAlignment="center" backgroundColor="green" width="300" height="600" rows="100 100 auto" columns="*">
            <TextField row="0" hint="Enter some text" text="{{  }}" />
            <TextField row="1" hint="Enter some text" text="{{  }}" />
            <Label  visibility="{{ isItemVisible ? 'visible' : 'collapsed' }}" row="1" height="20" backgroundColor="red" text="Hint validation label" textWrap="true" />
            <Label  visibility="{{ isItemVisible ? 'visible' : 'collapsed' }}"  row="2" backgroundColor="red" text="Hint validation label 2" textWrap="true" />
            
        </GridLayout>
    </StackLayout>
</Page>

main-page.ts

import { EventData, Observable } from 'data/observable';
import { Page } from 'ui/page';
import { HelloWorldModel } from './main-view-model';
var tmpObservable: Observable = new Observable();
var value = false;
// Event handler for Page "navigatingTo" event attached in main-page.xml
export function navigatingTo(args: EventData) {

    let page = <Page>args.object;

    tmpObservable.set("isItemVisible", value);
    page.bindingContext = tmpObservable;
}
export function onTap() {
    console.log("test emulator");
    value = !value;
    tmpObservable.set("isItemVisible", value);
}

In the example, the important part is to setup the row for the first label to be the same as the second TextView row.

You could also review nativescript-telerik-ui-pro plugin and its DataForm component, which provides validation functionality and will help you in creating such a form.

@3rror404
Copy link
Author

Hi @tsonevn,

thanks for the suggestion. I was really hoping to be able to do this without relying on one of the Layouts for positioning. Is it simply not possible to do this using frame or layout() or some other method that I'm not aware of?

@tsonevn
Copy link
Contributor

tsonevn commented Mar 17, 2017

Hi @3rror404,
Instead of using native code and to change the frame of the component, you could use component translateY and translateX properties, which will allow you to change the position of the element from code behind on specific condition.
For example:
XML

<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="navigatingTo" class="page">

    <Page.actionBar>
        <ActionBar title="My App" icon="" class="action-bar">
        </ActionBar>
    </Page.actionBar>

    <StackLayout class="p-20">
        <Button text="Show labels" tap="onTap" />
        
        <StackLayout horizontalAlignment="center" backgroundColor="green" width="300" height="600" >
            <Label id="labelID"  visibility="{{ isItemVisible ? 'visible' : 'collapsed' }}" height="20" backgroundColor="red" text="Hint validation label" textWrap="true" />
            <TextField  hint="Enter some text" height="100" text="{{  }}" />
            <TextField id="secondTextField" height="100" hint="Enter some text" text="{{  }}" />
        </StackLayout>
    </StackLayout>
</Page>

TypeScript

import { EventData, Observable } from 'data/observable';
import { Page } from 'ui/page';
import { HelloWorldModel } from './main-view-model';
import {Label} from "ui/label";
import {TextField} from "ui/text-field"
var tmpObservable: Observable = new Observable();
var newvalue= 0;
var value = false;
// Event handler for Page "navigatingTo" event attached in main-page.xml
let page:Page;
export function navigatingTo(args: EventData) {

    page = <Page>args.object;

    tmpObservable.set("isItemVisible", value);
    page.bindingContext = tmpObservable;
}
export function onTap() {
    
    tmpObservable.set("isItemVisible", true);
    var label:Label = <Label>page.getViewById("labelID");
    var textfield:TextField = <TextField>page.getViewById("secondTextField");
    newvalue = newvalue+100;
    label.translateY=newvalue;

}

@3rror404
Copy link
Author

Thanks @tsonevn. I appreciate the suggestions but having the error message within the markup was not a viable solution for me.

I've found that you can use marginTop and marginLeft to position an element in it's parent. The below code will create an error message like the one in my first post and position it just below whatever element you pass to createValidationError(). It will be sized accordingly and animate it's self into view.

function createValidationError(el) {

    // ## Get element size and position
    // ## (element is GridView column so use parent GridView)
    var elBounds = el.parent.ios.layer.bounds;
    var elWidth = elBounds.size.width;
    var elHeight = elBounds.size.height;
    var elPosition = el.parent.getLocationInWindow();   

    // ## Create validation error wrapper element
    var validationWrapper = new stackLayoutModule.StackLayout();
    validationWrapper.className = 'validation-wrapper';
    validationWrapper.width = elWidth;
    validationWrapper.id = el.id + '_error';

    // ## Position wrapper in parent
    validationWrapper.marginTop = elPosition.y + elHeight;
    validationWrapper.marginLeft = elPosition.x;

    // ## Create text element
    var validationMessage = new labelModule.Label();
    validationMessage.className = 'validation-message';
    validationMessage.text = 'Please enter something.';

    // ## Create arrow element
    var validationArrow = new stackLayoutModule.StackLayout();
    validationArrow.className = 'validation-arrow';
    validationArrow.horizontalAlignment = 'left';

    // ## Add views to wrapper
    validationWrapper.addChild(validationArrow);
    validationWrapper.addChild(validationMessage);

    //## Find LayoutBase and add validation wrapper element
    var frame = require('ui/frame');
    var page = frame.topmost().currentPage;
    var LayoutBase = require('ui/layouts/layout-base').LayoutBase;

    page._eachChildView(function (view) {
        if (view instanceof LayoutBase) {
            view.addChild(validationWrapper);
            return false;
        }
        return true;
    });

}

exports.tapTextInput = function(args) {
    var el = args.view;
    var elError = page.getViewById(el.id + '_error');

    if (typeof elError != 'undefined') {
        elError.parent.removeChild(elError);
    }
}
@keyframes validationWrapperIn {
    from { 
        opacity: 0;
        transform: translate(0, 10);
    }
    to {
        opacity: 1;
        transform: translate(0, 0);
    }
}

.validation-wrapper {
    animation-name: validationWrapperIn;
    animation-duration: 0.35;
    animation-timing-function: ease-out;
    vertical-align: top;
    horizontal-align: left;
}

.validation-message {
    background-color: #d40d0d;
    color: #FFF;
    height: auto;
    padding: 5 10;
    border-radius: 4;
}

.validation-arrow {
    width: 20;
    height: 10;
    border-color: transparent transparent #d40d0d transparent;
    border-width: 0 10 10 10;
    margin-left: 10;
    margin-bottom: -1;
}

@lock
Copy link

lock bot commented Aug 28, 2019

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@lock lock bot locked and limited conversation to collaborators Aug 28, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

2 participants