Dramatically simplify your React code

Actually, you could - there’s no dependencies in that code other than on Material-UI.

But will create such a project regardless.

This may take a while - it seems I’m running into some server issues on the MDG side because now it’s stuck at “Extracting npm-mongo@1.5.50” while creating the project.

Here it is:

https://github.com/Rhywden/meteor-viewmodel-test

While the title attribute is indeed set to the proper value, it does throw the error I listed above. I also didn’t test what happens if you change the binding’s value.

Edit: Just changed it so that the title should change in response to a button click (updated the repo thusly). It doesn’t. Also, if I then try to bind the same value to another (working) DOM element inside the Dialog, like this:

<Dialog b="title: titleTest" open={true}>
   <p b="text: titleTest" />
</Dialog>

both will show the initial value. But both also won’t be updated upon changing the bound value. If I change it to this, however, it works (thus no typos on my end, I dare say):

<Dialog title="unbound title" open={true}>
   <p b="text: titleTest" />
</Dialog>

Update both viewmodel-react and viewmodel-react-plugin to 0.7.9. You’ll probably have to do a meteor reset too.

1 Like

Great work! And it is indeed working!

I almost feel bad for telling you that I’ve already found the next issue: TextFields.

If I bound the value of a TextField like this:

<TextField b="value: textFieldValue" />

or like this:

<TextField value={this.textFieldValue()} />

then in 0.7.8 entering text into the textfield simply would not be possible (looked like a one-way binding, i.e. any updates to the underlying <input /> would immediately be reset).

With 0.7.9 this issue does not exist anymore. Because the binding only works once (upon setup) - entering anything into the TextField does not update the bound values. If I’m using a combination like this:

<TextField b="value: textFieldValue" />
<input b="value: textFieldValue" />
<p b="text: textFieldValue" />

then all three will have the initial value. Updating the TextField will not change the other two, updating the Input will change the paragraph but not the TextField.

If you want to have a look, I already updated my repository thusly. In the meantime, I can work around this by using the onChange handler of TextField.

1 Like

Can’t help you there because TextBox is a 3rd party control. value binding is for input elements, not components. Can’t you just style an input with some classes or does material ui forces you to use their own custom controls?

Just to be sure, is ViewModel production-ready yet?

It certainly does not force me to do anything. But if I want to use the styling and the additions to the bog-standard input fields then I kind of have to use it.

That’s kind of the point of MaterialUI.

It also takes away a bit of the luster of ViewModel if you can’t use such 3rd party components, to be honest.

Yes. Some have started using it to add new components to existing apps. I’m not 100% positive but I’m pretty sure some have even started green field projects with viewmodel-react.

2 Likes

With any other framework you can just add a class or two to your elements. e.g. With Semantic-UI you do <button class="ui button">ClickMe<button>.

I think you misunderstand the nature of libraries like material-ui which take some input and then outputs some HTML to fit the custom needs of the library. It’s like asking “Why doesn’t the value binding work with X control created with Y javascript library?” A: “Because I can’t anticipate what Y library is going to do under the hood or the structure of X when it’s rendered.”

This isn’t a problem unique to ViewModel. To use Material UI controls someone has to go through the trouble of replicating each and every element in the framework before it can be used in another JS framework. For example, there’s a Material-UI library for React, another for Angular 1, a separate one for Angular 2, Riot, Blaze, etc.

On the other hand a library like http://materializecss.com/ doesn’t require custom controls and thus can be used with any JS framework. It works like Bootstrap, SemanticUI, Foundation, etc.

At risk of being blunt, the way I see it you have 3 options:

  1. Use materializecss.
  2. Don’t use bindings other than if & repeat on material-ui’s custom controls. You can continue using all bindings on regular elements though.
  3. Don’t use ViewModel.

I’d go with number 1 but then I’m biased…

If I’m not allowed to use 3rd party components like this, then I’m not sure what’s the point of React.

Then again, I’m probably spoiled by C# and XAML where something like this is not a problem at all: You have a standard way of defining custom controls and if you don’t adhere to it, the control simply does not work.

Let’s use a WPF analogy. You have the standard controls (html elements), you also have a library that can interact with any of the standard controls (that library is ViewModel), then you have a library that wraps the standard controls and creates its own custom things (that library is any of the material ui libraries out there).

The library that interacts with the standard controls isn’t expected to work with the one that creates the custom ones. To interact with a custom control you first need to know how it works and code specifically for it.

Imagine a library that works with the standard input box and then someone asking “But why doesn’t it work with DevExpress’ fancy box?” A: “Because DevExpress’ fancy box works differently than the standard input box.”

ViewModel works with the standard controls (html elements) so it works out of the box with other frameworks that use standard html elements (React, materializecss, Semantic, Bootstrap, Foundation, etc.) But it doesn’t work with custom components for reasons already stated.

I know that. I still don’t like it.

Again, C# and XAML where this simply is not a problem.

Then again, this is most likely a massive failure on the React side for not properly demanding a standard interface, instead relying once again on monkeypatching all the things.

They have the same “problem”…

1 Like

No, XAML has a standard way of defining controls and their attributes. If you adhere to that way then you can bind (be that two-way, one-way or once) to any of the exposed attributes you like.

I know that because I’ve written such controls myself and I also used several 3rd party controls (commercial and free ones)

I’m also not sold on SemanticUI, as an example, because they’re patching stuff in after you render the page. Something that’s not compatible with the React way of doing things.

That’s the whole point. You have to know how the custom library operates in order to work with it. You can’t assume it has the same attributes as a standard element. The whole reason to create a custom control is so it’s different than a standard one.

Edit: The same way you can interact with any React custom component (like one of material-ui), as long as you know what attributes they expose.

Yes, but the gist of it is this:

If a XAML <TextBlock /> exposes the content attribute (which is bindable) and my custom XAML <MyCustomTextBlock /> also exposes the content attribute - then I know this attribute will also be bindable. That’s the guarantee behind the way a control is created.

However, the HTML <input /> tag exposes the value attribute which is bindable with your model. MaterialUI’s <TextField /> control also exposes the value attribute. But it’s not bindable for some reason.
To add to the confusion, other components like MaterialUI’s <Dialog /> have non-standard attributes like title which are bindable again.

That’s where my problem comes from - why is one attribute from a control from MaterialUI bindable, and why is it suddenly not possible with another? I mean, you were obviously able to change your code in such a way that title was bindable. What exactly stands in the way of value?

I’ve been using VM for Blaze for quite a while now and I just gave the react version a try. If you are used to VM for blaze with ecma2015 and vanilla html you should feel right at home with VM and react.

Well done, Manuel.

2 Likes

Hi Manuel! I know that ViewModel is for controlling the View part of MVC architecture, not the Model one. But still, would you know of a place where I could find an example of ViewModel used with Meteor and React in which data is actually retrieved and saved to MongoDB?

I’m currently using Blaze+VIewModel, but I like your approach with the React+ViewModel combination. However, I don’t know where to start to begin using it in my apps, with database usage… Thanks!

It’s pretty much the same process as with Blaze. Here’s a todo-list example:

lib/collections.js

Todos = new Mongo.Collection("todos"); 

server/publications.js

Meteor.publish("todos", function () {
  return Todos.find();
});

imports/App.js

App({
  todosSub: Meteor.subscribe('todos'),
  destroyed() {
    this.todosSub().stop();
  },
  todos() {
    return Todos.find();
  },
  newTodo: ViewModel.property.string.notBlank,
  addNewTodo() {
    if (this.newTodo.valid()) {
      Todos.insert({ name: this.newTodo() });
      this.newTodo.reset();
    }
  },
  render() {
    <div>
      <input b="value: newTodo, enter: addNewTodo" />
      <hr />
      <TodoItem b="repeat: todos" />
    </div>
  }
})

imports/TodoItem/TodoItem.js

TodoItem({
  _id: '',
  name:'',
  delete() {
    Todos.remove(this._id());
  },
  render() {
    <div>
        <label b="text: name" />
        <button b="click: delete">Delete</button>
    </div>
  }
})

I don’t know about you but that code makes me smile :slight_smile:

2 Likes