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

rv-show and rv-each #366

Closed
smasala opened this issue Sep 8, 2014 · 12 comments
Closed

rv-show and rv-each #366

smasala opened this issue Sep 8, 2014 · 12 comments

Comments

@smasala
Copy link

smasala commented Sep 8, 2014

It would be good if the current iterated item is passed to the rv-show call, so that you can determine whether to show the item within a controller function. However, no arguments are passed.

Example:

    <li rv-show="controller.showMe" rv-each-todo="data.myTodos">
        {todo.text}
    </li>

Controller:

    showMe: function(todo){
        //do something
        return true || false;
    }
@smasala
Copy link
Author

smasala commented Sep 8, 2014

Something similar to the following perhaps? :

Binding.prototype.set = function(value) {
      var _ref1;
      value = value instanceof Function && !this.binder["function"] ? this.formattedValue(value.call(this.model, this)) : this.formattedValue(value);
      return (_ref1 = this.binder.routine) != null ? _ref1.call(this, this.el, value) : void 0;
    };

Where this is passed to the function value.call(this.model, this) call so that it can be used to grab the actual element, models and other data.

@jhnns
Copy link
Contributor

jhnns commented Sep 8, 2014

I think this is already possible with the index-property which was introduced with this pull-request.

<li rv-show="initItem" rv-each-todo="data.myTodos">
    {todo.text}
</li>

(wrong example, see below)

initItem: function (event, models) {
    controller.showMe(models.data.myTodos[models.index]);
}

@smasala
Copy link
Author

smasala commented Sep 8, 2014

Thanks for your reply.
Ah I see, initItem is defined in the configuration when binding the data to the view. Hmm. Then if you wanted the same feature elsewhere it would require a new configuration every time.

@jhnns
Copy link
Contributor

jhnns commented Sep 8, 2014

Oops, sorry, my example is wrong. It should not be passed to rv-show. In fact, I'm using this pattern every time I need to construct or configure something on a single item:

<li rv-constructor="initItem" rv-each-todo="data.myTodos">
    {todo.text}
</li>

rv-constructor doesn't even need to be a registered binder. Rivets default behavior for unknown binders is just to call the function. This way I have a sort of constructor which is called for each item in the list. Within this constructor I'm free to initialize any variables.

@smasala
Copy link
Author

smasala commented Sep 8, 2014

But what if I want to pass the item to my controller, do some sort of calculation or procedure and then show the item depending that result. That would make sense to have it called from the rv-show attribute because then I could simply return a true or false

@jhnns
Copy link
Contributor

jhnns commented Sep 8, 2014

When using data-binding imho you should not run procedures when reading values. Usually you should run procedures when something changes. Reading values does in fact happen very often and thus has an impact on performance. The only exception to this rule are formatters which are necessary to bring the raw data in a presentable state.

@smasala
Copy link
Author

smasala commented Sep 8, 2014

Yes a complicated procedure is perhaps not the best example for this, due to as you say - performance. But the ability to evaluate the item in question more thoroughly would/could be very handy. For example you may want to filter out and only show a certain todo on a certain day if the todo text says "hello" for a crude example. Such logic would be great client side in a controller. It's a bit like like a jQuery event that has the event and element in question passed as an argument. What the developer does with that data and it's performance is no longer a framework problem but that of the developer.

@jhnns
Copy link
Contributor

jhnns commented Sep 9, 2014

But I'd use a view model for that case. For example:

var todos = [
    { label: "Clean kitchen", done: false },
    { label: "Clean bathroom", done: false }
];

Then I'd pass them to a function which creates view models

// createViewModels() is now applying all the logic
var viewTodos = createViewModels(todos);

viewTodos now looks like

[
    { label: "Clean kitchen", done: false, visible: false },
    { label: "Clean bathroom", done: false, visible: true }
];

And now let's pass our view model to rivets

rivets(element, viewTodos);

And if anything changes (for example the user adds a filter), you just need to manipulate your view models. This way your logic isn't executed when reading the values.

@smasala
Copy link
Author

smasala commented Sep 9, 2014

Yea that could be done too. But I don't know, it feels kind of messy of somehow :)

What if, you get a json string from the DB for a form for instance, show the form, allow the user to manipulate the data and send the json object back to the server as it is. The server simply takes the json and saves it to the database, with you example there would be extra values in the object that are purely for the frontend. It would require you to remove them again before sending the data. Plus you would need to unbind the data when manipulating it before submit in order to stop the view updating.

It just seems a bit like a hack, placing the data in the model and then removing it etc. Also, what if the rv-show is dependent on something dynamic. Your example is static in that it is calculated at the beginning, before view creation.

@jhnns
Copy link
Contributor

jhnns commented Sep 10, 2014

If you don't want to mess with the model itself you can also namespace it:

var todoItem = {
    visible: false,
    data: {
        label: "Clean kitchen",
        done: false
    }
}

In my experience you'll always need a separate view model in any advanced app. It's also a widely accepted software pattern, so this doesn't feel hacky to me.

@jhnns
Copy link
Contributor

jhnns commented Sep 10, 2014

You can also leverage prototype inheritance:

viewModel = Object.create(model);
viewModel.visible = false;

console.log(viewModel.label); // "Clean kitchen"
console.log(viewModel.visible); // false
console.log(model.visible); // undefined

You'll need to save the changes back to the model before sending it as JSON to the server:

Object.keys(model).forEach(function (key) {
    model[key] = viewModel[key];
});

@smasala
Copy link
Author

smasala commented Sep 25, 2014

Thanks for your input jhnns, I get where you are coming from... but it just seems like an overkill solution for me :)

It would be just be much easier to pass to the object as an argument when a controller function is called :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants