Angularjs and Jade ng-repeat nested issue - angularjs

I am beginning to play around with Jade and I am having this weird issue. I'm sure it's something stupid, but I've been trying for one hour without success.
I have an object that contains groups, each group contains items. So, there is one ng-repeat nested inside the other.
ul.page-sidebar-menu(ng-repeat="group in menuItems")
li(ng-class="group.groupStyle")
a(href="{{group.target}}")
i(ng-class="group.iconStyle")
span.title {{group.name}}
span(ng-class="group.spanStyle")
ul.sub-menu(ng-repeat="item in group.items")
li
a(href="{{item.target}}") {{item.name}}
The object source is like this:
[
{
name: "Inicio",
target: "/",
groupStyle: {
start: "start",
active: "active"
},
spanStyle: {
selected: "selected"
},
iconStyle: "icon-home"
},
{
name: "Catalogo",
target: "javascript:;",
groupStyle: { },
spanStyle: {
arrow: "arrow"
},
iconStyle: "icon-book",
items: [
{ name: "Clientes", target: "view1" },
{ name: "Rutas", target: "view1" },
{ name: "Transportistas", target: "view1" }
]
},
{
name: "Panel de Control",
target: "javascript:;",
groupStyle: { },
spanStyle: {
arrow: "arrow"
},
iconStyle: "icon-cogs",
items: [
{ name: "Usuarios", target: "view2" },
{ name: "Configuracion", target: "view2" }
]
}
]
So, theoretically each group has some number of items and nesting should be possible. Here comes the funny part: when Jade renders the HTML based on a list it only renders the first child of every group. This is the output:
But when I added a table before the list with another ng-repeat it works fine. The code:
ul.page-sidebar-menu(ng-repeat="group in menuItems")
li(ng-class="group.groupStyle")
a(href="{{group.target}}")
i(ng-class="group.iconStyle")
span.title {{group.name}}
span(ng-class="group.spanStyle")
table
tr(ng-repeat="item in group.items")
td {{item.name}}
ul.sub-menu(ng-repeat="item in group.items")
li
a(href="{{item.target}}") {{item.name}}
And the output:
So, please someone with more coffee in their body or more skills in Jade give me a hand. I'm sure it must be something obvious.
Thanks in advance.

As I guessed it was a stupid issue. The ng-repeat should be at the items level, not at the list level. Here is the code fixed.
ul.page-sidebar-menu
li(ng-repeat="group in menuItems", ng-class="group.groupStyle")
a(href="{{group.target}}")
i(ng-class="group.iconStyle")
span.title {{group.name}}
span(ng-class="group.spanStyle")
ul.sub-menu
li(ng-repeat="item in group.items")
a(href="{{item.target}}") {{item.name}}

Related

Create a custom element in EditorJs

I added EditorJs plugin in my react js application:
import ReactDOM from "react-dom";
import React, { Component } from "react";
import EditorJs from "react-editor-js";
import { EDITOR_JS_TOOLS } from "./constants";
class ReactEditor extends Component {
render() {
return (
<EditorJs
tools={EDITOR_JS_TOOLS}
data={{
blocks: [
{
type: "header",
data: {
text: "Editor.js",
level: 2
}
},
{
type: "paragraph",
data: {
}
},
{
type: "header",
data: {
text: "Key features",
level: 3
}
},
{
type: "list",
data: {
style: "unordered",
items: [
"It is a block-styled editor",
"It returns clean data output in JSON",
"Designed to be extendable and pluggable with a simple API"
]
}
},
{
type: "header",
data: {
text: "What does it mean «block-styled editor»",
level: 3
}
},
{
type: "paragraph",
data: {
text:
'Workspace in classic editors is made of a single contenteditable element, used to create different HTML markups. Editor.js <mark class="cdx-marker">workspace consists of separate Blocks: paragraphs, headings, images, lists, quotes, etc</mark>. Each of them is an independent contenteditable element (or more complex structure) provided by Plugin and united by Editor\'s Core.'
}
},
{
type: "paragraph",
data: {
text:
'There are dozens of ready-to-use Blocks and the simple API for creation any Block you need. For example, you can implement Blocks for Tweets, Instagram posts, surveys and polls, CTA-buttons and even games.'
}
},
{
type: "header",
data: {
text: "What does it mean clean data output",
level: 3
}
},
{
type: "paragraph",
data: {
text:
"Classic WYSIWYG-editors produce raw HTML-markup with both content data and content appearance. On the contrary, Editor.js outputs JSON object with data of each Block. You can see an example below"
}
},
{
type: "paragraph",
data: {
text:
'Given data can be used as you want: render with HTML for <code class="inline-code">Web clients</code>, render natively for <code class="inline-code">mobile apps</code>, create markup for <code class="inline-code">Facebook Instant Articles</code> or <code class="inline-code">Google AMP</code>, generate an <code class="inline-code">audio version</code> and so on.'
}
},
{
type: "paragraph",
data: {
text:
"Clean data is useful to sanitize, validate and process on the backend."
}
},
{
type: "delimiter",
data: {}
},
{
type: "paragraph",
data: {
text:
"We have been working on this project more than three years. Several large media projects help us to test and debug the Editor, to make it's core more stable. At the same time we significantly improved the API. Now, it can be used to create any plugin for any task. Hope you enjoy. 😏"
}
},
{
type: "image",
data: {
file: {
url:
"https://codex.so/upload/redactor_images/o_e48549d1855c7fc1807308dd14990126.jpg"
},
caption: "",
withBorder: true,
stretched: false,
withBackground: false
}
}
],
version: "2.12.4"
}}
/>
);
}
}
ReactDOM.render(<ReactEditor />, document.getElementById("root"));
According to the documentation i can create a custom element:
render() {
return (
<EditorJs holder="custom">
<div id="custom" />
</EditorJs>
);
}
Question: I want to add as a custom element an input: <input type="text"/>, but i don't manage even if i do:
<EditorJs holder="custom">
<input id="custom" type="text"/>
</EditorJs>
Who knows how to add this custom element in the plugin above?
demo: https://codesandbox.io/embed/react-editor-js-23opz
I found in the documentation that i can create a plugin for editor.js:
https://editorjs.io/the-first-plugin. One of example looks like this:
class SimpleImage {
static get toolbox() {
return {
title: 'Image',
icon: '<svg width="17" height="15" viewBox="0 0 336 276" xmlns="http://www.w3.org/2000/svg"><path d="M291 150V79c0-19-15-34-34-34H79c-19 0-34 15-34 34v42l67-44 81 72 56-29 42 30zm0 52l-43-30-56 30-81-67-66 39v23c0 19 15 34 34 34h178c17 0 31-13 34-29zM79 0h178c44 0 79 35 79 79v118c0 44-35 79-79 79H79c-44 0-79-35-79-79V79C0 35 35 0 79 0z"/></svg>'
};
}
render() {
return document.createElement('input');
}
save(blockContent) {
return {
url: blockContent.value
}
}
}

Vue router-link param undefined in nested loop

I have a loop inside another loop.
The videos.id is undefined in the <router-link> but is rendered just fine otherwise..
<ul>
<li v-for="category in categories" :key="category.id">
{{category.name}}
<div v-for="videos in category.videos" :key="videos.id">
<router-link v-bind:to="/video-player/ + videos.id"> {{videos.id}} {{videos.name}}</router-link>
</div>
</li>
</ul>
Based on your codepen: https://codepen.io/cwerner/pen/mdyjLBv
It is showing videos.id as undefined because this data doesn't exist:
{
id: 1,
name: "Category",
videos: [
{
name: "Video1"
},
{
name: "Video 2",
}
],
}
There is only an id for category, so you must add id's in the video objects too:
{
id: 1,
name: "Category",
videos: [
{
name: "Video1",
id: 1
},
{
name: "Video 2",
id: 2
}
],
}
Content of binded property has to be valid JS expression. /video-player/ + videos.id is not valid JS expression. Change it to v-bind:to="'/video-player/' + videos.id"

How to create sap.m.ComboBox without the strange 'is not a function' error?

I've been playing around with SapUI5 for a while and now I'm facing another issue, which is totally confusing me.
My goal: To add a ComboBox to an oTable.
What I tried: I decided to do the 'separation of concerns', in order to investigate it better, so I 'extracted' the ComboBox code from the table and am testing it on its own like this:
var cbWizardTypes = [
{
Code: "0",
Name: "Name0",
AdditionalText: "Additional0"
},
{
Code: "1",
Name: "Name1",
AdditionalText: "Additional1"
},
{
Code: "2",
Name: "Name2",
AdditionalText: "Additional2"
},
];
// now the template to use when showing the items
var cbWizardTypesTemplate = new sap.ui.core.ListItem({
key: "{Code}",
text: "{Name}",
additionalText: "{AdditionalText}"
});
// now let's create it and place it
var cbWizardType = new sap.m.ComboBox({
items: {
path: cbWizardTypes,
template: cbWizardTypesTemplate
},
showSecondaryValues: true
});
cbWizardType.placeAt(containerID, 'only');
Now, this is giving me this error in the console:
Additionally, I tried not to use a template, just to see what happens
var cbWizardType = new sap.m.ComboBox({
//items: {
// path: cbWizardTypes,
// template: cbWizardTypesTemplate
//},
items: cbWizardTypes,
showSecondaryValues: true
});
In this case, there are no errors in the Chrome Developer Tools - Console. I get a ComboBox with 3 items, but they are all blank.
Now, I will, at least, try to investigate further, although library-preload.js had been minified, so it will be really hard and time-consuming to navigate through all those 'd'-s, 'p'-s, 'j'-s, etc., I guess.
As always, I will appreciate any help. Thank you!
The problem lies with the binding path that you assign to the ComboBox. The binding should be a string & not an array. You will have to store the data in a model & bind it then to your control.
The code below should work
var cbWizardTypes = [
{
Code: "0",
Name: "Name0",
AdditionalText: "Additional0"
},
{
Code: "1",
Name: "Name1",
AdditionalText: "Additional1"
},
{
Code: "2",
Name: "Name2",
AdditionalText: "Additional2"
},
];
var oModel = new sap.ui.model.json.JSONModel({ items: cbWizardTypes});
// now the template to use when showing the items
var cbWizardTypesTemplate = new sap.ui.core.ListItem({
key: "{Code}",
text: "{Name}",
additionalText: "{AdditionalText}"
});
// now let's create it and place it
var cbWizardType = new sap.m.ComboBox({
items: {
path: "/items",
template: cbWizardTypesTemplate
},
showSecondaryValues: true
});
cbWizardType.setModel(oModel);
cbWizardType.placeAt(containerID, 'only');

Advanced filtering with AngularJS

I'm looking to filter the following list with AngularJS by both category and location. The difficulty I'm having is being able to properly nest the results beneath their respective categories (see below) after the filtering has occurred.
var jobs = [
{
title: "Software Engineer",
category: "Engineering",
location: "New York"
},
{
title: "Web Developer",
category: "Engineering",
location: "Chicago"
},
{
title: "UX Designer",
category: "Design",
location: "New York"
}
]
This is the desired output (also note the alphabetically ordered categories):
Design
UX Designer
Engineering
Software Engineer
Web Developer
Any help would be much appreciated. Thanks!
Here is a working example with your data on plnkr using the filter solution from AngularJS Group By Directive without External Dependencies:
http://plnkr.co/edit/w5UJUN?p=preview
Do you have something like this in your mind:
<b>Design</b>
<ul>
<li ng-repeat="job in jobs | filter: { category:'Design'}">
{{job.title}}
</li>
</ul>
<b>Engineering</b>
<ul>
<li ng-repeat="job in jobs | filter: { category:'Engineering'}">
{{job.title}}
</li>
</ul>
There is working JSFiddle.
I would re-structure my JSON as follows:
$scope.parsedJobs = [];
angular.forEach($scope.jobs, function(value, key) {
var j = $scope.parsedJobs.filter(function(element) {
return element.category == value.category;
});
if (j.length > 0) {
$scope.parsedJobs[0].elements.push({
title: value.title,
location: value.location
});
} else {
$scope.parsedJobs.push({
category: value.category,
elements: [{
title: value.title,
location: value.location
}]
});
}
});
Fiddle

Create an unordered list n-levels deep using AngularJS

While going through some AngularJS examples, I see how easy it is to repeat and create structures. However, I couldn't figure out how to do the following.
Assume we have a json structure like
{
"Asia": {
"India": {
"Bangalore": {},
"Mumbai": {},
"New Delhi": {}
},
"China": {
"Beijing": {},
"Shanghai": {}
}
},
"Europe": {
"France": {
"Paris": {}
},
"Germany": {
"Berlin": {}
}
}
}
What I want to do is - Convert this JSON structure to an Unordered list - The depth of this kind of structure is not known, and can possibly go deeper. How do I perform repeats dynamically using Angular JS?
Your JSON is poorly structured, you're using property names to carry data.
What you really want is something like this:
$scope.continents = [
{
name: 'Asia',
countries: [
{
name: 'India',
cities: [
'Bangalore',
'Mumbai',
'New Delhi'
]
},
{
name: 'China',
cities: [
'Beijing',
'Shanghai'
]
},
]
},
{
name: 'Europe',
countries: [
{
name: 'France',
cities: [
'Peris'
]
},
{
name: 'Germany',
cities: [
'Berlin'
]
},
]
}
];
That said... what it sounds like you're looking to do is create a recursive tree directive of some sort. That gets a little tricky. You'll need to normalize your structure a bit so you can recursively examine it. Then you'll have to create two directives. One for a list, and one for an item:
Here is an example of what I mean...
function Item(name, items) {
this.name = name;
this.items = items || [];
}
app.controller('MainCtrl', function($scope) {
$scope.items = [
new Item('test'),
new Item('foo', [
new Item('foo-1'),
new Item('foo-2', [
new Item('foo-2-1'),
new Item('foo-2-2')
])
]),
new Item('whatever')
];
});
app.directive('tree', function() {
return {
template: '<ul><tree-node ng-repeat="item in items"></tree-node></ul>',
restrict: 'E',
replace: true,
scope: {
items: '=items'
}
};
});
app.directive('treeNode', function($compile) {
return {
restrict: 'E',
template: '<li>{{item.name}}</li>',
link: function(scope, elm, attrs) {
//MAGIC HERE!!!: this will do the work of inserting the next set of nodes.
if (scope.item.items.length > 0) {
var children = $compile('<tree items="item.items"></tree>')(scope);
elm.append(children);
}
}
};
});
In case anyone is interested in the "least-effort" way to do this without creating a directive (not that you shouldn't, but just offering a variation), here is a simple example:
http://jsbin.com/hokupe/1/edit
Also here's a blog post and a 10-15 minutes video on how it works:
http://gurustop.net/blog/2014/07/15/angularjs-using-templates-ng-include-create-infinite-tree/
Sample Code:
<script type="text/ng-template" id="treeLevel.html">
<ul>
<li ng-repeat="item in items">
<input type="checkbox"
name="itemSelection"
ng-model="item._Selected" />
{{item.text}}
<div ng-include=" 'treeLevel.html'"
onload="items = item.children">
</div>
</li>
</ul>
</script>
<div ng-include=" 'treeLevel.html' "
onload="items = sourceItems">
</div>

Resources