-
-
Notifications
You must be signed in to change notification settings - Fork 241
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
How to correctly implement custom components (col and row at custom components within a GridLayout) #1422
Comments
I am actually facing the same issue. I tried to make my component extends That s a really important feature |
If you implement the @input rules for your custom component, and then add |
@breningham You mean doing |
@breningham @Component({
selector: 'my-cool-component',
template: `<Label [row]="row" text="I am in the second row."></Label>`
})
export class MyCoolComponent {
@Input() row: number;
}
@Component({
selector: "ns-app",
template: `<GridLayout rows="100, 100, 100", columns="*">
<Label text="I am in the first row" row="0"></Label>
<my-cool-component row="1"></my-cool-component>
<Button text="I am in the third row" row="2"></Button>
</GridLayout>
`
})
export class AppComponent {
} Sadly, this doesnt work either. If it would work though, it would kinda result in a lot of boilerplate. |
Weird as I'm using it in an app and it is infact working.
Though I implement all of the attributes. I'll send an example when I'm at
my pc.
…On Thu, 12 Jul 2018, 09:51 Maik, ***@***.***> wrote:
@breningham <https://github.com/breningham>
Do you mean like this?
@component({
selector: 'my-cool-component',
template: `<Label [row]="row" text="I am in the second row."></Label>`
})export class MyCoolComponent {
@input() row: number;
}
@component({
selector: "ns-app",
template: `<GridLayout rows="100, 100, 100", columns="*">
<Label text="I am in the first row" row="0"></Label> <my-cool-component row="1"></my-cool-component> <Button text="I am in the third row" row="2"></Button> </GridLayout> `
})export class AppComponent {
}
Sadly, this doesnt work either. If it would work though, it would kinda
result in a lot of boilerplate.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1422 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AWGO4joivwbQCQYykVeSyhLsefbdGUPyks5uFw4NgaJpZM4VJUl0>
.
|
Also it does not work and you should not need to do |
We need some explanation for using this in Angular because the Big deal of Angular is the reusable Components and NativeScript don't have any docs about it, that is sad for this great framework, we need this feature ASAP. |
Another possible workaround is to for example @maik-becker component could work with: // register component host with NativeScript Label
registerElement('my-cool-component', () => require('tns-core-modules/ui/label').Label)
@Component({
selector: 'my-cool-component',
template: `<ng-container></ng-container>`
})
export class MyCoolComponent {
// access Label[text] host property
@HostBinding text = 'I am in the second row.';
}
@Component({
selector: "ns-app",
template: `<GridLayout rows="100, 100, 100", columns="*">
<Label text="I am in the first row" row="0"></Label>
<my-cool-component row="1"></my-cool-component>
<Button text="I am in the third row" row="2"></Button>
</GridLayout>
`
})
export class AppComponent {
} |
I finally did it, just extends your component class to
After that your component can use any NativeScript CSS property or component properties like backgroundColor, col and row, etc without the need of wrap it inside another element. 👍 |
@darkyelox One minor thing. import { ContentView } from 'tns-core-modules/ui/content-view/content-view';
if (!isKnownView('MyButton')) {
registerElement('MyButton', () => ContentView);
} |
@m-abs you think it can be make automagically on backstage for every new component created? |
@m-abs @yasserN could't this be done inside of Nativescript-angular (i think it's possible), this would be a huge difference because the ui tree would get a lot smaller in some screen implementations and also it would be simpler for beginners. what do you think @NickIliev ? |
It is worth a try. I've made the changes needed here: I'm concerned this change will break too much. |
humm indeed Switch from ProxyViewContainer to ContentView might break a few stuff, did you tested an app ? |
I've tested in It seems to work great and fells smoother but the last part might just be in my head. :) |
so, now we will be able to use built-in directives like |
Hi @m-abs , Are you interested in creating a PR from your fork with the described changes. We could run our tests on that PR to see what the picture is and if something will be broken. |
@VladimirAmiorkov Of cause :) |
Hey folks, Components use Unfortunately, this leads to the the "row/col" issue. It is quite easily solvable by adding a
You can then use the component and set the
Here is a playground showing this approach. |
@vakrilov I think the current behavior is very surprising to most people.I'm sure this isn't the first nor the last issue about the problem. Without the trick of registering or components as A short while back I reported this this bug on I fully understand not wanting to make So I agree with the comment by @danielgek on the PR, this should be documented :) |
I totally agree with you. Here are some suggestions:
Summary: |
Yes, no. 3 is a good idea. It would still be a bit surprising that styling from the parent component won't work, but I don't think it is needed very much. Maybe the |
Hi @vakrilov On the usage of the "registerElement" solution, how should be the right way when working on a shared (WEB/{N}) project? We do not want the WEB to be dependant on {N} stuff. It would be nice if in the {N} version of the module you could declare the components requiring registration and then the core would do it for you auto-magically. |
Maybe we can create a typescript decorator over the component that do the work of register it using @TNSAngularComponent('SomeNice')
@Component({
selector: 'SomeNice',
template: `<StackLayout></StackLayout`
})
export class SomeNiceComponent {
} |
@darkyelox You might as well write a normal function to register the component-selector as a TNS View-class. We use these helper functions. import { isKnownView, registerElement, ViewClass } from 'nativescript-angular/element-registry';
import { ContentView } from 'tns-core-modules/ui/content-view/content-view';
import { GridLayout } from 'tns-core-modules/ui/layouts/grid-layout/grid-layout';
import { StackLayout } from 'tns-core-modules/ui/layouts/stack-layout/stack-layout';
export function registerComponentView(tagName: string, viewClass: ViewClass = ContentView) {
if (isKnownView(tagName)) {
return;
}
registerElement(tagName, () => viewClass);
}
export function registerComponentStackLayout(tagName: string) {
registerComponentView(tagName, StackLayout);
}
export function registerComponentGridLayout(tagName: string) {
registerComponentView(tagName, GridLayout);
} |
@vakrilov I'm weighing in on the option 3 of ProxyViewContainer forwarding layout specific options. I'm working on a POC that would allow gridlayouts to be specified by using the CSS grid spec. It's really trivial, but it works by watching the direct children's CSS properties and setting their rows/columns accordingly. When we get into angular views, you'd have to wrap them in another layout, which is not ideal for simple components. On the web, angular views are usually blocks. ProxyViewContainer is supposed to be a helper to decrease view complexity as native views are heavier. I imagine the developer should be aware of this difference, but we should aim to ease this web to native transition. This should give the developer some additional options when styling their views (especially simple presentational ones): <GridLayout ...>
<single-child-component row="1"></single-child-component> <!-- works -->
</GridLayout> <GridLayout ...>
<StackLayout row="1">
<multi-children-component></multi-children-component> <!-- works -->
<StackLayout>
</GridLayout> <GridLayout ...>
<GridLayout row="1" multi-children-component></GridLayout> <!-- directive-like component for even more optimization -->
</GridLayout> |
I also lean towards option 3. It will improve the usability a lot. |
It would be fairly easy to "proxy" the layout properties directly to top-view of the component, if there is just one. For instance: <Label text="First Text"></Label>
<Label text="Second Text"></Label> Should it be like with Or should we throw an error like it is done for I also think it is important, that we don't just focus on the |
A small Proof-of-Concept (please don't use this in production): /**
* Set layout property to the ProxyViewContainers child. Assumes there is only one child.
*/
ProxyViewContainer.prototype['_updateLayoutProperty'] = function(this: ProxyViewContainer, propName: string, value: any, attempt = 0) {
let first = true;
const customPropName = `_custom_${propName}`;
const timeoutPropName = `${customPropName}_timeout`;
if (this[timeoutPropName]) {
clearTimeout(this[timeoutPropName]);
}
delete this[timeoutPropName];
this[customPropName] = value;
this.eachChild((v) => {
if (first) {
v[propName] = value;
}
first = false;
return true;
});
if (first) {
// Children not added yet, delay for 1ms and retry.
if (attempt > 5) {
return;
}
this[timeoutPropName] = setTimeout(() => this['_updateLayoutProperty'](propName, this[customPropName], attempt + 1), 1);
}
};
ProxyViewContainer.prototype['_getLayoutProperty'] = function(this: ProxyViewContainer, propName: string) {
let first = true;
let value: any;
this.eachChild((v) => {
if (first) {
value = v[propName];
}
first = false;
return true;
});
if (first) {
return this[`_custom_${propName}`];
}
return value;
};
// Override ProxyViewContainer's layout properties, and set the value on the child view.
const layoutProperties = [
// AbsoluteLayout
'left',
'top',
// DockLayout
'dock',
// FlexLayout
'flexDirection',
'flexWrap',
'justifyContent',
'alignItems',
'alignContent',
'order',
'flexGrow',
'flexShrink',
'flexWrapBefore',
'alignSelf',
'flexFlow',
'flex',
// GridLayout
'column',
'columnSpan',
'col',
'colSpan',
'row',
'rowSpan',
];
for (const propName of layoutProperties) {
Object.defineProperty(ProxyViewContainer.prototype, propName, {
configurable: true,
set(this: ProxyViewContainer, value: any) {
this['_updateLayoutProperty'](propName, value);
},
get() {
return this['_getLayoutProperty'](propName);
},
});
} |
I've started working on it here: https://github.com/m-abs/NativeScript/tree/feat/proxy-layout |
Hey @m-abs
☝️ Of course this is just my opinion and I would love to hear others too. One thing that is probably a good idea is to check if any of those properties is already set on the child elements and don't override it in this case. This will allow you to be able to explicitly set properties on the children. This is something that some people already do and it's good to be back-compatible. |
I decided to apply them to all children and log it as an error, if there is more than one child.
Good idea. I'm also looking into how to give a warning, if styling is applied to the |
Sorry it took so long, but now I've created the PR on for NativeScript core. |
Hey everyone,
when working with own components in a GridLayout I noticed that properties like row, col and colSpan are not working on the custom component (aswell as class, flexGrow, alignSelf, etc.). I mean, it makes sense since those properties are not defined on my custom component.
For instance, simple stuff like this would render both the Label and my-cool-component in the first row of the GridLayout, since the layout defaults row and column back to 0.
How can I make these properties work with own components though?
export class MyCoolComponent extends ViewBase
) does not make these properties work for the GridLayout.export class MyCoolComponent { @Input() col: number; @Input() row: number; }
) for the GridLayout.Is there any way I can make my component defining row and col with the outer GridLayout using these properties (same goes for all the other propreties the components included in NativeScript have)? Did I somehow misunderstand what components are there for? Is there a piece of documentation which i just didnt see and points to a solution of my problem?
The text was updated successfully, but these errors were encountered: