dgrid (onDemandGrid) loads on first time button click, but error on second time button is clicked - dojo

Thanks to some previous help here, I got the Dojo dgrid to work; and even figured out how to tie it to data from my rest service.
Now I added an input box, a button, and all the logic happens on the button-click. But the second time I click the button, even with the same input value in the input field, I get an error.
ERROR:
TypeError: Cannot read property 'element' of undefined in StoreMixin.js:33
Including the picture so you can see my console.logs
I read this How To reset the OnDemandGrid, but is it necessary to check to see if grid exists and do different logic? Can't I just "new up" a new one each time?
CODE:
<div data-dojo-type="dijit/layout/ContentPane" data-dojo-props='title:"CustomersGrid"'>
<label for="lastnameStartsWith">Lastname Starts With:</label>
<input id="lastnameStartsWith" type="text" name="lastnameStartsWith" value="Wag"
data-dojo-type="dijit/form/TextBox"
data-dojo-props="trim:true, propercase:true" />
<br />
<br />
<button id="queryStudentsButton" data-dojo-type="dijit/form/Button"
data-dojo-type="dijit/form/Button"
data-dojo-props="iconClass:'dijitIconTask'">
<span>Query</span>
<script type='dojo/on' data-dojo-event='click'>
require([
'dstore/RequestMemory',
'dstore/Memory',
'dgrid/OnDemandGrid'
], function (RequestMemory, Memory, OnDemandGrid) {
var url = '../students/' + dojo.byId('lastnameStartsWith').value;
console.log("query students for dataGrid latsnameStartsWith:" + dojo.byId('lastnameStartsWith').value);
require(['dojo/request'], function(request){
request.get(url,
{headers: {"Content-Type": 'application/json',
"username": securityConfig.username,
"password": securityConfig.password}}
)
.then(function(response){
//console.log("string response=" + response);
var respJSON = JSON.parse(response);
var respDataForDGrid = respJSON.recordset;
console.log("got respJSON back, num rows= " + respDataForDGrid.length);
//================================================
// Create an instance of OnDemandGrid referencing the store
console.log("Debug1");
var grid2 = new OnDemandGrid({
collection: new Memory({ data: respDataForDGrid }),
columns: {
student_id: 'ID',
student_firstname: 'First Name',
student_lastname: 'Last Name',
student_city: 'City',
student_state: 'State',
student_zip: 'Zip'
}
}, 'grid2');
console.log("Debug2");
grid2.startup();
console.log("Debug3");
},
function(error){
console.log("Error=" + error);
//dom.byId('studentFeedback').value += response;
});
});
});
</script>
</button>
<h2>My demoGrid - From JSON RestService (Database)</h2>
<div id='grid2'></div>
</div>
Part 2 -
I tried mix of your code and code on this page:
How To reset the OnDemandGrid
if (grid2Registered){
console.log("reuse existing grid");
grid2Registered.set('collection', memStore);
// refresh: clear the grid and re-queries the store for data.
grid2Registered.refresh();
}
else{...
Doc here (https://github.com/SitePen/dgrid/blob/v0.4.3/doc/components/core-components/OnDemandList-and-OnDemandGrid.md) says:
Clears the grid and re-queries the store for data. If
keepScrollPosition is true on either the instance or the options
passed to refresh, an attempt will be made to preserve the current
scroll position. OnDemandList returns a promise from refresh, which
resolves when items in view finish rendering. The promise resolves
with the QueryResults that were rendered.

This one has been tough! Below a working example.
First I switched from declarative to programmatic for the onClick function: declarative scripts are parsed by dojo, and as a consequence you cannot examine them (set break points, etc.) under the debugger (at least I don't know how to do that). So it seems to me good practice to avoid them.
Then, indeed the bug is due to re-instantiating the dgrid with the same id, so that you do need a way to detect that the dgrid already exists. But there is a trick: for dgrids to be properly handled by the dijit system, they need to be mixed in with the dijitRegistry extension. See here for details.
Then you can use registry.byId('grid2') to detect that the dgrid already exists.
Also I had to skip the respDataForDgrid part and used directly respJSON instead (may be due to a difference with your server side(?) - I used a simple text file with a json array on the server side).
<!DOCTYPE HTML><html lang="en">
<head>
<meta charset="utf-8">
<title>Neal Walters stask overflow test</title>
<link rel="stylesheet"
href="dojo-release-1.12.2-src/dijit/themes/claro/claro.css"
media="screen">
<link rel="stylesheet"
href="dojo-release-1.12.2-src/dgrid/css/dgrid.css" media="screen">
</head>
<body class="claro">
<div data-dojo-type="dijit/layout/ContentPane"
data-dojo-props='title:"CustomersGrid"'>
<label for="lastnameStartsWith">Lastname Starts With:</label> <input
id="lastnameStartsWith" type="text" name="lastnameStartsWith"
value="Wag" data-dojo-type="dijit/form/TextBox"
data-dojo-props="trim:true, propercase:true" /> <br /> <br />
<button id="queryStudentsButton" data-dojo-type="dijit/form/Button"
data-dojo-props="iconClass:'dijitIconTask', onClick: myClick">Query</button>
<h2>My demoGrid - From JSON RestService (Database)</h2>
<div id='grid2'></div>
</div>
<script src="dojo-release-1.12.2-src/dojo/dojo.js"
data-dojo-config="async:true"></script>
<script type="text/javascript">
require(["dojo", "dojo/parser", "dojo/domReady!"],
function(dojo, parser){
parser.parse();
});
function myClick(){
var url = 'students/' + dojo.byId('lastnameStartsWith').value, securityConfig = {username: 'john', password: 'Doe'};
console.log("query students for dataGrid latsnameStartsWith:" + dojo.byId('lastnameStartsWith').value);
require(['dojo/_base/declare', 'dojo/request', "dijit/registry", "dstore/RequestMemory", "dstore/Memory", "dgrid/OnDemandGrid", "dgrid/extensions/DijitRegistry"], function(declare, request, registry, RequestMemory, Memory, OnDemandGrid, DijitRegistry){
request.get(url,{})
.then(function(response){
console.log("string response=" + response);
var respJSON = JSON.parse(response);
//var respDataForDGrid = respJSON.recordset;
//console.log("got respJSON back, num rows= " + respDataForDGrid.length);
//================================================
// Create an instance of OnDemandGrid referencing the store
console.log("Debug1");
var theGrid = registry.byId('grid2');
if (theGrid){
theGrid.set('collection', new Memory({data: respJSON}));
}else{
var grid2 = new (declare([OnDemandGrid, DijitRegistry]))({
collection: new Memory({ data: respJSON }),
columns: {
student_id: 'ID',
student_firstname: 'First Name',
student_lastname: 'Last Name',
student_city: 'City',
student_state: 'State',
student_zip: 'Zip'
}
}, 'grid2');
console.log("Debug2");
grid2.startup();
console.log("Debug3");
}
},
function(error){
console.log("Error=" + error);
//dom.byId('studentFeedback').value += response;
});
});
};
</script>
</body>
</html>

Related

binding dynamic img in vuejs does not work for me

I have a dynamic img being pulled from an api using vue.js. For some strange reason, the image won't bind. I have tried both :src. and :attr but neither works. The url does display in the vue console inside of the data but can't get the image to display on the page. any help will go a long way.
<html>
<head>
<style></style>
</head>
<body>
<div class="container">
<div id="editor">
<img v-bind:src="PictureURL" />
</div>
</div>
<script type="text/javascript" src="https://unpkg.com/vue#2.0.3/dist/vue.js"></script>
<script>
new Vue({
el: "#editor",
data: {
PictureUrl: "",
},
created: function() {
this.getCurrentUser();
},
methods: {
getCurrentUser: function() {
var root = 'https://example.com';
var headers = {
accept: "application/json;odata=verbose"
}
var vm = this;
var __REQUESTDIGEST = '';
$.ajax({
url: root + "_api/Properties",
type: 'Get',
headers: headers,
success: function(data) {
vm.PictureUrl = data.d.PictureUrl;
}
})
},
}
})
</script>
</body>
</html>
Change <img v-bind:src="PictureURL" /> to <img v-bind:src="PictureUrl" />, so that you match the data item name. Vue should be giving you an error in the console about this.
https://jsfiddle.net/kch7sfda/
Example here.
You can try to:
1. add v-if to img element
2. rename PictureUrl to pictureUrl (first lowercase letter)

Why v-bind is one-way data binding while v-for can update data from children component in vue.js?

guys. I am reading the book of The Majesty Of Vue.js 2. I am confused with one example in the book.
My question is - why upvote button can modify data of Vue instance which displayed in pre tag while favorite button can not?
It is said that favorite is bound via v-bind directive, which is one way data binding means that children are not able to sync data with parent. But how did story get updated? Two way data binding like v-model?
Here is the code example:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hello Vue</title>
</head>
<body>
<div v-cloak id="app">
<div class="container">
<h1>Let's hear some stories!</h1>
<ul class="list-group">
<story v-for="story in stories" :story="story" :favorite="favorite"></story>
</ul>
<pre>{{ $data }}</pre>
</div>
</div>
</body>
<template id="story-template">
<li class="list-group-item">
{{ story.writer }} said "{{ story.plot }}"
Story upvotes {{ story.upvotes }}.
<button v-show="!story.voted" #click="upvote" class="btn btn-default">Upvote</button>
<button v-show="!isFavorite" #click="setFavorite" class="btn btn-primary">Favorite</button>
</li>
</template>
<script src="../../vue.js"></script>
<script type="text/javascript">
Vue.component('story', {
template: "#story-template",
props: ['story', 'favorite'],
methods: {
upvote: function () {
this.story.upvotes += 1;
this.story.voted = true;
},
setFavorite: function () {
this.favorite = this.story;
}
},
computed: {
isFavorite: function () {
return this.story === this.favorite
}
}
});
window.app = new Vue({
el: '#app',
data: {
stories: [
{
plot: 'My horse is amazing.',
writer: 'Mr. Weebl',
upvotes: 28,
voted: false
},
{
plot: 'Narwhals invented Shish Kebab.',
writer: 'Mr. Weebl',
upvotes: 8,
voted: false
},
{
plot: 'The dark side of the Force is stronger.',
writer: 'Darth Vader',
upvotes: 49,
voted: false
},
{
plot: 'One does not simply walk into Mordor',
writer: 'Boromir',
upvotes: 74,
voted: false
}
],
favorite: {}
}
})
</script>
</html>
This has to do with how objects work in Javascript. When stored in a variable you sir a reference to that object. So when you pass it around, you're actually only passing the reference. Meaning that altering an object (not overwriting!), alters is on all places.
What happens in your example is that you modify the story object. You alter its keys but do not overwrite the object itself. Seeing the app has the same reference to the story object. The changes are shown.
In the case of the favorite however. You get passed the reference of the favorite object. But as soon as you click the favorite button. It swaps the variable to reference the story, but only locally. The app still holds the old reference. This is because you only pass the reference itself and not the parent object.
This is where state managers like Vuex come in to place.

Vuejs 2 How to update v-model by component

I would like to create a component for i-check but I cannot get the v-model data during form submission.
I can do it via normal input element.
Below is an example. Thanks
<!DOCTYPE html><html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<script src="https://unpkg.com/vue"></script>
<title>JS Bin</title>
</head>
<body>
<div id=wrap>
<ichecker id="test" checked v-model="formData.checkbox"></ichecker>
<!--<input type=checkbox checked v-model="formData.checkbox" />-->
<button v-on:click.prevent=submit()>Submit</button>
</div>
</body>
</html>
Vue.component('ichecker', {
props: ['id', 'checked'],
model: {
prop: 'checked',
event: 'change'
},
template: "<input type='checkbox' :id='id' />",
mounted: () => {
// var $el = jQuery(`#${this.id}`);
// $el.iCheck({.....
}
});
new Vue ({
el: '#wrap',
data: {
formData : {
checkbox : ''
}
},
methods: {
submit: function() {
document.body.appendChild(document.createTextNode(this.formData.checkbox?'true ':'false '));
// form submission
}
}
});
https://jsbin.com/jugerigeto/edit?html,js,output
You are in a quite particular scenario where the <input> itself is the root element of your component. So you can't use v-model if you want to listen to the native event, cause it only exists on v-on. v-model is just a shorcut thought, and you can do it easily like this:
HTML:
<ichecker :checked="formData.checkbox"
#change.native="formData.checkbox = $event.target.checked">
</ichecker>
JS:
Vue.component('ichecker', {
prop: ['checked'],
template: '<input type="checkbox" :checked="checked" />'
});
https://jsbin.com/suvonolita/edit?html,js,output
You may also want to do it without the native event, if the input is not your root node. Or you may really want v-model. The way to implement v-model on a custom component is described there: https://v2.vuejs.org/v2/guide/components.html#Customizing-Component-v-model

How to get results after submit form with PhantomJS?

I'm trying to get results from a simple form using PhantomJS. I'm using jQuery but don't work. I have this HTML:
<!doctype html>
<html>
<head>
<title>PhantomJS!</title>
</head>
<body>
<form method="post" id="frm">
<input type="text" name="nombre" id="nombre" />
<input type="submit" value="Publicar" id="btn-submit" />
</form>
Your name is <span id="nombrez"><?php if (isset($_POST['nombre'])) { echo $_POST['nombre'];} ?></span>
</body>
</html>
And this Javascript code:
var page = require('webpage').create();
page.open('http://localhost/phantom/', function() {
page.includeJs("https://code.jquery.com/jquery-3.1.1.slim.js", function() {
page.evaluate(function() {
$('#nombre').val('Fabian');
document.forms[0].submit();
});
page.onLoadFinished = function(){
console.log($("#nombrez").html());
phantom.exit();
};
});
});
page.onLoadFinished must not be called inside of page.evaluate, but inside the main PhantomJS script:
var page = require('webpage').create();
page.onLoadFinished = function(){
var html = page.evaluate(function(){
return document.getElementById("nombrez").innerHTML;
});
console.log(html);
phantom.exit();
};
page.open('http://localhost/phantom/', function() {
page.includeJs("https://code.jquery.com/jquery-3.1.1.slim.js", function() {
page.evaluate(function() {
$('#nombre').val('Fabian');
document.forms[0].submit();
});
});
});
However page.onLoadFinished fires every time a page is done loading and with this implementation phantom will exit the first the time page is loaded, even before the form is submitted.
You need to implement some check to distinguish between the first and the second load of the page. For example, if return html variable is empty it means that we haven't submitted page yet.

Polymer 1.0 dynamically add options to menu

Hi I am having some trouble getting a menu to add options dynamically. They idea is the selection of the first menu decides what the second menu contains. I have built this before successfully without polymer. And it semi-works with polymer. dropdown one gets its content from json based on the selection, dropdown two gets its content also from a json. This part works, the issue is when you make a selection from dropdown one and then change it, dropdown two doesn't delete the old selection. I got this working last time with a function that first deletes all dropdown two's children before repopulating the content. Issue with Polymer is once the childNodes are deleted the dropdown breaks and no other children can be added via data binding. tried adding native with plain JS which populates the menu but the children are not selectable(from what I have read this might be a bug). Also I believe data binding on dynamic items also doesnt work anymore. anyway here is what I have:
<link rel="import" href="../../../bower_components/polymer/polymer.html">
<link rel="import" href="../../../bower_components/paper-material/paper-material.html">
<link rel="import" href="../../../bower_components/paper-dropdown-menu/paper-dropdown-menu.html">
<link rel="import" href="../../../bower_components/paper-menu/paper-menu.html">
<link rel="import" href="../../../bower_components/paper-item/paper-item.html">
<link rel="import" href="../../../bower_components/iron-ajax/iron-ajax.html">
<link rel="import" href="../../../bower_components/paper-button/paper-button.html">
<link rel="import" href="../../../bower_components/iron-dropdown/demo/x-select.html">
<dom-module id="add-skill">
<template>
<paper-material elevation="1">
<paper-dropdown-menu id="ddMenu" attr-for-selected="value" >
<paper-menu class="dropdown-content" id="vendorSelect" on-iron-select="_itemSelected">
<template is="dom-repeat" items="{{vendorList}}">
<paper-item id="vendorName" value="item">[[item]]</paper-item>
</template>
</paper-menu>
</paper-dropdown-menu>
<paper-dropdown-menu>
<paper-menu class="dropdown-content" id="certificationSelect" on-iron-select="_itemSelected">
</paper-menu>
</paper-dropdown-menu>
<!-- testing ideas -->
<paper-dropdown-menu>
<paper-menu class="dropdown-content" id="test" on-iron-select="_itemSelected">
<option extends="paper-item"> Option </option>
<option extends="paper-item"> Option1 </option>
<option extends="paper-item"> Option2 </option>
</paper-menu>
</paper-dropdown-menu>
<paper-button on-click="_deleteElement">
Delete
</paper-button>
</paper-material>
<iron-ajax
id="vendorSubmit"
method="POST"
url="../../../addskill.php"
handle-as="json"
on-response="handleVendorResponse"
debounce-duration="300">
</iron-ajax>
<iron-ajax
id="certificationSubmit"
method="POST"
url="../../../addskill.php"
handle-as="json"
on-response="handleCertificationResponse"
debounce-duration="300">
</iron-ajax>
</template>
<script>
Polymer({
is: 'add-skill',
ready: function() {
this.sendVendorRequest();
this.vendorList = [];
this.certificationList = [];
},
sendVendorRequest: function() {
var datalist = 'vendor=' + encodeURIComponent('1');
//console.log('datalist: '+datalist);
this.$.vendorSubmit.body = datalist;
this.$.vendorSubmit.generateRequest();
},
handleVendorResponse: function(request) {
var response = request.detail.response;
for (var i = 0; i < response.length; i++) {
this.push('vendorList', response[i].name);
}
},
vendorClick: function() {
var item = this.$;
//var itemx = this.$.vendorSelect.selectedItem.innerHTML;
//console.log(item);
//console.log(itemx);
},
sendCertificationRequest: function(vendor) {
var datalist = 'vendorName=' + encodeURIComponent(vendor);
console.log('datalist: ' + datalist);
this.$.certificationSubmit.body = datalist;
this.$.certificationSubmit.generateRequest();
},
handleCertificationResponse: function(request) {
var response = request.detail.response;
//var vendorSelect = document.getElementById('vendorSelect');
for (var i = 0; i < response.length; i++) {
this.push('certificationList', response[i].name);
}
console.log(this.certificationList);
},
_itemSelected: function(e) {
var selectedItem = e.target.selectedItem;
if (selectedItem) {
this.sendCertificationRequest(selectedItem.innerText);
console.log("selected: " + selectedItem.innerText);
}
},
_removeArray: function(arr) {
this.$.certificationList.remove();
for (var i = 0; i < arr.length; i++) {
console.log(arr[i]);
arr.splice(0, i);
arr.pop();
}
console.log(arr.length);
},
_deleteElement: function() {
var element = document.getElementById('certificationSelect');
while (element.firstChild) {
element.removeChild(element.firstChild);
}
},
_createElement: function() {
var doc = document.querySelector('#test');
var option = document.createElement('option');
option.extends = "paper-item";
option.innerHTML = "Option";
doc.appendChild(option);
}
});
</script>
</dom-module>
Any guidance is always appreciated
Here's a working version of your JSBin, which uses data binding and a <template is="dom-repeat"> to create new, selectable <paper-item> elements dynamically.
I'm not sure what specific issues you ran into when using data binding to stamp out the <paper-item> elements, but the important thing to remember in Polymer 1.0 is that when you modify an Array (or an Object) that is bound to a template, you need to use the new helper methods (like this.push('arrayName', newItem)) to ensure the bindings are updated.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<base href="http://element-party.xyz">
<script src="bower_components/webcomponentsjs/webcomponents-lite.js"></script>
<link rel="import" href="all-elements.html">
</head>
<body>
<dom-module id="x-module">
<template>
<paper-material elevation="1">
<paper-dropdown-menu>
<paper-menu class="dropdown-content" on-iron-select="_itemSelected">
<template is="dom-repeat" items="[[_menuItems]]">
<paper-item>[[item]]</paper-item>
</template>
</paper-menu>
</paper-dropdown-menu>
<paper-button on-click="_createItem">Add</paper-button>
</paper-material>
</template>
<script>
Polymer({
_createItem: function() {
this.push('_menuItems', 'New Option ' + this._menuItems.length);
},
_itemSelected: function() {
console.log('Selected!');
},
ready: function() {
this._menuItems = ['First Initial Option', 'Second Initial Option'];
}
});
</script>
</dom-module>
<x-module></x-module>
</body>
</html>