Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Set Initial value to vue.js model which is directly available in HTML

Tags:

vue.js

vuejs2

I have my code in PHP, I do have some variable suppose

$checked = true;

and I am writing something like this

<input v-model="subscribed" type="checkbox" <?= ($checked ? 'checked' : '') ?>  />

now in my vue js file how do I populate my model with this checked data so

subscribed has value of true or false. (I don't intend to assign value to js directly by PHP code I need to set it inside/in input it self not anywhere else)

please check code snippet.

<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/[email protected]/dist/vue.min.js"></script>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>
<body>
  <div id="app">
     <label>
       <input id='sub' v-model="subscribed" @click="printState" type="checkbox" checked />
       Subscription
     </label>
  </div>
  <script>  
   new Vue({
      el: '#app',
      data: function() {
        return {
          subscribed: false,
          // subscribed: sub.checked
          // ^ this works but i dont want to add everytime id and get its value manually
        }
      },
      created: function() {
        var self = this;
        setTimeout(function() {
          console.log('Checkbox is having its initial value true/checked');
          console.log(self.subscribed);
        }, 1000)
      },
      methods: {
        printState: function(){
           console.log(this.subscribed)
        }
      }
    });
  </script>
</body>

you can see now I have to add model variable in data subscribed but then I also need to provide its value and if I provide its value then it will override its actual value ( so to make it work I am settings it value initially from the DOM element it self {sub.checked sub is dom element fetched by dom.getelembyid... } but I don't like that way as there will be much more elements (text|select|radio) etc ..)

so is there any way (elegant way) that subscribed will pick its value automatically from the input it self.

like image 974
Hardik Satasiya Avatar asked Dec 19 '17 11:12

Hardik Satasiya


3 Answers

This is not in the spirit of Vue, as you should have logic outside the template when feasible, but here's a clever workaround that should work in most situations

<input 
  v-model="foobar"
  :run="!foobar ? foobar = 1 : true"
/>

:run isn't a special attribute, it's completely arbitrary. I use it to define the value of what I'm v-modeling.

In my case, this was useful because I needed to v-model on an index of an array where the index doesn't exist yet. The template is creating the index it v-models on. I do advise against the above workaround and suggest testing throughly if you use it for potential side effects, but it will let you init a value inline in the template.

like image 130
Goose Avatar answered Oct 21 '22 01:10

Goose


Vue really isn't designed to init from the HTML, however, one approach is to use a directive to init the values. This is loosely based around the same idea as Angular's ng-init:

Vue.directive('init', {
  bind: function(el, binding, vnode) {
    vnode.context[binding.arg] = binding.value;
  }
});

That essentially takes the binding argument, which is the name after the colon (see below) and sets it to the given value on the Vue instance, which you can then use as:

<input v-model="subscribed" type="checkbox" v-init:subscribed="<?=($checked) ? 'true' : 'false'?>"  />

A few caveats with that though, firstly you still need to declare your data properties upfront, it won't dynamically inject them, secondly everything get evaluated as JavaScript, so if you want to init with a string it needs to be wrapped in single quotes and finally it won't understand camelCase, so if you want to set camelCase data properties you will need to write a kebab-case to camelCase conversion in the directive.

Here's your updated snippet in a JSFiddle: https://jsfiddle.net/evnctym1/

like image 41
craig_h Avatar answered Oct 21 '22 03:10

craig_h


Here's some possible concepts you might find useful when binding your backend data with Vue.

0. Build a REST API and use it

Preferred approach but sometimes this is not possible. For example, if you don't have full control over the backend code.

1. You could assign global variables

To get your data inside Vue context with PHP/HTML:

<script>
var myDataFromPhp = {
  stringExample: '<?php echo "Hello World!"; ?>',
  objectExample: JSON.parse('<?php echo json_encode(["Hello again" => "Greetings from Mars!"]); ?>'),
}
</script>

In Vue:

data: {
  someString: window.myDataFromPhp && window.myDataFromPhp.stringExample? window.myDataFromPhp.stringExample : '',
  someObject: window.myDataFromPhp && window.myDataFromPhp.objectExample? window.myDataFromPhp.objectExample : {},
},

Maybe just stuff all data in one object at the PHP side to make it prettier. You could also use variable naming that says it's read only and meant to be static: var _OBJECT_EXAMPLE = ...


2. If you don't feel like exposing a global variable inside a <script> tag

When binding form data to Vue, the following has worked for me when initialising Vue app in specific state. This is useful if you just have one little thing you need to bind.

Inside your Vue app's scope:

<input name="someInputData" type="hidden" :value="someInputData = 'Heya!'">

Then declare data in Vue app:

data: {
  someInputData: undefined, // NOTE: This one is initialised in template element input[name="someInputData"]
},

I'm not sure if this has any async side effects. Use with caution and test.


3. Use vanilla JS:

In HTML:

data: {
  someInputData: document.getElementById('my-hidden-input')? document.getElementById('my-hidden-input').value : undefined, 
}

Just make sure there's only unique DOM elements or your data will be all wrong ...

like image 27
sampoh Avatar answered Oct 21 '22 02:10

sampoh