Custom Directives and Template doesn't work - html

I'm trying to make a custom directive that will show some elements that i previously declared in the MainController
HTML body:
<body ng-app="myApp" ng-controller="MainController">
<h1> Choose your Car </h1>
<div ng-repeat="car in cars">
<my-pattern info="car"></my-pattern>
</div>
<script src="js/MainController.js"></script>
<script src="js/myPattern.js"></script>
</body>
MainController:
angular.module('myApp').controller('MainController',function($scope) {
$scope.cars=[
{
icon: 'imgs/lamborghini.jpg',
name: 'Lamborghini',
price: 100000
},
{
icon: 'imgs/audi.png',
name: 'Audi',
price: 80000
}
];
});
Custom Directive
angular.module('myApp').directive('myPattern', function() {
return{
restrict: 'E',
scope: {
info: '='
},
templateUrl: 'js/directives/myPattern.html'
};
});
Template:
<img ng-src="{{info.icon}}">
<h2>{{info.name}}</h2>
<p>{{info.price}}</p>
If i don't use the directive but i just make the output whit the expressions <h2>{{car.name}}</h2> like this it work but with the custom directive it doesn't show me nothing.
I have all in different files.

This is working for me. There must be a issue with the location of your template with respect to your current HTML:
var app = angular.module("myApp", []);
angular.module('myApp').controller('MainController', function($scope) {
$scope.cars = [{
icon: 'imgs/lamborghini.jpg',
name: 'Lamborghini',
price: 100000
}, {
icon: 'imgs/audi.png',
name: 'Audi',
price: 80000
}];
});
angular.module('myApp').directive('myPattern', function() {
return {
restrict: 'E',
scope: {
info: '='
},
templateUrl: 'js/directives/myPattern.html'
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="MainController">
<h1> Choose your Car </h1>
<div ng-repeat="car in cars">
<my-pattern info="car"></my-pattern>
</div>
<script type="text/ng-template" id="js/directives/myPattern.html">
<img ng-src="{{info.icon}}">
<h2>{{info.name}}</h2>
<p>{{info.price}}</p>
</script>
</div>

I solved, i didn't synchronize the directives in the Custom Directive and in the body HTML. The code was right but i make a mistake with the calling script of the directive:
<script src="js/directives/myPattern.js"></script>

Related

How to dynamically change content of component with JSON?

I am creating my design portfolio using Vue CLI 3. The architecture of my website is very simple. I have a home page, about page, work page, and several individual project pages:
Home
About
Work
Project
Project
Project
The work page consists of several links that would click through to the individual project pages. The work component is set up like so:
<template>
<div>
<projectLink v-for="data in projectLinkJson" />
</div>
</template>
<script>
import projectLink from '#/components/projectLink.vue'
import json from '#/json/projectLink.json'
export default {
name: 'work',
data(){
return{
projectLinkJson: json
}
},
components: {
projectLink
}
}
</script>
As you can see, I'm importing JSON to dynamically render the content. Next, the projectLink component can be seen in the code block below. Within this component I am passing a param into <router-link> called projectName
<template>
<router-link :to="{ name: 'projectDetails', params: { name: projectName }}">
<h1>{{ title }}</h1>
</router-link>
</template>
<script>
export default {
name: 'projectLink',
props: {
title: String,
projectName: String
}
}
</script>
My routes.js file is setup like so:
const routes = [
{ path: '/', component: home },
{ path: '/about', component: about },
{ path: '/work', component: work },
{
path: "/work/:name",
name: "projectDetails",
props: true,
component: projectDetails
},
];
and my JSON is like so:
{
"0": {
"title": "test",
"projectName": "test"
}
}
Lastly, my projectDetails component is the component that is where I am having this issue:
<template>
<div>
<div
v-for="(data,index) in projectDetailsJson" v-if="index <= 1">
<h1>{{ data.title }}</h1>
<p>{{ data.description }}</p>
</div>
</div>
</template>
<script>
import json from '#/json/projectDetails.json'
export default {
name: 'projectDetails',
data(){
return{
projectDetailsJson: json
}
},
props: {
description: String,
title: String
}
}
</script>
I am successfully routing to the URL I want, which is /project/'name'. I want to use the projectDetails component as the framework for each of my individual project pages. But how do I do this dynamically? I want to retrieve data from a JSON file and display the correct object from the array based on the name that was passed to the URL. I do not want to iterate and have all of the array display on the page. I just want one project to display.
Quick solution:
projectDetails.vue
<template>
<div>
<div>
<h1>{{ projectDetails.title }}</h1>
<p>{{ projectDetails.description }}</p>
</div>
</div>
</template>
<script>
import json from '#/json/projectDetails.json';
export default {
name: 'projectDetails',
props: {
name: String,
},
data() {
return {
projectDetails: Object.values(json).find(project => project.title === this.name),
};
},
};
</script>
In my opinion, a better solution:
I don't get the idea that you keep project data in 2 separate JSON files. During compilation, both files are saved to the resulting JavaScript file. Isn't it better to keep this data in 1 file? You don't have to use all of your data in one place. The second thing, if you have a project listing then you can do routing with an optional segment, and depending on whether the segment has a value or not, display the listing or data of a particular project. Then you load project data only in one place, and when one project is selected, pass its data to the data rendering component of this project. Nowhere else do you need to load this JSON file.
routes.js
import home from '#/components/home.vue';
import about from '#/components/about.vue';
import work from '#/components/work.vue';
const routes = [
{path: '/', name: 'home', component: home},
{path: '/about', name: 'about', component: about},
{path: '/work/:name?', name: 'work', component: work, props: true},
];
export default routes;
work.vue
<template>
<div>
<project-details v-if="currentProject" :project="currentProject"/>
<projectLink v-else
v-for="project in projects"
v-bind="project"
v-bind:key="project.projectName"
/>
</div>
</template>
<script>
import projectLink from './projectLink';
import projectDetails from './projectDetails';
import json from '#/json/projectLink.json';
export default {
name: 'work',
props: {
name: String,
},
data() {
return {
projects: Object.values(json),
};
},
computed: {
currentProject() {
if (this.name) {
return this.projects.find(
project => project.projectName === this.name,
);
}
},
},
components: {
projectLink,
projectDetails,
},
};
</script>
projectDetails.vue
<template>
<div>
<div>
<h1>{{ project.title }}</h1>
<p>{{ project.description }}</p>
</div>
</div>
</template>
<script>
export default {
name: 'projectDetails',
props: {
project: Object,
},
};
</script>
projectLink.vue (changed only one line)
<router-link v-if="projectName" :to="{ name: 'work', params: { name: projectName }}">
A full working example:
Vue.component("navigation", {
template: "#navigation"
});
const Projects = {
template: "#projects",
props: ["projects"]
};
const Project = {
template: "#project",
props: ["project"]
};
const HomePage = {
template: "#home"
};
const AboutPage = {
template: "#about"
};
const WorkPage = {
data() {
return {
projects: [{
slug: "foo",
name: "Foo",
desc: "Fus Ro Dah"
},
{
slug: "bar",
name: "Bar",
desc: "Lorem Ipsum"
}
]
};
},
props: {
slug: String
},
template: "#work",
components: {
Projects,
Project
},
computed: {
currentProject() {
if (this.slug) {
return this.projects.find(project => project.slug === this.slug);
}
}
}
};
const router = new VueRouter({
routes: [{
path: "/",
name: "home",
component: HomePage
},
{
path: "/about",
name: "about",
component: AboutPage
},
{
path: "/work/:slug?",
name: "work",
component: WorkPage,
props: true
}
]
});
new Vue({
router,
template: "#base"
}).$mount("#app");
ul.nav {
list-style-type: none;
margin: 0;
padding: 0;
overflow: hidden;
background-color: #333;
}
ul.nav>li {
float: left;
}
ul.nav>li>a {
display: block;
color: white;
text-align: center;
padding: 14px 16px;
text-decoration: none;
}
ul.nav>li>a:hover {
background-color: #111;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue-router/3.1.3/vue-router.min.js"></script>
<div id="app"></div>
<script type="text/x-template" id="base">
<div id="app">
<div>
<navigation></navigation>
<router-view></router-view>
</div>
</div>
</script>
<script type="text/x-template" id="navigation">
<ul class="nav" id="navigation">
<li>
<router-link :to="{name: 'home'}">Home</router-link>
</li>
<li>
<router-link :to="{name: 'about'}">About</router-link>
</li>
<li>
<router-link :to="{name: 'work'}">Work</router-link>
</li>
</ul>
</script>
<script type="text/x-template" id="home">
<div id="home">This is Home Page</div>
</script>
<script type="text/x-template" id="about">
<div id="about">This is About Page</div>
</script>
<script type="text/x-template" id="work">
<div id="work">
<project v-if="currentProject" :project="currentProject"></project>
<projects v-else :projects="projects"></projects>
</div>
</script>
<script type="text/x-template" id="projects">
<div id="projects">
<ul>
<li v-for="project in projects" :key="project.slug">
<router-link :to="{name: 'work', params:{ slug: project.slug}}">{{project.name}}</router-link>
</li>
</ul>
</div>
</script>
<script type="text/x-template" id="project">
<div id="project">
<h2>{{project.name}}</h2>
<p>{{project.desc}}</p>
</div>
</script>
Great work thus far, Austin! You're very close having this working. There are a few different ways you could parse out the correct data from your JSON file into the projectDetails component, but I'll just demo my preferred way.
First, you're going to need a bit of vanilla JS to search through your JSON file and return only the row that you want. I would do this as a method since the data isn't going to be changing or requiring the component to re-render. So, after your props, I would add something like this:
methods: {
findProject(projectName) {
return Object.values(json).find(project => project.title === projectName)
}
}
Note that this is going to return the first project that matches the project name. If you have projects with the exact same project name, this won't work.
Next, you'll just need to update the default value of projectDetailsJson to call this method and pass the route's project name. Update data with something like this:
data() {
return {
projectDetailsJson: this.findProject(this.$route.params.name)
}
}
If that doesn't work, we may need to set the projectDetailsJson in the created lifecycle hook, but try the above code first.
If I understood correctly, you want to keep a parent component as a layout for all of your page?
If I always understood correctly, you must use the children property of vuerouter
https://router.vuejs.org/guide/essentials/nested-routes.html
import layout from 'layout';
const projectRoute = {
path: '/project',
component: Layout, // Load your layout
redirect: '/project/list',
name: 'Project',
children: [
{
path: "list", // here the path become /project/list
component: () => import('#/views/project/List'), // load your components
name: "List of project",
},
{
path: "detail/:id",
component: () => import('#/views/project/Detail'),
name: "Detail of project",
}
],
};
So you can create your layout and add everything you want, this will be available on all child components, and you can use $emit, $refs $props ect...
+
You can create an file routes/index.js and create folder routes/modules . Inside this, you can add your routes/modules/project.js and load the modules in routes/index.js
import Vue from 'vue';
import VueRouter from 'vue-router';
Vue.use(VueRouter);
import projectRoutes from "./modules/project";
const routes = [
projectRoutes,
{
// other routes....
},
]
export default new VueRouter({
routes,
mode: 'history',
history: true,
});
#see the same doc : https://router.vuejs.org/guide/essentials/nested-routes.html
Finally, you just have to do the processing on the layout, and use the props to distribute the values ​​both in detail and in the project list; and use the filter methods described just above
I hope I have understood your request, if this is not the case, let me know,
see you
Edit: Here is a very nice architecture with vue, vuex and vuerouter. maybe inspire you
https://github.com/tuandm/laravue/tree/master/resources/js
For everyone, to take this one step further. How would you show only the projectLinks that match the current URL? So if I have three different JSON projectTypes: design, code, motion. If the URL contains motion in it, how do I filter my projectLink components to show only those that have a matching JSON value of either design, code or motion. Essentially I'm just trying to filter.

How to access child scope?

In the below code, I'm trying to get a console log in the Directive (child scope),I need to get scope details.I tried adding a scope variable to the function in the directive also, but didn't work.
How can I fix this?
myDirective.html
<html ng-app="MyAppdr">
<head>
<script src="angular.js"></script>
<script src="appdr.js"></script>
</head>
<body ng-controller="dirCtrl">
<h1>hello</h1>
<employee-card></employee-card>
<!--div employee-card></div>
<div class="employee-card"></div--->
</body>
<html>
employee_info.html
<b>{{employee.name}}</b> - {{employee.job}}
<br/><br/>
<div ng-show='!!employee.followers'>
followers
<ul>
<li ng-repeat='name in employee.followers'>{{name}}</li>
</ul>
<button ng-click="follow('Galaa')">follow</button>
</div>
</div>
appdr.js
name="MyAppdr";
requires=[];
appdr=angular.module(name,requires);
appdr.controller("dirCtrl",function($scope){
console.log("This is controller dirCtrl");
$scope.employee={
name:"subo",
job:"cat",
followers:["chin","adyakshaka","aluu"]
}
console.log("parent ",$scope);
/*
$scope.follow=function(name){
$scope.employee.followers.push(name);
}
*/
});
appdr.directive("employeeCard",function(){
//$scope.employee={};
console.log("child ",$scope);
return{
templateUrl:'employee_info.html',
restrict:"AEC",
//replace:true,
controller:function($scope){
$scope.follow=function(name){
$scope.employee.followers.push(name);
}
},
scope:true
}
});
For your particular case, using scope: false seems to be sufficient if you are not showing multiple cards in same page.
appdr.directive("employeeCard",function() {
return {
scope: false,
// other attributes
If you need to show multiple cards in same page, use isolated scope and pass in
appdr.directive("employeeCard",function() {
return {
scope: {
employee: '='
},
// other attributes
<employee-card employee="employee"></employee-card>
Move the console.log() inside the controller of the directive.
appdr.directive("employeeCard", function() {
return {
templateUrl: 'employee_info.html',
restrict: "AEC",
//replace:true,
controller: function($scope) {
console.log("child ", $scope);
$scope.follow = function(name) {
$scope.employee.followers.push(name);
}
},
scope: true
}
});
If you will have only one employee card on the page then keep scope: true else if you need to show multiple cards in the same page, use an isolated scope and pass it in the template.
appdr.directive("employeeCard", function() {
return {
templateUrl: 'employee_info.html',
restrict: "AEC",
//replace:true,
controller: function($scope) {
console.log("child ", $scope);
$scope.follow = function(name) {
$scope.employee.followers.push(name);
}
},
scope: {
employee: "="
}
}
});
And in the html use something like
<employee-card employee="employee"></employee-card>
Refer the documentation of directive here

when using routeparams in angular,templateurl not working?

https://plnkr.co/edit/oo05d6H6AxuJGXBAUQvr?p=preview
I have created an array of items and when I click on each item details page will be displayed ,for all the items in the array I have used same details page,can anyone look at my plunker and explain why the templateURL is not working when I click on an item?
var app = angular.module("myApp", ["ngRoute"]);
app.controller('mobileController', function($scope) {
$scope.items = [{
name: 'Iphone',
}, {
name: 'Oneplus'
}, {
name: 'Moto'
}];
});
app.config(function($routeProvider) {
$routeProvider
.when('/item/:itemName', {
templateUrl: 'details.html',
controller: 'ItemCtrl'
});
app.controller('ItemCtrl', ['$scope', '$routeParams',
function($scope, $routeParams) {
$scope.itemName = $routeParams.itemName;
}
]);
});
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-route.js"></script>
<script src="script.js"></script>
<body ng-app="myApp" ng-controller="mobileController">
<h2> Welcome to Mobile Store</h2>
<p>Search:<input type="text" ng-model="test"></p>
<ul>
<li ng-repeat="item in items|filter:test">{{ item.name }}
</li>
</ul>
<div ng-view></div>
</body>
</html>
here is my details page
<!DOCTYPE html>
{{itemName}}
is it because of a mismatch?
.when('/item/:itemName', {
a href="/items/{{item}}"
there's an extra s there
Summary of problems:
Your ItemCtrl is currently defined inside your module's config function. Move it out of there
app.config(function($routeProvider) {
$routeProvider
.when('/item/:itemName', {
templateUrl: 'details.html',
controller: 'ItemCtrl'
});
}); // you were missing this
app.controller('ItemCtrl', ['$scope', '$routeParams',
Your route is /item/:itemName and since you're not using HTML5 mode, you need to create your href attributes with a # prefix. For example
ng-href="#/item/{{item.name}}"
Fixed demo here ~ https://plnkr.co/edit/rKHsBMFcXqJUGp8Blx7Q?p=preview

AngularJS Move to nested abstract view

I have nested abstract view in my angular js project.
I got Error : Cannot transition to abstract state 'main.middle' when i move to nested abstract view.
My html code is as below:
<!DOCTYPE html>
<html ng-app="nesting">
<head>
<script data-require="angular.js#*" data-semver="1.3.0-beta.5" src="https://code.angularjs.org/1.3.0-beta.5/angular.js"></script>
<script data-require="ui-router#*" data-semver="0.2.10" src="https://rawgit.com/angular-ui/ui-router/0.2.10/release/angular-ui-router.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-controller="testController">
href:
<br />
#/alpha
#/beta
#/gama
<button ng-click="moveToMiddle()">move to middle</button>
<br />
ui-sref:
<br />
<a ui-sref="main.middle.alpha">main.middle.alpha</a>
<a ui-sref="main.middle.beta">main.middle.beta</a>
<a ui-sref="main.middle.gama">main.middle.gama</a>
<hr />
<div ui-view=""></div>
<script>
'use strict';
var $urlRouterProviderRef = null;
var $stateProviderRef = null;
var app = angular.module('nesting', [
'ui.router'
]);
app.config(function( $urlRouterProvider, $stateProvider) {
$urlRouterProvider.otherwise('/alpha');
$stateProvider
.state('main', {
url: "",
abstract: true,
template: '<div><h3>Main</h3><div ui-view=""></div></div>',
})
.state('main.middle', {
url: "",
abstract: true,
template: '<div><h4>Middle</h4><div ui-view=""></div></div>',
})
.state('main.middle.alpha', {
url: "/alpha",
template: '<div><h5>The leaf: {{state.name}}</h5></div>',
controller: function ($scope, $state){
$scope.state = $state.current;
},
})
.state('main.middle.beta', {
url: "/beta",
template: '<div><h5>The leaf: {{state.name}}</h5></div>',
controller: function ($scope, $state){
$scope.state = $state.current;
},
})
.state('main.middle.gama', {
url: "/gama",
template: '<div><h5>The leaf: {{state.name}}</h5></div>',
controller: function ($scope, $state){
$scope.state = $state.current;
},
})
;
});
app.controller('testController', function ($scope, $state) {
$scope.moveToMiddle = function () {
$state.go('main.middle');
}
})
</script>
</body>
</html>
When I click on move to middle button I got error.
How to move to abstract view?
I have referred this but its not useful in my case.
You never go to an abstract state. From the docs:
An abstract state can have child states but can not get activated itself. An 'abstract' state is simply a state that can't be transitioned to. It is activated implicitly when one of its descendants are activated.
If you define main.middle.alpha, main.middle.beta, and main.middle.gama as not abstract, you can transition to those.
As mentioned in angualr docs
An abstract state can have child states but can not get activated
itself. An 'abstract' state is simply a state that can't be
transitioned to. It is activated implicitly when one of its
descendants are activated.
app.controller('testController', function ($scope, $state) {
$scope.moveToMiddle = function () {
$state.go('main.middle.alpha');
}
as abstract state cannot be instantiated and can't viewed. if you want to access then remove abstract line

Cannot get separate views to appear in index.html, and controller is also not working

I'm just starting out with Angular and most of programming in general. I'm trying to make a separate view1.html file appear on my index.html page but it won't, so I'm assuming it's a routing problem. I tried pasting the view1.html content in the body of my index.html to test it and it wasn't showing the controller content either. I'm sure they're simple mistakes but I can't find them. view.html is in a separate folder called views. I only have the javascript in the index.html page for convenience.
index.html
<!DOCTYPE html>
<html lang="en" ng-app='demoApp'>
<head>
<meta charset="UTF-8">
<title>First Angular App</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.11/angular.min.js"></script>
<body>
<div>
<div ng-view></div>
</div>
<script>
// create module called "demoApp" under the variable name "demoApp"
var demoApp = angular.module('demoApp', []);
// ROUTING
demoApp.config(function ($routeProvider) {
$routeProvider
.when ('/',
{
controller: 'SimpleController',
templateUrl: 'views/view1.html'
})
.when('/view2',
{
controller: 'SimpleController',
templateUrl: 'views/view2.html'
})
.otherwise({ redirectTo: '/' });
});
// CONTROLLERS
demoApp.controller('SimpleController', function ($scope) {
$scope.customers = [
{ name: 'Caleb', city: 'Indianapolis' },
{ name: 'Samantha', city: 'Zionsville' },
{ name: 'Tim', city: 'Palo Alto' }
];
$scope.addCustomer = function () {
$scope.customers.push(
{
name: $scope.newCustomer.name,
city: $scope.newCustomer.city
});
};
}
</script>
</body>
</html>
view1.html
<h2>View 1</h2>
Name:
<input type="text" ng-model="name" />
<ul>
<li ng-repeat="cust in customers"></li>
</ul>
Customer Name:
<input type="text" ng-model="newCustomer.name" />
<br>Customer City:
<input type="text" ng-model="newCustomer.city" />
<br>
<button ng-click="addCustomer()">Add Customer</button>
View 2
</div>
You need to include the script for angular's router.
<head>
<meta charset="UTF-8">
<title>First Angular App</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.11/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.11/angular-route.min.js"></script>
Also, looks like you're missing a closing </head> tag.
Here's a working version of your HTML file:
<!DOCTYPE html>
<html lang="en" ng-app='demoApp'>
<head>
<meta charset="UTF-8">
<title>First Angular App</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.11/angular.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.11/angular-route.js"></script>
</head>
<body>
<div>
<div ng-view></div>
</div>
<script>
// create module called "demoApp" under the variable name "demoApp"
var demoApp = angular.module('demoApp', ['ngRoute']);
// ROUTING
demoApp.config(function ($routeProvider) {
$routeProvider
.when ('/',
{
controller: 'SimpleController',
templateUrl: 'view1.html'
})
.when('/view2',
{
controller: 'SimpleController',
templateUrl: 'view2.html'
})
.otherwise({ redirectTo: '/' });
});
// CONTROLLERS
demoApp.controller('SimpleController', function ($scope) {
$scope.customers = [
{ name: 'Caleb', city: 'Indianapolis' },
{ name: 'Samantha', city: 'Zionsville' },
{ name: 'Tim', city: 'Palo Alto' }
];
$scope.newCustomer = { name: "", city: ""};
$scope.addCustomer = function () {
$scope.customers.push(
{
name: $scope.newCustomer.name,
city: $scope.newCustomer.city
});
};
});
</script>
</body>
</html>