How to bind a click event on “v-html” in Vue - events

I have a simple example in jsfiddle, as described in the example, I want to insert the element via v-html and then bind the event in the insert element. In addition to adding id operation dom this way, is there a better way?
https://jsfiddle.net/limingyang/bnLmx1en/1/
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<div id="app">
<div v-html="link"></div>
</div>
var app = new Vue({
el: '#app',
data: {
link: 'click me'
}
})

You can add a ref on your div, and operate with its children elements like you do in regular JavaScript. For example, you can set an event listener for a link inside mounted hook:
var app = new Vue({
el: '#app',
data: {
link: 'click me'
},
mounted() {
this.$refs['mydiv'].firstChild.addEventListener('click', function(event) {
event.preventDefault();
console.log('clicked: ', event.target);
})
}
})
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<div id="app">
<div v-html="link" ref="mydiv"></div> <!-- notice 'ref' -->
</div>

For people having problem with #tony19 solution:
If your v-html is dynamically updated and you want to do something on the element you have to use
Vue.nextTick(() => {});
Because you have to wait for the DOM to create the element before accessing its child nodes.

Related

Vuejs js for multiple pages, not for a single page application

I need to build an application using laravel 5.3 and vuejs 2, because I need to use two-way binding rather than use jquery.
I need to set up the views with blade templates. Then, I need to use vuejs in each page as mentioned below.
resources/asserts/js/components/List.vue
<script>
const panel = new Vue({
el: '#list-panel',
name: 'list',
data: {
message: 'Test message'
},
methods: {
setMessage: function(){
this.message = 'New message';
}
}
})
</script>
resources/asserts/views/post/index.blade.php
<div id="panel" class="panel panel-default">
<div class="panel-heading">Posts</div>
<div class="panel-body">
<p>{{ message }}</p>
<button v-on:click="setMessage">SET</button>
</div>
</div>
There is Add.vue to create.blade.php etc...
In Add.vue el: '#add-panel'
This is my app.js. I already commented default code like follows.
Vue.component('list', require('./components/List.vue'));
Vue.component('add', require('./components/Add.vue'));
// const app = new Vue({
// el: '#app'
// });
I hardly checked most of documentations and tutorials. But they use a single js file. They use components for small elements with template, not only js.
Is it possible to use vuejs this way? Do I need to use app.js. What is the best way to do this?
If you want to sprinkle a bit of vuejs within your blade files you have basically two options:
Option #1
Declare global Vue components
Example
// in laravel built in app.js file
Vue.component('foo', require('./components/Foo.vue'));
Vue.component('bar', require('./components/Bar.vue'));
const app = new Vue({
el: '#app'
});
create a main layout file where the root div has an id of #app
// layout.blade.php
<html>
<header></header>
<body>
<div id="app">
#yield('content')
</div>
</body>
</html>
Finally in your views:
//some-view.blade.php
#extends('layout')
#section('content')
<foo :prop="{{ $someVarFromController }}"></foo>
#endsection
Option #2
This is what I am currently using, and gives me more flexibility actually
// in laravel built in app.js file
const app = new Vue({
el: '#app',
components: {
Foo: require('./components/Foo.vue'),
Bar: require('./components/Bar.vue')
}
});
In the layout file you will be using vuejs dynamic components
<html>
<header></header>
<body>
<div id="app">
#if (isset($component))
<component :is={{ $component }} inline-template>
#endif
#yield('content')
#if (isset($component))
</component>
#endif
</div>
</body>
</html>
In your views:
//some-view.blade.php
#extends('layout', ['component' => 'foo'])
#section('content')
// all the vue stuff available in blade
// don't forget to use the # symbol every time you don't want blade to parse the expression.
// Example: #{{ some.vue.propertie }}
#endsection
And finally you can create the vue components like you always would
// resources/assets/js/components/foo.vue
<script>
export default {
// the component
}
</script>
Create your 'app' for every page in seperate JS files. Good practice would be using the same name as page name to get it clear where it belongs.
Name you main div the same as the file (fileName.php + assets/js/fileName.js).
Use #fileName' as yourel`
in blade use #{{ vue expressions }} to let Blade skip this and allow VueJS handle that.
Done. Good luck!

Updating a polymer element property with data from API call

I'm trying to update a property in a polymer element with data from an ajax api call. I have something similar working elsewhere in the app where users are able to add packages dynamically.
Anyone know what I'm doing wrong here?
<link rel="import" href="../bower_components/polymer/polymer.html">
<link rel="import" href="address-input.html">
<link rel="import" href="package-list.html">
<link rel="import" href="../bower_components/iron-ajax/iron-ajax.html">
<dom-module id="step-one">
<style>
</style>
<template>
<section id="addresses">
<div class="container">
<div class="row">
<h5>Addresses</h5>
<address-input></address-input>
</div>
</div>
</section>
<section id="packages">
<div class="container">
<div class="row">
<h5>Packages</h5>
<package-list></package-list>
</div>
</div>
</section>
<section id="submit-shipping-info">
<div class="container">
<div class="row">
<a class="waves-effect waves-light btn col s12 m12 l12" id="submit" on-click="submitInfo">Submit</a>
<template is="dom-repeat" items="{{options}}">
<p>{{item.rates}}</p>
</template>
</div>
</div>
</section>
</template>
</dom-module>
<script>
Polymer ({
is: 'step-one',
properties: {
options: {
type: Object,
notify: true,
value: []
}
},
submitInfo: function(e) {
e.preventDefault();
//add dimensions of all packages to the dimensions array
var dimensions=[];
$('#packages .package-card').each(function(){
var weight= $(this).find('.weight').val();
var length= $(this).find('.length').val();
var height= $(this).find('.height').val();
var width= $(this).find('.width').val();
var dimension={width:width,length:length,height:height,weight:weight};
dimensions.push(dimension);
});
//capture address data
var from = $('#fromAddress').val();
var to = $('#toAddress').val();
//URL that processes getting a URL
var getQuoteURL = '../v2/API/get_rates.php';
var stuff = [];
jQuery.ajax({
type: "POST",
dataType: "json",
cache: false,
url: getQuoteURL,
data:{
from:from,
to:to,
dimension:dimensions
}
}).done(function(data){
$.each(data['rates'], function(i, rate ) {
stuff.push({carrier:rate.carrier});
return stuff;
});
//show step two when ajax call completes
$('.step-two').removeClass('hide').addClass('show');
console.log(stuff);//I can see all objects I need to pass to the 'options' property
return stuff;
});
this.push('options',stuff);//doesn't seem to update the 'options' property with these as a value
}
});
</script>
I'm able to console.log the array i'm trying to use, but when I try to push it to the 'options' property, it won't update.
Consider using Polymer built in methods instead of jQuery.
1. A button to submit a request.
<paper-button on-click="handleClick">Send a package</paper-button>
2. AJAX requests using <iron-ajax> element!
<iron-ajax id="SendPkg"
url="my/api/url"
method="POST"
headers='{"Content-Type": "application/json"}'
body={{packageDetails}}
on-response="handleResponse">
</iron-ajax>
3. Handle the on-click event,
On click, select <iron-ajax> by ID and call <iron-ajax>'s generateRequest()
Use either data binding or Polymer's DOM API to get the package's width, height ...etc
handleClick: function() {
this.packageDetails = {"width": this.pkgWidth, "height": this.pkgHeight };
this.$.SendPkg.generateRequest();
},
4. Handle the response
handleResponse: function() {
//Push data to options...
},
return stuff;
});
this.push('options',stuff);//doesn't seem to update the 'options' property with these as a value
should be
return stuff;
this.push('options',stuff);//doesn't seem to update the 'options' property with these as a value
)};
otherwise
this.push('options',stuff);
is executed before data has arrived
The solution ended up being to put this into a variable:
var self = this;
then in the ajax .done() replace the value of the object with the new object from the ajax call.
self.options = stuff;
I guess you have to put "this" into a variable before you can overwrite it's values. Then the other issue was that I was trying to use .push() to add to it, but really all I needed to do was replace it. (Using self.push('options',stuff); didn't seem to work as far as adding to an object)

ReactJS updating coomponents and passing array as argument?

I'm new to ReactJS and I fell I'm missing some fundamental information.
I am working on simple TODO list, where you click on <li> and it gets transfered to Finished section.
http://jsbin.com/gadavifayo/1/edit?html,js,output
I have 2 arrays that contain list of tasks, when you click on one task <li> it is removed from array and transferred to other array. After that clicked <ul> is updated but not the one where task went.
When using it you may notice that <ul> is updated only when clicked.
How can I update both <ul> when clicking on only one?
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Testing</title>
<link rel="stylesheet" href="bootstrap.css">
</head>
<body>
<div id="react-app"></div>
<script src="https://fb.me/react-0.14.3.js"></script>
<script src="https://fb.me/react-dom-0.14.3.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>
<script type="text/babel">
/*
* Components
*/
var pendingItems = [
'Clean the room',
'Get milf',
'Sellout and stuff'
];
var finishedItems = [
'Clean the room',
];
var TodoList = React.createClass({
getInitialState: function() {
return { items: this.props.list };
},
handleClick: function(i) {
console.log('You clicked: ' + i + ':' + this.props.listString);
if (this.props.listString == "pendingItems") {
var removed = this.state.items.splice(i, 1);
finishedItems.push(removed);
};
if (this.props.listString == "finishedItems") {
var removed = this.state.items.splice(i, 1);
pendingItems.push(removed);
};
this.forceUpdate()
},
render: function() {
return (
<ul>
{this.state.items.map(function(item, i) {
return (
<li onClick={this.handleClick.bind(this, i)} key={i}>{this.state.items[i]}</li>
);
}, this)}
</ul>
);
},
});
var Layout = React.createClass({
render: function (){
return (
<div className='col-xs-12'>
<div className='col-xs-6'>
<TodoList list={pendingItems} listString="pendingItems"/>
</div>
<div className='col-xs-6'>
<TodoList list={finishedItems} listString="finishedItems"/>
</div>
<div className='col-xs-6'></div>
</div>
)
}
});
ReactDOM.render(<Layout />, document.getElementById('react-app'));
</script>
</body>
</html>
You need to use the states. In getInitialState you put your two list, onclick do whatever transformationyou want (you then have for example updated_list1 and updated_list2 and then you set the list like that:
this.setState({ list1: updated_list1, list2: updated_list2 }); in your case this.setState({ pendingItems: pendingItems ... after the .push
the setState function will automatically rerender, no need to call forceupdate.
The second thing important here is that you have to make the two list communication kinda, so my advise would be to put your both ul in the same component (so you can manage the lists in the same component state as mentionned above).
However this is not the only way to go and you may choose the put the states of your two list in the parent component (Layout). In this case you should use this way to go. https://facebook.github.io/react/tips/expose-component-functions.html
In any case you need (if you want to keep it simple and without external model management like backbone or flux pattern) to put lists states in the same component state. (reminder: method 1 => ul in the same componenet so the states too, method 2 => keep state in the parent component)

Targetting dynamically added content using jQuery Mobile

This is my main page:
<head>
<script type="text/javascript">
// Hide e2 and e3
$(document).on('pagechange', function(){
$("#e2").hide();
$("#e3").hide();
});
// Ajax call to insert e1-e3
$(document).on('pageshow', function(){
$("#course").bind("change", function() {
$.ajax({
type: "GET",
url: "includes/db/ajax.php",
data: "course="+$("#course").val(),
success: function(html) {
$("#insert-es").html(html);
$("#page").trigger('create');
}
});
});
});
</script>
</head>
<body>
<div data-role="page" id="insert-quests">
<div id="insert-es"></div>
</div>
</body>
Ajax.php will generate a list of divs based on a database table, for example...
<div id="e1">...</div>
<div id="e2">...</div>
<div id="e3">...</div>
Prior to adding these divs through jQuery and AJAX, I was able to hide 2 and 3 using this script below. (I need to do this because 2 and 3 are only supposed to be shown when the user selects a certain response in 1.)
$(document).on('pageinit', function(){
$("#2").hide();
$("#3").hide();
});
So I have two questions:
I tried pagechange and some other events but no result. How can I use jQuery mobile on content after I have brought it in using AJAX?
The amount of divs is related to the amount of entries in a table of the database. How can I get the amount of entries, so that I can just use a loop there? For example...
for ( var i = 0; i < amount_of_entries ; i++ ) {
$("#e" + i).hide();
}

Events not working when using Mustache with Backbone.js

So I am making a test app using RequireJs, Mustache and Backbone.js. I had some success with rendering the collection of models with the Mustache template. But my Mustache template has a button and when I try to bind click event on the button in the view, the button click doesn't invoke the callback function. I am really stuck, can someone tell me where I am not doing right?
Here is my code:
ItemView.js:
define(['jquery', 'backbone', 'underscore', 'mustache', '../../atm/model/item'], function ($, Backbone, _, Mustache, Item) {
var ItemView = Backbone.View.extend({
initialize: function() {
},
tagName: 'li',
events: {
'click .button': 'showPriceChange'
},
render: function() {
var template = $('#template-atm').html();
var itemObj = this.model.toJSON();
itemObj['cid'] = this.model.cid;
var rendering = Mustache.to_html(template, itemObj);
this.el = rendering;
return this;
},
showPriceChange: function(event) {
alert('Changing...');
$('#' + elemId).empty();
$('#' + elemId).append(document.createTextNode('Changed'));
},
});
return ItemView;
});
atm.html:
<!DOCTYPE html>
<html>
<head>
<title>Elevator</title>
<script data-main="scripts/main" src="scripts/require-jquery.js"></script>
<style type="text/css">
</style>
</head>
<body>
<h1>Vending Machine</h1>
<div id="atm-items">
</div>
<script id="template-atm" type="html/template">
<li>
<p>Item: {{name}}</p>
<label for="price-{{cid}}">Price:</label>
<input id="price-{{cid}}" type="text" value="{{price}}"/>
<button class="button">Change</button>
<p id="status-{{name}}-{{cid}}">- -</p>
</li>
</script>
</body>
</html>
You're replacing the view's el inside render:
render: function() {
//...
this.el = rendering;
//...
}
When you do that, you're losing the jQuery delegate that is attached to this.el, that delegate handler (which Backbone adds) is responsible for the event routing.
Usually, you add things to this.el rather than replacing this.el. If your template looked like this:
<script id="template-atm" type="html/template">
<p>Item: {{name}}</p>
<label for="price-{{cid}}">Price:</label>
<input id="price-{{cid}}" type="text" value="{{price}}"/>
<button class="button">Change</button>
<p id="status-{{name}}-{{cid}}">- -</p>
</script>
then you would this.$el.append(rendering) in your view's render; this would give you an <li> in this.el since you've set your view's tagName to li.
Alternatively, if you really need to keep the <li> in the template, you could use setElement to replace this.el, this.$el, and take care of the event delegation:
this.setElement(rendering);
Presumably you're wrapping all these <li>s in a <ul>, <ol>, or <menu> somewhere else; if you're not then you're producing invalid HTML and the browser might try to correct it for you, the corrections might cause you trouble elsewhere as your HTML structure might not be what your selectors think it is.

Resources