This is a migrated thread and some comments may be shown as answers.

Notifying change with ES5 getter and setter

3 Answers 95 Views
General Discussions
This is a migrated thread and some comments may be shown as answers.
This question is locked. New answers and comments are not allowed.
Richard
Top achievements
Rank 1
Richard asked on 30 Mar 2015, 01:43 AM
I'm experimenting on how I can restructure my NativeScript app using typescript. The first version of the application used the observable object as it is and everything was working fine. I tried  restructuring the code by moving the view model to a different class that wraps around the observable object. The thing is that values are set just fine from the UI but it appears setting the value from code does not change the UI. The idea is to encapsulate everything in the view model and use the generated object like any javascript object without using .get and .set.

MainViewModel.ts
import observableModule = require("data/observable");
import observableArrayModule = require("data/observable-array");
import Utils = require("../lib/Utils");
 
export interface ITask { name: string; }
 
export class ViewModel {
    private observable = new observableModule.Observable();
 
    constructor(json?: any) {
        this.init();
        if (json)
            for (var key in json)
                this.observable.set(key, json[key]);
    }
    private init() {
        this.observable.set('task', '');
        this.observable.set('tasks', new observableArrayModule.ObservableArray());
    }
 
    public textFieldId = Utils.uniqueId("ui");
    public get task(): string {
        return this.observable.get('task');
    }
    public set task(value) {
        this.observable.set('task', value);
    }
    public get tasks(): observableArrayModule.ObservableArray<ITask> {
        return this.observable.get('tasks');
    }
    public set tasks(value) {
        this.observable.set('tasks', value);
    }
    public addTask(task: ITask) {
        this.tasks.push(task);
    }
}


Main.ts
import viewModule = require("ui/core/view");
import textFieldModule = require("ui/text-field");
import pageModule = require("ui/page");
import frame = require("ui/frame");
import model = require("./mainViewModel");
 
var page: pageModule.Page;
var vm = new model.ViewModel();
 
export function onPageLoaded(args) {
    page = args.object;
    page.bindingContext = vm;
    if (frame.topmost().android) {
        frame.topmost().android.actionBar.hide();
    }
}
  
export function add() {
    vm.tasks.push({ name: vm.task });
    vm.task = "";
    var view = viewModule.getViewById<textFieldModule.TextField>(page, vm.textFieldId);
    view.dismissSoftInput();
}

Main.xml
<Page loaded="onPageLoaded">
  <GridLayout rows="auto,*">
    <StackLayout orientation="horizontal" row="0">
      <TextField width="200" text="{{task}}" hint="Enter a task" id="{{textFieldId}}"/>
      <Button text="Add" tap="add"></Button>
    </StackLayout>
 
    <ListView items="{{tasks}}" row="1">
      <ListView.itemTemplate>
        <Label text="{{name}}" />
      </ListView.itemTemplate>
    </ListView>
  </GridLayout>
</Page>


When the add function is called after a button is clicked, the list of items increase but the textbox is not cleared when I set that task property to an empty string.

I did try simply inheriting from observable instead of having a local copy but that caused the application to hang anytime I run it. Very sure I did not implement it well.

What do I do to get the two way binding to work? Am I approaching this wrongly? If so, what is the right way to go around this?




3 Answers, 1 is accepted

Sort by
0
Richard
Top achievements
Rank 1
answered on 30 Mar 2015, 08:40 PM
I exposed the internal observable and used it for the binding and now the ui updates correctly. I guess the question now therefore is how do I make NativeScript create my custom class like an observable that can participate in two way binding?
0
Richard
Top achievements
Rank 1
answered on 31 Mar 2015, 01:18 AM
After looking around the source code (bindable.js), I realized that the event listeners are only registered when the source is an instance of observable. Anything else is ignored.

I changed it from
if (sourceOptionsInstance instanceof observable.Observable) {

to
if (sourceOptionsInstance.addEventListener && sourceOptionsInstance.removeEventListener) {

Is there any reason for the current behavior? Is there another way of getting this to work without change code in the main libraries?
0
Erjan Gavalji
Telerik team
answered on 03 Apr 2015, 08:45 AM
Hi Richard,

How about inheriting the Observable class, e.g.

export class ViewModel extends observableModule.Observable {
...
}

Kind regards,
Erjan Gavalji
Telerik
 

See What's Next in App Development. Register for TelerikNEXT.

 
Tags
General Discussions
Asked by
Richard
Top achievements
Rank 1
Answers by
Richard
Top achievements
Rank 1
Erjan Gavalji
Telerik team
Share this question
or