I am new to Windows App Development and I am trying to create a ListView in order to understand how it works. My problem is that I am trying to use a namespace on my ListView div and it returns the error saying that the property dataSource doesn't exist.
This is my html and javascript:
// For an introduction to the Page Control template, see the following documentation:
// http://go.microsoft.com/fwlink/?LinkId=232511
(function () {
"use strict";
WinJS.UI.Pages.define("/pages/episodes/episodes.html", {
// This function is called whenever a user navigates to this page. It
// populates the page elements with the app's data.
ready: function (element, options) {
// TODO: Initialize the page here.
Episodes.Data.assignItems(items);
WinJS.UI.processAll();
},
unload: function () {
// TODO: Respond to navigations away from this page.
},
updateLayout: function (element) {
/// <param name="element" domElement="true" />
// TODO: Respond to changes in layout.
},
});
WinJS.Namespace.define("Episodes.Data", {
itemsBindingList: undefined,
assignItems: function (items) {
Episodes.Data.itemsBindingList = new WinJS.Binding.List(items);
},
});
var items = [
{ title: 'Air Gear' },
{ title: 'Bakuman' }
];
})();
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>episodes</title>
<link href="episodes.css" rel="stylesheet" />
<script src="episodes.js"></script>
</head>
<body>
<div class="episodes fragment">
<header class="page-header" aria-label="Header content" role="banner">
<button class="back-button" data-win-control="WinJS.UI.BackButton"></button>
<h1 class="titlearea win-type-ellipsis">
<span class="pagetitle">Welcome to episodes</span>
</h1>
</header>
<section class="page-section" aria-label="Main content" role="main">
<div data-win-control="WinJS.UI.ListView" data-win-options="{
itemDataSource : Episodes.Data.itemsBindingList.dataSource
}"></div>
</section>
</div>
</body>
</html>
Since I am using an anonymous function on my .js file, I created a namespace that I can use on the .html file. Inside the ListView div, I have this:
div data-win-control="WinJS.UI.ListView" data-win-options="{
itemDataSource : Episodes.Data.itemsBindingList.dataSource
}"></div>
I am using the namespace to retrieve my data that I want to show on the ListView. My problem is that I get an error saying:
"{\"exception\":null,\"error\":{\"description\":\"It's not possible to obtain the property 'dataSource' of undifined or null reference\"
From what I can tell it is the fact that your property is initially undefined:
WinJS.Namespace.define("Episodes.Data", {
itemsBindingList: undefined, //this is a problem
assignItems: function (items) {
Episodes.Data.itemsBindingList = new WinJS.Binding.List(items);
},
});
Your html is then trying to bind to a property of an undefined object:
<section class="page-section" aria-label="Main content" role="main">
<div data-win-control="WinJS.UI.ListView" data-win-options="{
itemDataSource : Episodes.Data.itemsBindingList.dataSource
}"></div>
</section>
Either try using an empty array to initialize:
WinJS.Namespace.define("Episodes.Data", {
itemsBindingList: new WinJS.Binding.List([]),
assignItems: function (items) {
Episodes.Data.itemsBindingList = new WinJS.Binding.List(items);
},
});
Or you can set the datasource in your code:
ready: function (element, options) {
// TODO: Initialize the page here.
var listView = document.getElementById('myListView').winControl;
Episodes.Data.assignItems(items);
listView.data = Episodes.Data.itemsBindingList;
WinJS.UI.processAll();
},
You can verify this by debugging in the ready function and your exception should come before your breakpoint gets hit.
Related
I have used List.JS before successfully, but this time I'm trying to use it with a Vue.JS rendering of a list from JSON data.
I have a button at the top that when clicked should show only the QB position player.
Unfortunately I just get nothing, all list items are removed and I don't get an error in the console so I'm not sure how to diagnose this.
Could it have something to do with the fact that the list elements aren't prerendered/static html but injected using vue.js?
https://jsfiddle.net/nolaandy/hw2mheem/
HTML/Vue Template
<div id='app'>
<div class="all-players-wrapper" id="all-player-listings">
<button id="filter-qb">QB</button>
<ul class="list">
<li v-for="player in playerJSON">
<div class="player-listing">
<div class="player-left">
<div class="player-name">{{player.firstName}} {{player.lastName}}</div>
<div class="playerPosition">{{ player.Position }}</div>
</div><!-- end player-left -->
<div class="player-right">
<div class="player-grade">GRADE <span>{{player.NFLGrade}}</span></div>
</div> <!--end player-right -->
</div>
</li>
</ul>
</div>
</div>
JS
var vm = new Vue({
el: '#app',
data: {
status: 'Combine Particpants',
playerJSON: []
},
created: function () {
this.loadData();
},
methods: {
loadData: function () {
var self = this;
axios.get('https://s3-us-west-2.amazonaws.com/s.cdpn.io/500458/tiny.json').then(function (response) {
self.playerJSON = response.data
console.log(response.data);
})
.catch(function (error) {
self.status = 'An error occurred - ' + error
});
}
}
});
var options = {
valueNames: [ 'playerPosition' ]
};
var featureList = new List('all-player-listings', options);
$('#filter-qb').click(function() {
featureList.filter(function(item) {
if (item.values().playerPosition == "QB") {
return true;
} else {
return false;
}
});
return false;
});
As you suspected, List.js isn't going to work properly if the DOM changes unpredictably. In this case, axios makes its call and populates the data after the (empty) List has been read into featureList.
Your example would work if you put the list-selecting-and-filtering code in the resolution of the axios call, but that's not going to be a solution that works in a truly dynamic environment.
A custom directive will be called every time the DOM updates, so you can apply your adjustments consistently. Here's a directive to apply a filter using List.js:
directives: {
filteredList(el, binding) {
if (binding.value) {
const options = {
valueNames: ['playerPosition']
};
const featureList = new List(el, options);
featureList.filter((item) => item.values().playerPosition === binding.value);
}
}
}
Apply it like so:
<div class="all-players-wrapper" v-filtered-list="filterValue">
Add the filterValue data item, and have the button set it:
<button id="filter-qb" #click="() => filterValue='QB'">QB</button>
and you're in business.
It's worth noting that you could get the same effect by using a computed to filter the data, and you wouldn't need an external library.
Updated fiddle
I have a single-page AngularJS app.
The index.html file looks like this:
<html ng-app="myApp" ng-controller="MyCtrl as myctrl">
<head>
<link rel="stylesheet" href="my-style-sheet.css">
<title>{{myctrl.title}}</title>
</head>
<body>
<div class="container">
<ol>
<li><a ui-sref="stateA">StateA</a></li>
<li><a ui-sref="stateB">StateB</a></li>
</ol>
<div ui-view></div>
</div>
<script src="my-app.js"></script>
</body>
</html>
As the user clicks on the StateA or StateB links, the page displays the content of those pages in place of <div ui-view></div>. Terrific.
As the user clicks around, the displayed content changes. I need the title of the page to change too. Currently it gets the title from the controller MyCtrl like this: <title>{{myctrl.title}}</title>. But when the user clicks those links, those states each have their own controllers. So I cannot use <title>{{myctrl.title}}</title>.
How can I update the title when various states the user clicks on have different controllers?
You can attach some data to each state of your routes, like a value that can be used to set the title of the page.
.state('test', {
url: '/test',
templateUrl: '/templates/test.html',
data: {
title: 'test title'
}
})
Then write a directive to read the title. You can check to see if the required data is available on the state you are going to, and attach the whole thing to $stateChangeSuccess event.
function dynamicTitle($rootScope, $timeout) {
return {
link: function(scope, el) {
$rootScope.$on('$stateChangeSuccess', function(event, toState) {
var title = (toState.data && toState.data.title)
? toState.data.title
: 'Default title';
$timeout(function() {
el.text(title);
}, 0, false);
};);
}
};
}
angular.module('myApp').directive('dynamicTitle', dynamicTitle);
And attach it to your <title>
<title dynamic-title></title>
You can create an AngularJS factory, inject it, modify it by calling 'Title.setTitle()' from controllers
<html ng-app="app" ng-controller="Ctrl">
<head>
<title>{{ Title.title() }}</title>
app.factory('Title', function() {
var title = 'Hello';
return {
title: function() { return title; },
setTitle: function(newTitle) { title = newTitle }
};
});
As a new developer on angularjs I have a specific question regarding provider. I'm trying to use the lazy load angular-lazy-load that has a specific functionality regarding the scrollable container. This scrollable container should be set in the config step.
Here is an example :
var myApp = angular.module('myApp', ['angularLazyImg']);
myApp.config(['lazyImgConfigProvider',
function(lazyImgConfigProvider) {
var scrollable = document.querySelector('.container');
lazyImgConfigProvider.setOptions({
offset: 1, // how early you want to load image (default = 100)
errorClass: 'error', // in case of loading image failure what class should be added (default = null)
successClass: null, // in case of loading image success what class should be added (default = null)
onError: function(image) {}, // function fired on loading error
onSuccess: function(image) {}, // function fired on loading success
container: angular.element(scrollable)
});
}
]);
myApp.controller('lazyCtrl', ['$scope',
function($scope) {
$scope.images = [{
url: "https://cdn.sstatic.net/stackoverflow/img/apple-touch-icon#2.png?v=73d79a89bded&a"
}, {
url: "http://www.skrenta.com/images/stackoverflow.jpg"
}, {
url: "http://sourcingrecruitment.info/wp-content/uploads/2015/05/stackoverflow.png"
}, {
url: "http://gillespaquette.ca/images/stack-icon.png"
}, {
url: "http://www.iconsdb.com/icons/preview/black/stackoverflow-3-xxl.png"
}, {
url: "https://avatars0.githubusercontent.com/u/139426?v=3&s=400"
}, {
url: "http://gillespaquette.ca/images/stack-icon.png"
} ];
}
]);
.container {
overflow: scroll;
height: 200px;
position: absolute;
}
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Example - example-guide-concepts-1-production</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://rawgit.com/Pentiado/angular-lazy-img/master/release/angular-lazy-img.js"></script>
</head>
<body ng-app="myApp">
Test lazy image
<div ng-controller="lazyCtrl">
<div class="container">
<div ng-repeat="image in images">
<img width="100px" height="100px" lazy-img="{{image.url}}" />
</div>
</div>
</div>
</body>
But my question is how can I do to add a container option if the container element is not yet created and will be by the controller ?
Thanks in advance, please let me know which informations are missing to answer.
Ok so the trick is to add an attribut lazyImgContainer to the container in the html file. Not yet documented in github so I have added a pull request to get it documented.
I use Fuel UX Tree framework for loading tree view data.
On first load of document, tree view renders correctly, but when I try to reload tree with new DataSource, I get nothing.
Here is my code example:
Template for tree UI:
<div id="MyTree" class="tree tree-plus-minus tree-no-line tree-unselectable">
<div class="tree-folder" style="display:none;">
<div class="tree-folder-header">
<i class="fa fa-folder"></i>
<div class="tree-folder-name">
</div>
</div>
<div class="tree-folder-content">
</div>
<div class="tree-loader" style="display:none">
</div>
</div>
<div class="tree-item" style="display:none;">
<i class="tree-dot"></i>
<div class="tree-item-name">
</div>
</div>
</div>
Tree UI init function:
var UITree = function () {
return {
//main function to initiate the module
init: function (el, data) {
if (typeof el != 'undefined' && typeof data == 'object') {
var DataSourceTree = function (options) {
this._data = options.data;
this._delay = options.delay;
};
DataSourceTree.prototype = {
data: function (options, callback) {
var self = this;
var data = $.extend(true, [], self._data);
callback({ data: data });
}
};
var treeDataSource = new DataSourceTree(data);
$(el).tree({
selectable: false,
dataSource: treeDataSource,
loadingHTML: '<img src="assets/img/input-spinner.gif"/>',
});
}
}
};
}();
.
After loading data with Ajax I call init function:
//my data from ajax result
var myData = {
"data": [
{
"name": "some_name",
"type": "item"
},
{
"name": "some_name_2",
"type": "item"
}
]
};
// call tree init to render data
UITree.init($('#MyTree'), myData);
Function works without error and renders tree only on first load of page, not on next Ajax call.
Finally I found solution. Maybe it's not the best practice, but this is only way what I found:
Looks like Fuelux Tree UI doesn't work when element already has assigned data property, so I made this:
// 1. Clear MyTree wrapper template with:
$('#MyTree .tree-item:visible').remove();
// 2. remove assigned data from template element object
delete($('#MyTree').data().tree);
// 3. Refactor Tree UI
$('#MyTree').tree({
selectable: false,
dataSource: {
data: function(options, callback) {
callback(MyData);
}
}
});
I searched all over internet, re-read Fuelux documentation, but nothing is mentioned about re-factoring Tree UI, so if anyone will meet same problem, this solution can help.
I was going through the react getting started tutorial and have run into a problem with an experiment I am doing. I am able log an object but in the console, I get the following error:
Uncaught TypeError: Cannot read property 'results' of undefined
I can log the object so I know my api call is successfully but for some reason my react state does not seem to get updated. I think that my render function is happening before my data object gets updated from the API but not sure how to fix it.
http://jsfiddle.net/xJvY5/
<!doctype html>
<html>
<head>
<title>Weather Widget</title>
<link rel="stylesheet" href="weather.css" />
<script src="http://fb.me/react-0.10.0.js"></script>
<script src="http://fb.me/JSXTransformer-0.10.0.js"></script>
<script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
</head>
<body>
<script type="text/jsx">
/*** #jsx React.DOM */
var weatherWidget = React.createClass({
loadData: function(){
$.ajax({
url: 'http://query.yahooapis.com/v1/public/yql?q=select%20item%20from%20weather.forecast%20where%20location%3D%2222102%22&format=json',
dataType : "jsonp",
cache: false,
success: function(data) {
console.log(data)
this.setState({data: data});
}.bind(this)
});
},
getInitialState: function(){
return {data: []};
},
componentWillMount: function(){
this.loadData();
},
render: function(){
return(
<div className="ww-container">
<div className="ww-current-condition">
<div className="ww-current-temperture">{this.state.data.query.results.channel.item.condition.temp}°</div>
</div>
</div>
)
}
});
React.renderComponent(<weatherWidget />, document.body);
</script>
</body>
</html>
The problem is that React is trying to access the result of the API call while it hasn't been fetched yet. You should add null checks when accessing nested objects (this is a javascript issue rather than something React-specific).
Secondly, while the data is unavailable, your component will still try to render something. React renders your component the moment you inject it into the page, so consider showing a "loading" indicator while the API result has not been saved to state.
Here is a fork of your fiddle with appropriate null checks & "loading indicator":
http://jsfiddle.net/jxg/9WZA5/
render: function(){
var degrees = this.state.item ? this.state.item.condition.temp : 'loading...';
return(
<div className="ww-container">
<div className="ww-current-condition">
<div className="ww-current-temperture">{degrees}°</div>
</div>
</div>
);