I have an issue with the new Webix 4.3 release:
angular.js:66 Uncaught Error: [$injector:modulerr] Failed to instantiate
module monitorApp due to:
Error: [$injector:modulerr] Failed to instantiate module webix due to:
Error: [$injector:nomod] Module 'webix' is not available! You either
misspelled the module name or forgot to load it. If registering a module
ensure that you specify the dependencies as the second argument.
I get this error just updating the version from 4.2, here is how do i inject the module:
var app=angular.module("monitorApp", ['webix', 'ngRoute','ui.router']);
html:
<!--Includes-->
<script src="Scripts/angular.js"></script>
<script src="Scripts/webix.js"></script>
Any ideas about what is it going?
Starting from Webix 4.3, AngularJs integration is a separate module
https://docs.webix.com/desktop__whats_new_4_3.html
You can grab it from github
https://github.com/webix-hub/webix-angular
Or install through npm
npm install webix-angular
You can add an extra file which is mandatory for the Webix Angular Integration part.
// Code goes here
if (window.angular)
(function(){
function id_helper($element){
//we need uniq id as reference
var id = $element.attr("id");
if (!id){
id = webix.uid();
$element.attr("id", id);
}
return id;
}
function locate_view_id($element){
if (typeof $element.attr("webix-ui") != "undefined")
return $element.attr("id");
return locate_view_id($element.parent());
}
//creates webix ui components
angular.module("webix", [])
.directive('webixUi', [ "$parse", function($parse) {
return {
restrict: 'A',
scope: false,
link:function ($scope, $element, $attrs, $controller){
var dataname = $attrs["webixUi"];
var callback = $attrs["webixReady"];
var watch = $attrs["webixWatch"];
var wxRoot = null;
var id = id_helper($element);
$element.ready(function(){
if (wxRoot) return;
if (callback)
callback = $parse(callback);
//destruct components
$element.bind('$destroy', function() {
if (wxRoot && !wxRoot.$destructed && wxRoot.destructor)
wxRoot.destructor();
});
//ensure that ui is destroyed on scope destruction
$scope.$on('$destroy', function(){
if (wxRoot && !wxRoot.$destructed && wxRoot.destructor)
wxRoot.destructor();
});
//webix-ui attribute has some value - will try to use it as configuration
if (dataname){
//configuration
var watcher = function(data){
if (wxRoot) wxRoot.destructor();
if ($scope[dataname]){
var config = webix.copy($scope[dataname]);
config.$scope =$scope;
$element[0].innerHTML = "";
wxRoot = webix.ui(config, $element[0]);
if (callback)
callback($scope, { root: wxRoot });
}
};
if (watch !== "false")
$scope.$watch(dataname, watcher);
watcher();
} else {
//if webix-ui is empty - init inner content as webix markup
if (!$attrs["view"])
$element.attr("view", "rows");
var ui = webix.markup;
var tmp_a = ui.attribute; ui.attribute = "";
//FIXME - memory leaking, need to detect the moment of dom element removing and destroy UI
if (typeof $attrs["webixRefresh"] != "undefined")
wxRoot = ui.init($element[0], $element[0], $scope);
else
wxRoot = ui.init($element[0], null, $scope);
ui.attribute = tmp_a;
if (callback)
callback($scope, { root: wxRoot });
}
//size of ui
$scope.$watch(function() {
return $element[0].offsetWidth + "." + $element[0].offsetHeight;
}, function() {
if (wxRoot) wxRoot.adjust();
});
});
}
};
}])
.directive('webixShow', [ "$parse", function($parse) {
return {
restrict: 'A',
scope: false,
link:function ($scope, $element, $attrs, $controller){
var attr = $parse($attrs["webixShow"]);
var id = id_helper($element);
if (!attr($scope))
$element.attr("hidden", "true");
$scope.$watch($attrs["webixShow"], function(){
var view = webix.$$(id);
if (view){
if (attr($scope)){
webix.$$(id).show();
$element[0].removeAttribute("hidden");
} else
webix.$$(id).hide();
}
});
}
};
}])
.directive('webixEvent', [ "$parse", function($parse) {
var wrap_helper = function($scope, view, eventobj){
var ev = eventobj.split("=");
var action = $parse(ev[1]);
var name = ev[0].trim();
view.attachEvent(name, function(){
return action($scope, { id:arguments[0], details:arguments });
});
};
return {
restrict: 'A',
scope: false,
link:function ($scope, $element, $attrs, $controller){
var events = $attrs["webixEvent"].split(";");
var id = id_helper($element);
setTimeout(function(){
var first = $element[0].firstChild;
if (first && first.nodeType == 1)
id = first.getAttribute("view_id") || id;
var view = webix.$$(id);
for (var i = 0; i < events.length; i++) {
wrap_helper($scope, view, events[i]);
}
});
}
};
}])
.directive('webixElements', [ "$parse", function($parse) {
return {
restrict: 'A',
scope: false,
link:function ($scope, $element, $attrs, $controller){
var data = $attrs["webixElements"];
var id = id_helper($element);
if ($scope.$watchCollection)
$scope.$watchCollection(data, function(collection){
setTimeout(function(){
var view = webix.$$(id);
if (view){
view.define("elements", collection);
view.refresh();
}
},1);
});
}
};
}])
.directive('webixData', [ "$parse", function($parse) {
return {
restrict: 'A',
scope: false,
link:function ($scope, $element, $attrs, $controller){
var data = $attrs["webixData"];
var id = id_helper($element);
if ($scope.$watchCollection)
$scope.$watchCollection(data, function(collection){
if (collection){
setTimeout(function(){
loadData($element, id, collection, 0);
},1);
}
});
}
};
}]);
function loadData($element, id, collection, num){
if (num > 10) return;
var first = $element[0].firstChild;
if (first && first.nodeType == 1)
id = first.getAttribute("view_id") || id;
var view = webix.$$(id);
if (view){
if (view.options_setter){
view.define("options", collection);
view.refresh();
}else{
if (view.clearAll)
view.clearAll();
view.parse(collection);
}
} else {
webix.delay(loadData, this, [$element, id, collection], 100, num+1);
}
}
})();
<!doctype html>
<html lang="en" ng-app="webixApp">
<head>
<meta charset="utf-8">
<title>Webix - Angular : Layouts</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.4/angular.js"></script>
<script src="https://angular-ui.github.io/ui-router/release/angular-ui-router.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdn.webix.com/edge/webix.css">
<script type="text/javascript" src="https://cdn.webix.com/edge/webix.js"></script>
<script type="text/javascript" src="script.js"></script>
<script type="text/javascript">
var app = angular.module('webixApp', ["webix", 'ui.router']);
app.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("/add");
$stateProvider
.state('/add', {
url: '/add',
template: 'This is the Routed Addition File'
})
.state('/sub', {
url: '/sub',
template: 'This is the Routed Subtraction File'
})
.state('/div', {
url: '/div',
template: 'This is the Routed Division File'
})
.state('/multi', {
url: '/multi',
template: 'This is the Routed Multiplication File'
})
})
app.controller('jsonCtrl', function($scope) {
var treeData = {
data: [{
id: "root",
value: "Super Micro Flow",
open: true,
link: "add",
data: [{
id: "1",
open: true,
value: "AP1",
link: "multi",
data: [{
id: "1.1",
value: "2G09#M10000761",
link: "div",
data: [{
id: "1.11",
value: "2G09#M10000761",
link: "add",
data: [{
id: "1.11",
value: "2G09#M10000761",
link: "sub"
}, {
id: "1.12",
value: "2G09#M10855757",
link: "multi"
}, {
id: "1.13",
value: "2G09#D10PP0761",
link: "div"
}]
}, {
id: "1.12",
value: "2G09#M10855757",
link: "multi"
}, {
id: "1.13",
value: "2G09#D10PP0761",
link: "div"
}]
}, {
id: "1.2",
value: "2G09#M10855757",
link: "multi"
}, {
id: "1.3",
value: "2G09#D10PP0761",
link: "div"
}]
}, {
id: "2",
open: false,
value: "AP12",
link: "multi",
data: [{
id: "2.1",
value: "2G09#M10000761",
link: "sub"
}, {
id: "2.2",
value: "2G09#M10855757",
link: "multi"
}, {
id: "2.3",
value: "2G09#D10PP0761",
link: "div"
}]
}, {
id: "3",
open: false,
value: "EP45",
link: "div",
data: [{
id: "3.1",
value: "2G09#M10000761",
link: "sub"
}, {
id: "3.2",
value: "2G09#M10855757",
link: "multi"
}, {
id: "3.3",
value: "2G09#D10PP0761",
link: "div"
}]
}, {
id: "4",
open: false,
value: "CR7767",
link: "sub",
data: [{
id: "4.1",
value: "2G09#M10000761",
link: "sub"
}, {
id: "4.2",
value: "2G09#M10855757",
link: "multi"
}, {
id: "4.3",
value: "2G09#D10PP0761",
link: "div"
}]
}]
}]
}
$scope.myTree = {
view: "tree",
data: treeData,
template: "{common.icon()}{common.folder()}<a ui-sref='#link#' href='##link#'>#value#</a>",
}
})
</script>
</head>
<body ng-controller="jsonCtrl">
<div webix-ui type="space">
<div height="35">Welcome to Angular Webix App </div>
<div view="cols" type="wide" margin="10">
<div width="300">
<input type="text" placeholder="Type something here" ng-model="app"> Hello {{app}}!
<div webix-ui="myTree"></div>
</div>
<div view="resizer"></div>
<div view="tabview">
<div header="Angular UI Routing Example">
<div ng-controller="jsonCtrl">
<div ui-view></div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
You can copy the script file and put into your application as an independent file. Don't write anything in this file.
Related
I want the title for the highchart to be $scope.data.title but currently the attribute title interpret data.title as a string and a bind to the scope. I've tried putting "", {{}} around data.title the .html but it doesn't work. I think im missing something else.
index.html
<test-chart title="{{data.title}}">
<chart-series title="Series 1" type="line">
</chart-series>
<chart-series title="Series 3" type="column">
</chart-series>
</test-chart>
script.js
.directive('testChart', function() {
return {
restrict: 'E',
transclude: true,
controllerAs:'chartCtrl',
scope: {
},
controller: ['$scope', '$element', '$attrs', function ChartController($scope, $element, $attrs) {
$scope.data = {
title: 'HIGHGRAPH',
series: [{
title: 'series1',
type: 'line',
data: [1,2]
}, {
title: 'series2',
type: 'area',
data: [3,5]
}]
}
var hc = Highcharts.chart('highchart_container', {});
$scope.$watch("data",function(newValue,oldValue) {
hc.update({
title: {
text: newValue.title
}
})
})
this.addSeries = function(a) {
hc.addSeries({
name: a.title,
type: a.type,
data: [1,2,3,4,5,6]
})
};
}],
templateUrl: 'my-tabs.html'
};
EDIT: https://plnkr.co/edit/spUAkCjK61HgUGu40pZl?p=preview
This not works as intended, but is it possible to do it without the watch?
you won't have to put s.title inside ng-repeat. it should be outside.
Here is a working snippet.:-
var app = angular.module("myApp", []);
app.controller("myCtrl", function($scope) {
});
app.directive('testChart', function() {
return {
restrict: 'E',
transclude: true,
controllerAs:'chartCtrl',
scope: {
},
controller: ['$scope', '$element', '$attrs', function ChartController($scope, $element, $attrs) {
$scope.data = {
title: 'HIGHGRAPH',
series: [{
title: 'series1',
type: 'line',
data: [1,2]
}, {
title: 'series2',
type: 'area',
data: [3,5]
}]
}
var hc = Highcharts.chart('highchart_container', {
title: {
text: $scope.data.title
}
});
this.addSeries = function(a) {
hc.addSeries({
name: a.title,
type: a.type,
data: [1,2,3,4,5,6]
})
};
}],
template: '<div> <p>This is a chart</p><ul> <li ng-repeat="s in series"> </li> </ul> <div id=\'highchart_container\'></div> <ng-transclude></ng-transclude> </div>'
};
})
.directive('chartSeries', function() {
return {
require: '^test-chart',
restrict: 'E',
transclude: true,
scope: {
title: '#'
},
link: function(scope, element, attrs, chartCtrl) {
chartCtrl.addSeries(attrs);
},
};
});
#highchart_container{
height:250px!important;
}
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<script src="https://code.highcharts.com/highcharts.js"></script>
<body>
<div ng-app="myApp" ng-controller="myCtrl">
<test-chart title="Custom title">
<chart-series title="Series 1" type="line">
</chart-series>
<chart-series title="Series 3" type="column">
</chart-series>
</test-chart>
</div>
Apparently this is how you must do it. Highchart docs for title
working plunker
Now my-tabs.html
<div>
<div id='highchart_container'></div>
<ng-transclude></ng-transclude>
</div>
and index.html
<body ng-app="docsTabsExample">
<test-chart>
<chart-series title="Series 1" type="line">
</chart-series>
<chart-series title="Series 3" type="column">
</chart-series>
</test-chart>
</body>
</html>
script.js
(function(angular) {
'use strict';
angular.module('docsTabsExample', [])
.directive('testChart', function() {
return {
restrict: 'E',
transclude: true,
controllerAs:'chartCtrl',
controller: ['$scope', '$element', '$attrs', function ChartController($scope, $element, $attrs) {
$scope.data = {
title: 'HIGHGRAPH',
series: [{
title: 'series1',
type: 'line',
data: [1,2]
}, {
title: 'series2',
type: 'area',
data: [3,5]
}]
}
var hc = Highcharts.chart('highchart_container', {
title: {
align:"center",
text:$scope.data.title
}
});
this.addSeries = function(a) {
hc.addSeries({
name: a.title,
type: a.type,
data: [1,2,3,4,5,6]
})
};
}],
templateUrl: 'my-tabs.html'
};
})
.directive('chartSeries', function() {
return {
require: '^test-chart',
restrict: 'E',
transclude: true,
scope: {
title: '#'
},
link: function(scope, element, attrs, chartCtrl) {
chartCtrl.addSeries(attrs);
},
};
});
})(window.angular);
i can't see why you need data binding if the title is a scope variable of the chartCtrl.
this will simply do the trick:
<div>
{{data.title}}
<div id='highchart_container'></div>
<ng-transclude></ng-transclude>
</div>
Plunker
Solved it by adding a watch on the object.
https://plnkr.co/edit/spUAkCjK61HgUGu40pZl?p=preview
two questions: is it possible to access my showData() function that is located in the controller even though its not within the directive? I also have placed one in the directive but it is still not alerting? please advice on what i am doing wrong.
var myApp = angular.module('myApp', []);
myApp.controller('productController', ['$scope',
function($scope) {
$scope.product1 = {
name: 'Phone',
price: '100',
stock: true
};
$scope.product2 = {
name: 'Ipad',
price: '1000',
stock: true
};
$scope.product3 = {
name: 'Laptop',
price: '800',
stock: false
};
$scope.showData = function() {
alert("Display Data");
}
}
]);
myApp.directive('myInventory', function() {
return {
restrict: 'E',
scope: {
name: '#',
price: '#'
},
template: '{{name}} costs {{price}} <button ng-click="showData()">change cost</button>'
};
directive.link = function($scope, element) {
$scope.showData = function() {
alert("Display Data");
};
}
return directive;
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.9/angular.min.js"></script>
<body ng-App="myApp">
<div ng-controller="productController">
<h1>{{product1.name}}</h1>
<my-inventory name="{{product1.name}}" price="{{product1.price}}"></my-inventory>
<h1>{{product2.name}}</h1>
<my-inventory name="{{product2.name}}" price="{{product2.price}}"></my-inventory>
<h1>{{product3.name}}</h1>
<my-inventory name="{{product3.name}}" price="{{product3.price}}"></my-inventory>
</div>
</body>
In your directive, you're returning too early so half of your code isn't executing.
myApp.directive('myInventory', function() {
return {
restrict: 'E',
scope: {
name: '#',
price: '#'
},
template: '{{name}} costs {{price}} <button ng-click="showData()">change cost</button>',
link: function($scope, element) {
$scope.showData = function() {
alert("Display Data");
};
}
});
It's possible for you to pass showData from the controller to the directive.
myApp.directive('myInventory', function() {
return {
restrict: 'E',
scope: {
name: '#',
price: '#',
showData: '&' // Add it to your scope like so
},
template: '{{name}} costs {{price}} <button ng-click="showData()">change cost</button>',
link: function($scope, element) {
// Don't need showData() here anymore
}
});
Call your directive with your function like so
<my-inventory show-data="showData()" name="{{ something }}" price="{{ something }}"></my-inventory>
I'm studying angular and trying to write treeview with dynamic load. It has some initial pre-loaded nodes, but many of them have to be loaded by demand.
I write initial tree view here jsfiddle but cannot understand how to implement lazy load.
when I click NodeLazy I want to update treeService.Nodes and this update have to refresh view
when I click any Node I want select this node by bold font and be able get selected from treeService
Sure, I could use one of existing angular tree, but I want to understand how to do this.
var app = angular.module('demo', []);
app.directive("treeView", function() {
return {
restrict: "E",
replace: true,
scope: {
t: "=src"
},
template: "<ul class='ul-node'><tree-branch ng-repeat='c in t.children' src='c'></tree-branch></ul>"
};
});
app.directive("treeBranch", function($compile) {
return {
restrict: "E",
replace: true,
scope: {
b: "=src"
},
template: "<li class='{{b.class_li}}'>{{b.name}}</li>",
controller: function($scope) {
var has_children = angular.isArray($scope.b.children);
$scope.b.class_li = has_children ? "li-node" : "li-leaf";
},
link: function(scope, element, attrs) {
var has_children = angular.isArray(scope.b.children);
if (has_children) {
element.append('<tree-view src="b"></tree-view>');
$compile(element.contents())(scope);
}
element.on("click", function(event) {
console.log("click|" + scope.b.name);
event.stopPropagation();
if (has_children) {
element.toggleClass("ul-collapsed");
}
});
}
};
});
app.service("treeService", function() {
var nodes = {
children: [
{ name: "Node_1",
children: [
{ name: "Node_1.1",
children: [{ name: "Leaf_1" }, { name: "Leaf_2" }] },
{ name: "NodeLazy_1.2", }] },
{ name: "NodeLazy_2", }]
};
return {
Nodes : nodes
};
});
app.controller("ctrl", function($scope, treeService) {
$scope.nodes = treeService.Nodes;
});
I have tried to pass locals in BottomSheet Controller
//Bottom Sheet Controller
angular
.module('app').controller('BottomSheetCtrl', function($scope, $mdBottomSheet) {
$scope.items = [
{ name: 'Share', icon: 'share-arrow' },
{ name: 'Upload', icon: 'upload' },
{ name: 'Copy', icon: 'copy' },
];
$scope.items.append($scope.Item);
console.log($scope.items);
});
//AppCtrl
angular
.module('app').controller('AppCtrl', function($scope, $mdBottomSheet){
$scope.openBottomSheet = function() {
$mdBottomSheet.show({
template:
'<md-bottom-sheet>{{}}</md-bottom-sheet>',
controller: 'BottomSheetCtrl',
scope: $scope.$new(true),
// preserveScope: true,
locals: {
Item: {
'name': 'Print this page', 'icon': 'print'
},
}
});
};
});
But $scope.Item is not populating. What is the correct way of passing locals in BottomSheet Controller?
You have to inject the locals into the bottom sheet controller - CodePen
Markup
<div ng-controller="BottomSheetExample" class="md-padding bottomSheetdemoBasicUsage" ng-cloak="" ng-app="MyApp">
<md-button flex="50" class="md-primary md-raised" ng-click="showListBottomSheet()">Show as List</md-button>
</div>
JS
angular.module('MyApp',['ngMaterial', 'ngMessages', 'material.svgAssetsCache'])
.controller('BottomSheetExample', function($scope, $timeout, $mdBottomSheet, $mdToast) {
$scope.showListBottomSheet = function() {
$scope.alert = '';
$mdBottomSheet.show({
template: '<md-bottom-sheet ng-cloak>{{Item.name}}</md-bottom-sheet>',
controller: 'ListBottomSheetCtrl',
locals: {
Item: {
'name': 'Print this page', 'icon': 'print'
},
}
}).then(function(clickedItem) {
$scope.alert = clickedItem['name'] + ' clicked!';
});
};
})
.controller('ListBottomSheetCtrl', function($scope, $mdBottomSheet, Item) {
console.log(Item);
$scope.Item = Item;
});
Bottom sheet reference
locals - {string=}: An object containing key/value pairs. The keys
will be used as names of values to inject into the controller. For
example, locals: {three: 3} would inject three into the controller
with the value of 3.
As an alternative you can also set the bindToController property to true. This allows accessing locals in the BottomSheetCtrl. So in the BottomSheetCtrl you can then get the value of Item like this:
var Item = this.locals.Item;
JS
//Bottom Sheet Controller
angular
.module('app').controller('BottomSheetCtrl', function($scope,
$mdBottomSheet) {
$scope.items = [
{ name: 'Share', icon: 'share-arrow' },
{ name: 'Upload', icon: 'upload' },
{ name: 'Copy', icon: 'copy' },
];
var Item = this.locals.Item;
$scope.items.append(Item);
console.log($scope.items);
});
//AppCtrl
angular
.module('app').controller('AppCtrl', function($scope, $mdBottomSheet){
$scope.openBottomSheet = function() {
$mdBottomSheet.show({
template:
'<md-bottom-sheet>{{}}</md-bottom-sheet>',
controller: 'BottomSheetCtrl',
scope: $scope.$new(true),
bindToController: true,
locals: {
Item: {
'name': 'Print this page', 'icon': 'print'
},
}
});
};
});
I have a directive that has a piece of custom HTML that I can pass in as a custom option. I would like to use ng-repeat inside the custom HTML, but it is not being displayed. Here is the code.
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
$scope.local = {
options: {
columns: [{
name: 'product_id',
label: 'Product ID'
}, {
name: 'product_name',
label: 'Name'
}],
getBody: function( col, i ) {
return col.name == 'product_id' ? col.label : '<div class="dropdown product-status"><button class="btn btn-primary dropdown-toggle" data-toggle="dropdown">Status<span class="caret"></span></button><span class="dropdown-arrow"></span><ul class="dropdown-menu"><li ng-repeat="one_status in dataset.status"><a class="status all" ng-click="dataset.updateStatus(one_status.status_id)" ng-bind="one_status.status_name"></a></li></ul></div>';
}
},
dataset: {
status: [{
status_id: 1,
status_name: 'first draft'
}, {
status_id: 2,
status_name: 'in edit'
}]
}
};
});
app.directive('test', function($sce) {
var linkFunc = function(scope) {
scope.getBody = function( col, index ) {
return $sce.trustAsHtml( scope.options.getBody( col, index ) );
};
};
return {
link: linkFunc,
scope: {
options: '=',
dataset: '='
},
templateUrl: 'test.html'
};
});
http://plnkr.co/edit/mCynQWtQElTGfoLXZHqZ?p=preview
you need to make sure $sce is in a part of your controller
check this out
-- http://jsfiddle.net/3J25M/2/
.controller('ngBindHtmlCtrl', ['$scope','$sce', function ngBindHtmlCtrl($scope, $sce)
...etc
also, check this question out: AngularJS using $sce.trustAsHtml with ng-repeat