퓨즈[Fusetools] 문서[Docs] 번역

  1. Home
  2. 퓨즈[Fusetools] 문서[Docs] 번역
  3. 퓨즈 기본
  4. 컴포넌트 만들기

컴포넌트 만들기

UX 마크업은 기존의 컴포넌트를 결합하여 더 복잡한 새 컴포넌트를 만들 수 있는 조합 가능한 선언적 언어입니다. UX 마크업 요소의 모든 트리는 ux:Class 속성을 사용하여 쉽게 컴포넌트로 변환될 수 있습니다.

퓨즈는 몇 가지 이유로 앱을 컴포넌트(클래스)로 분리하도록 권장합니다:

  • 좋은 연습. 컴포넌트 지향성은 코드베이스를 깨끗하고 테스트 가능하며 확장 가능하고 유지 보수하기 쉽게 유지합니다.
  • 재사용. 여러 위치에서 UI 및 로직을 재사용 할 수 있도록 컴포넌트를 만드는 것이 유용합니다.
  • 스타일. 프로젝트 전체에서 일관된 룩앤필을 만들기 위해서는 프리미티브를 기반으로 새로운 클래스를 만드는 것이 좋습니다.

서브클래싱(Subclassing)

ux:Class 속성은 요소의 유형이 부모 클래스인 새로운 Uno 클래스를 만듭니다. 즉, 새 클래스는 서브 클래스의 모든 공용 속성, 이벤트 및 동작을 상속받습니다.

UX 마크업 내부에 대한 자세한 내용은 UX 마크업 레퍼런스를 참조하십시오.

내부 로직

컴포넌트에는 컴포넌트의 내부 비즈니스 로직을 관리하는 JavaScript 태그가 포함될 수 있습니다.

종속성 (ux:Dependency)

컴포넌트는 작업 환경에서 특정 객체나 서비스에 액세스해야 하는 경우가 있습니다. 예를 들어, 컴포넌트가 App의 라우터에 액세스해야 할 수 있습니다.

다음과 같이 ux:Dependency 속성을 사용하여 컴포넌트에 종속성을 선언할 수 있습니다:

1
2
3
4
5
6
7
8
9
10
11
12
<Panel ux:Class="MyBackButton" Clicked="{clicked}">
    <Router ux:Dependency="router" />
    <Panel ux:Dependency="panel" />
    <JavaScript>
        function clicked() {
            router.goBack();
        }
    </JavaScript>
    <WhilePressed>
        <Change panel.Opacity="0.5" Duration="0.3" />
    </WhilePressed>
</Panel>

위의 예는 router와 panel의 종속적 관계를 선언합니다. 컴포넌트를 클릭하면 라우터가 .goBack()에 사용됩니다. 컴포넌트를 누른 상태에서 panel이라고 종속된 패널이 반투명으로 희미해집니다.

종속성은 Uno의 생성자 인수와 동일하며 읽기 전용 필드에 저장됩니다. 이것은 객체의 초기화 시에 항상 생성되며 절대로 변경되지 않기 때문에, 우리는 JavaScript나 애니메이터에서 객체를 해당 이름으로 Change와 같이 안전하게 사용할 수 있음을 의미합니다.

종속성이 있는 컴포넌트를 인스턴스화 할 때 각 종속성(즉, 종속성 주입)에 대해 객체를 제공해야 합니다. 그렇지 않으면 컴파일 시간 오류가 생성됩니다.

1
2
3
4
5
<App>
    <Router ux:Name="router" />
    <Panel ux:name="p1" />
    <MyBackButton router="router" panel="p1" />
</App>

컴포넌트는 종속성에 대한 기본값을 설정할 수 없습니다.

종속성 상속하기

하위 클래스를 만들 때 종속성은 전달되지 않습니다. 따라서 서브 클래싱하는 수퍼 클래스에 수동으로 전달해야 합니다.

1
2
3
4
5
6
<Page ux:Class="A">
    <Router ux:Dependency="router" />
</Page>
<A ux:Class="B">
    <Router ux:Dependency="router" ux:Binding="router" />
</A>

속성 (ux:Property)

속성은 종속성과 비슷하지만 뮤터블[mutable] 객체(변경 가능, 애니메이션 가능)이며 컴포넌트는 기본값을 제공할 수 있고 property-bound를 사용하여 속성 바인딩 할 수 있습니다.

1
2
3
4
5
<Panel ux:Class="MyButton" BackgroundColor="#f00">
    <float4 ux:Property="BackgroundColor" />
    <Text>SUBMIT</Text>
    <Rectangle Layer="Background" Color="{ReadProperty this.BackgroundColor}" CornerRadius="10" />
</Panel>

위의 예제에서 컨트롤은 BackgroundColor라는 새 속성을 정의하며 기본값은 루트 노드에 설정된 #f00입니다. 이 색은 둥근 모서리가 있는 배경 사각형의 Color 속성에 속성 바인딩 됩니다.

컴포넌트가 인스턴스화 되면 사용자는 기본 BackgroundColor를 그대로 두거나 새 BackgroundColor를 설정할 수 있습니다.

1
2
<MyButton /> <!-- 기본 색상 유지 -->
<MyButton BackgroundColor="#0f0"/> <!-- 대신 녹색을 사용합니다. -->

애니메이터를 사용하여 속성을 애니메이트 할 수도 있습니다:

1
2
3
4
<MyButton ux:Name="mb1" />
<WhilePressed>
    <Change mb1.BackgroundColor="#00f" Duration="1" />
</WhilePressed>

종속성 대신 속성을 사용하는 단점은 속성이 변경될 수 있기 때문에 속성으로 전달된 객체를 UX 마크업에서 이름으로 참조할 수 없다는 것입니다(예: 애니메이터).

속성은 JavaScript에서 명명된 객체로 직접 사용할 수 없지만 모듈의 루트 범위에서 this 객체에 대한 Observable 속성으로 사용할 수 있습니다.

다음은 JavaScript의 속성 변경에 응답하는 방법의 예입니다:

1
2
3
4
5
6
7
8
9
10
11
12
13
<Panel ux:Class="RgbDisplayer" RGB="#A00">
    <float4 ux:Property="RGB" />
    <JavaScript>
        var Observable = require("FuseJS/Observable");
        var rgbString = this.RGB.map(function(val) {
            return  "R: " + (val[0]*255).toFixed(1) +
            " G: " + (val[1]*255).toFixed(1) +
            " B: " + (val[2]*255).toFixed(1);
        });
    module.exports = {rgbString};
    </JavaScript>
    <Text Value="{rgbString}" />
</Panel>

속성을 통해 Observable 전달하기

Observable은 속성을 사용하여 사용자 정의 컴포넌트로 전달할 수도 있습니다. Observable을 받아들이는 object 속성을 추가할 수 있습니다:

1
2
3
4
5
6
7
8
9
10
11
12
13
<Panel ux:Class="CoolPanel">
    <object ux:Property="ObservableProperty" />
    <JavaScript>
        var passedInObservable = this.ObservableProperty.inner();
    </JavaScript>
</Panel>
<JavaScript>
    var Observable = require("FuseJS/Observable");
    module.exports = {
        valueToPass: Observable("123")
    };
</JavaScript>
<CoolPanel ObservableProperty="{valueToPass}" />

대부분의 경우 속성을 통해 전달된 Observable을 가져올 때 inner()를 사용하려고 합니다. 자바스크립트 값 this.Propertyname은 어떤 Propertyname이 포함하든 Observable이기 때문입니다. Observable을 전달하면 this.Propertyname은 우리가 관측한 관측치와 함께 Observable을 포함하게 됩니다.

속성을 통해 파일에 대한 참조 전달

파일에 대한 참조를 컴포넌트로 전달하려는 경우가 있습니다. 예를 들면 이미지 또는 비디오를 컴포넌트에 포함시킬 때입니다. FileSource 유형의 속성을 만든 다음 이미지 또는 비디오의 File 속성에 속성 바인딩을 적용하여 이 작업을 수행할 수 있습니다. 그런 다음 클래스를 인스턴스화 할 때 이름으로 로컬 파일을 참조할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<App>
    <Panel ux:Class="TestComponent">

        <FileSource ux:Property="ImageFile" />
        <FileSource ux:Property="VideoFile" />

        <Grid Rows="1*,1*">
            <Image File="{ReadProperty ImageFile}" Margin="10"/>
            <Video File="{ReadProperty VideoFile}" AutoPlay="true" IsLooping="true"/>
        </Grid>
    </Panel>

    <TestComponent ImageFile="test.png" VideoFile="testvideo.mp4"/>
</App>

템플릿 (ux:Template)

템플릿을 사용하면 모양에 사용되는 사용자 정의 요소를 가져 와서 컴포넌트의 사용자 정의 가능성을 높일 수 있습니다. 예를 들어, PageIndicator 요소는 PageControl의 각 페이지에 대한 템플릿에서 요소를 생성합니다. 요소는 ux:Template 속성을 템플릿을 식별할 키로 설정하여 템플릿으로 만듭니다. 이 키는 사용할 템플릿을 찾을 때 템플릿을 자식으로 허용하는 요소에 사용됩니다.

템플릿은 Each에 의해 그려집니다. Each는 TemplateSource라는 속성을 가지고 있는데, 이는 Each가 템플릿을 얻는 요소를 지정합니다. 앞에서 언급했듯이 템플릿은 키를 사용하여 자신을 식별합니다. TemplateKey 속성은 Each 템플릿을 사용할 수 있는지 여부를 지정합니다.

다음은 Each가 부모 클래스(CoolRepeater)에서 키를 찾아야하기 때문에 TemplateSource=”this”로 설정합니다.

1
2
3
4
5
<StackPanel ux:Class="CoolRepeater" Background="#FAD">
    <Each TemplateSource="this" TemplateKey="Item" Count="20">
        <Text>Default template</Text>
    </Each>
</StackPanel>

이 템플릿은 20번 반복되는 템플릿을 허용하는 사용자 정의 컴포넌트입니다. 템플릿을 지정하지 않으면 Each 안에 있는 것이 무엇이든 간에 Each가 기본 템플릿으로 대체됩니다. 그런 다음 사용자 정의 컴포넌트를 다음과 같이 사용할 수 있습니다:

1
2
3
<CoolRepeater>
    <Text ux:Template="Item">Hello, world!</Text>
</CoolRepeater>

이벤트 (UserEvent)

컴포넌트가 메시지를 외부로 전달하기를 원하는 많은 경우가 있을 것입니다. 이것을 위해 UserEvent를 사용할 수 있고, UX와 JavaScript 모두에서 이벤트를 발생시키고 처리할 수 있습니다.

특정 이벤트를 발생시킬 수 있음을 나타 내기 위해 컴포넌트 클래스의 루트에 UserEvent를 배치합니다. 우리가 배치하는 곳은 UserEvent가 붙어있는 노드와 그 자식만 그것을 발생시키거나 처리할 수 있기 때문에 중요합니다.

1
2
3
<Panel ux:Class="MyComponent">
    <UserEvent ux:Name="myEvent" />
</Panel>

그러면 myEvent라는 이벤트가 작성됩니다.

참고: 앱의 어디에서든지 발생하거나 처리할 수 있는 UserEvent를 만들려면 다음과 같이 루트 App 노드에 선언하십시오.

1
2
3
4
<App>
    <UserEvent ux:Name="myGlobalEvent" />
    <!-- 나머지 앱 코드를 작성하세요 -->
</App>

이제 RaiseUserEvent를 사용하여 UX에서 이벤트를 발생시킬 수 있습니다.

1
2
3
4
5
6
7
<Panel ux:Class="MyComponent">
    <UserEvent ux:Name="myEvent" />

    <Clicked>
        <RaiseUserEvent EventName="myEvent" />
    </Clicked>
</Panel>

또는 자바스크립트에서 이를 발생시킬 수 있습니다.

1
2
3
4
5
6
7
8
9
<Panel ux:Class="MyComponent">
    <UserEvent ux:Name="myEvent" />

    <JavaScript>
        setTimeout(function() {
            myEvent.raise();
        }, 5000);
    </JavaScript>
</Panel>

컴포넌트를 인스턴스화하면 OnUserEvent 트리거를 사용하여 해당 이벤트에 응답할 수 있습니다.

1
2
3
4
5
<MyComponent>
    <OnUserEvent EventName="myEvent">
        <!-- Actions/animators go here -->
    </OnUserEvent>
</MyComponent>

UserEvent는 현재 범위 외부에서 선언되었지만 이름으로 참조하고 있습니다. EventName이 이벤트의 이름을 참조하므로이 작업을 수행할 수 있습니다. UserEvent의 실제 인스턴스는 런타임에 확인됩니다.

JavaScript로 이벤트를 처리할 수도 있습니다.

1
2
3
4
5
6
7
8
9
10
11
<JavaScript>
    function eventHandler() {
        // 코드를 작성하세요
    }

    module.exports = { eventHandler: eventHandler };
</JavaScript>

<MyComponent>
    <OnUserEvent EventName="myEvent" Handler="{eventHandler}"/>
</MyComponent>

이벤트를 발생시킬 때 인수를 전달할 수 있습니다.

1
2
3
4
myEvent.raise({
    userName: "james",
    isAdmin: false
});

이것은 UX에서 이벤트를 발생시킬 때도 가능합니다.

1
2
3
4
<RaiseUserEvent EventName="myEvent">
    <UserEventArg Name="userName" StringValue="james" />
    <UserEventArg Name="isAdmin" BoolValue="false" />
</RaiseUserEvent>

인수는 이벤트 핸들러로 전달됩니다.

1
2
3
4
5
6
7
8
9
<JavaScript>
    function eventHandler(args) {
        console.log("Username: " + args.userName + ", Is admin: " + args.isAdmin);
    }

    module.exports = { eventHandler: eventHandler };
</JavaScript>

<OnUserEvent EventName="myEvent" Handler="{eventHandler}" />
Was this article helpful to you? Yes No

How can we help?