Skip to content
StefansArya edited this page Apr 30, 2020 · 5 revisions

Initialize/Define Model

Let's start with this simple element

<sf-m name="something">
    <span>{{ text }}</span>
</sf-m>
sf.model.for('something', function(self, other){
    // `text` on the DOM element will be filled with `My Name`
    self.text = 'My Name';

    // Run something after this model was loaded on the DOM
    self.init = function(){
        // self.$el('.selector');
    }
});

sf.model.for('another-thing', function(self, other){
    self.stuff = 'Hello world';
});

Binded text node shouldn't be replaced with another one because it will remove the binding feature.

Model element collection

After any element with <sf-m> tag was inserted into DOM, the related model will have $el property that contains collection of <sf-m> elements. This also applies when using component (But only contain one element).

sf.model.for('something', function(self){
    self.init = function(){
        self.$el('#element').html("Route was finished");
        // self.$el.find('#element').html("Route was finished");
    }
});

// Or with extend class, component also have this feature
class MyClass{
    // will be called when model initialized
    // and before calling `self.init`
    static construct(){
        // this.abc === 1
        this.hello = 2;
    }

    method(){ console.log("1") }
}

sf.model('something', {extend: MyClass}, function(self){
    self.abc = 1;
    self.init = function(){
        // self.hello === 2
        // self.method(); (console output -> 1 and 2)
    }

    self.method = function(){
        this.super(/* args */); // can also call parent method
        console.log("2");
    }
});

Model & Template feature

Get data from current model scope {{ model.variable }}

Conditional template

{{@if model.view === true :
        {[ <html_content> ]}
    @elseif model.view === undefined:
        model.data.splice(0);
    @else:
        {[ <html_content> ]}
}}

As a replacement for <script> tag. You can also output html by wrap it inside '{[ ... ]}'. {{@exec javascript stuff}} All model variable or function can be referenced to the execution scope.

Make sure you write in ES5 because browser doesn't use transpiler.

<sf-m name="something">
    <span>{{@exec
            for(var i = 0; i < 5; i++){
                {[ <label>i -> {{ i }}</label> ]}
            }

            myAlert("hello");
            {[ <br> ]}

            // Below will displaying the data without escaping the html
            // Always make sure you have secured the output
            // And maybe escaping HTML tags
            @return number.join(',');
        }}</span>
</sf-m>
sf.model.for('something', function(self, other){
    self.number = [1, 2, 3, 4, 5];
});

And the HTML output content will be escaped like below

<sf-m name="something">
    <span>
        <label>i -&gt; 0</label>
        <label>i -&gt; 1</label>
        <label>i -&gt; 2</label>
        <label>i -&gt; 3</label>
        <label>i -&gt; 4</label>
        <br> 1,2,3,4,5
    </span>
</sf-m>

Any {[ inner {{ property }} ]} will not bind into the model, as an alternative you must define a variable outer of the {[ template ]}.

<sf-m name="something">
    <span>
        {{@exec
            var bindMe = prefix;
            for(var i = 0; i < 5; i++){
                {[ <label>i -> {{ bindMe }}</label> ]}
            }
        }}
    </span>
</sf-m>

If you want the framework to parse any custom element (element tag with dashes -) you need to add sf-parse to the element attribute.

Bind input with model

To enable two-way data binding with input element, you need to set model property in sf-bound attribute. When you need to obtain multiple value for checkbox or select, your model property should be an Array type. But if you like to check if the checkbox is checked or not, you need to set the model property as Boolean type. Using String data type will only return the last selected data.

<!-- data on the model will be updated if input detected and vice versa -->
<input sf-bind="myInput" type="text" />
<textarea sf-bind="myText" type="text"></textarea>
<input sf-bind="myFiles" type="file" />
<input sf-bind="myRadio" type="radio" value="radio1" />
<input sf-bind="myRadio" type="radio" value="radio2" />

<!-- You can also set property in `name` attribute -->
<input name="myCheckbox" type="checkbox" value="check1" sf-bind />
<input name="myCheckbox" type="checkbox" value="check2" sf-bind />

<select sf-bind="selectInput" typedata="number">
     <option value="1">Select 1</option>
     <option value="2">Select 2</option>
     <option value="3">Select 3</option>
</select>

<select sf-bind="selectInput" multiple>
     <option value="{{ x.val }}" sf-repeat-this="x in selectData">
        {{ x.text }}
     </option>
</select>

To enable one-way data binding with input element, you need to define model property in sf-into attribute.

<!-- 'myInput' on the model will be updated if input detected -->
<!-- (View -> Model) -->
<input sf-into="myInput" type="text" typedata="number"/>

<!-- input will be updated if 'myInput' on the model was changed -->
<!-- (Model -> Input) -->
<input value="{{myInput}}" type="text" />

<!-- You can also add prefixed value -->
<input value="It's {{myInput}}" type="text" />

<!-- When binding to `class` attribute you need to use `:class` -->
<span :class="icon-{{myInput || 'nothing'}}" style="font-size: {{myInput}}" />
// Model for the above examples
sf.model.for('example', function(self){
    // Any input on element will update this content and the binded view
    self.myInput = 0;

    // Listen any changes before new value assigned to `myInput`
    // Useful if you want to process any changes
    // Returned value will assigned as new value
    self.on$myInput = function(oldValue, newValue){}

    // Listen changes (Model -> View)
    // Useful to avoid execution when new value was assigned from the view
    // Returned value will assigned as new value
    self.out$myInput = function(oldValue, newValue){}

    // Listen changes (Model -> View)
    // This can also being triggered when (View -> [Model -> View])
    // Returned value will only change element content
    self.m2v$myInput = function(oldValue, newValue){
            return formatCurrency(newValue);
    }

    // Listen changes (View -> Model)
    // This will triggered on input/change event
    // Returned value will assigned as new value
    self.v2m$myInput = function(oldValue, newValue){
        console.log("Value will be reverted in 3s to oldValue");
        setTimeout(function(){
            self.myInput = oldValue;
        }, 3000);
    }
});
Clone this wiki locally