Angularjs - Array items dependent property not updating - angularjs

I have this scope array variable
$scope.menuItems = [
{
name: 'Login',
url:'#/login',
isAvailable: true
},
{
name: 'Register',
url: '#/register',
isAvailable: $scope.global.roleId == null
},
{
name: 'My Restaurants',
url: '#/myrestaurants',
isAvailable: $scope.global.roleId == constants.OWNER_USER_ROLE_ID
},
{
name: 'Create Restaurant',
url: '#/createrestaurant',
isAvailable: $scope.global.roleId == constants.OWNER_USER_ROLE_ID
},
{
name: 'Logout',
url: '#/logout',
isAvailable: $scope.global.roleId != null
}
];
This object is dependent on another scope variable $scope.global.roleId
I am loading menu from this array like this
<ul class="nav navbar-nav">
<li ng-repeat="n in menuItems | filter:{isAvailable:true}">
{{n.name}}
</li>
</ul>
And I want it to update menus automatically when $scope.global.roleId is updated.
To be noted, I am updating this variable $scope.global.roleId from a child controller and this field is getting updated properly but it is not affecting the array field isAvailable.
I checked both the variables $scope.global.roleId and $scope.menuItems in log and $scope.global.roleId is updating properly everytime but not $scope.menuItems.isAvailable field which is dependent on former
What wrong am I doing or expecting? And what is the right way to achieve this?

Achieved it like this, converting variable to property
{
name: 'My Restaurants',
url: '#/myrestaurants',
get isAvailable() {
return $scope.global.roleId == constants.OWNER_USER_ROLE_ID;
}
},

Related

vuejs ajax request using a v-model in a v-for

I have a set of data (todos array) that displays in my model, and I am rendering this data in a list.
I am trying to implement a functionality on the list that whenever click on any item on the list, the selected variable's value should be updated with that item'sforumID, and whenever a different item is clicked the selected variable should update accordingly.
I tried adding a v-model, but that breaks. So need some direction on how can I achieve this.
new Vue({
el: "#app",
data: {
myId:"",
selected:"",
todos: [{"ForumId":41830,"Name":"test","Description":{"Text":"","Html":""},"ShowDescriptionInTopics":false,"AllowAnonymous":false,"IsLocked":false,"IsHidden":false,"RequiresApproval":false,"MustPostToParticipate":false,"DisplayInCalendar":false,"DisplayPostDatesInCalendar":false,"StartDate":null,"EndDate":null,"PostStartDate":null,"PostEndDate":null,"StartDateAvailabilityType":null,"EndDateAvailabilityType":null},{"ForumId":41863,"Name":"new forum","Description":{"Text":"","Html":""},"ShowDescriptionInTopics":false,"AllowAnonymous":false,"IsLocked":false,"IsHidden":false,"RequiresApproval":false,"MustPostToParticipate":false,"DisplayInCalendar":false,"DisplayPostDatesInCalendar":false,"StartDate":null,"EndDate":null,"PostStartDate":null,"PostEndDate":null,"StartDateAvailabilityType":null,"EndDateAvailabilityType":null}]
},
methods: {
myMethod1() {
var vm = this;
$.ajax({
url: "https://example.com/" + vm.myId +"/blogs/"+selected+"/topics/",
type: 'Get',
headers: {
accept: "application/json;odata=verbose"
},
success: function (data) {
console.log(data);
vm.Topics=data
}
})
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<h2>Todos:</h2>
<li v-for="(item, index) in todos">{{item.Name}}
</li>
</div>
We can use the v-model directive to create two-way data bindings on form input, textarea, select elements, and custom components.
The following error in your code also proves the above definition.
<a v-model="selected">: v-model is not supported on this element type.
If you are working with contenteditable, it's recommended to wrap a
library dedicated to that purpose inside a custom component.
Now, as you said, " item on the list when clicked, grabs the forumID found in the dataset and replaces the selected value with that forumID changing whenever a different item in the list is clicked"
This simply stated that you only want to update the selected variable with the currently clicked item's forumID which directly means-
no two-way-binding is required == no v-model is required.
So, why not use only a click event to update the selected variable?
Below is the demo in which when you click on any list item, the selected variable will update with that item's forumID.
new Vue({
el: "#app",
data() {
return {
myId: "",
selected: "",
todos: [{
ForumId: 41830,
Name: "test",
Description: {
Text: "",
Html: ""
},
ShowDescriptionInTopics: false,
AllowAnonymous: false,
IsLocked: false,
IsHidden: false,
RequiresApproval: false,
MustPostToParticipate: false,
DisplayInCalendar: false,
DisplayPostDatesInCalendar: false,
StartDate: null,
EndDate: null,
PostStartDate: null,
PostEndDate: null,
StartDateAvailabilityType: null,
EndDateAvailabilityType: null,
},
{
ForumId: 41863,
Name: "new forum",
Description: {
Text: "",
Html: ""
},
ShowDescriptionInTopics: false,
AllowAnonymous: false,
IsLocked: false,
IsHidden: false,
RequiresApproval: false,
MustPostToParticipate: false,
DisplayInCalendar: false,
DisplayPostDatesInCalendar: false,
StartDate: null,
EndDate: null,
PostStartDate: null,
PostEndDate: null,
StartDateAvailabilityType: null,
EndDateAvailabilityType: null,
},
],
};
},
methods: {
myMethod1() {
var vm = this;
$.ajax({
url: "https://example.com/" + vm.myId + "/blogs/" + vm.selected + "/topics/",
type: 'Get',
headers: {
accept: "application/json;odata=verbose"
},
success: function(data) {
console.log(data);
vm.Topics = data
}
})
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<h2>Todos:</h2>
<div style="margin-bottom:20px;">Click on any item</div>
<li v-for="(item, index) in todos" :key="index">
{{item.Name}}
</li>
<div v-if="selected" style="margin-top:20px;">
Updated selected variable - {{selected}}
</div>
</div>
Try this also-
I called two actions together on the anchor tag's click, First, update the selected variable, and second, call myMethod1 function.
You can also call myMethod1 and pass forumId to it and update the selected variable inside it-
{{item.Name}}
And
myMethod1(forum_id) {
var vm = this;
vm.selected = forum_id
// YOUR REST CODE HERE
}

In angular formly, how to add ng-click or ng-change to a checkbox?

I am using angular-formly, I am trying to add the normal angular events to a checkbox but no joy. Here is what I have:
{
type: "checkbox",
key: "is_active",
templateOptions: {
type: "",
label: "Is Active"
}
}
I looked over the documentations over and over and I can not find the solution. Please show where to add ng-click or ng-change in the above.
I hoped something like the below would work:
{
type: "checkbox",
key: "is_active",
templateOptions: {
type: "",
label: "Is Active"
},
ngClick : "functionName"
}
where functionName is a function inside the controller that renders the form. So my functions will always be in my controller, I just need to define or pass the event to the checkbox
It's been awhile since I've messed with formly but I believe this should do
{
type: "checkbox",
key: "is_active",
templateOptions: {
type: "",
label: "Is Active",
onClick: yourControllerFunctionHere //notice this isn't a string but a reference to your controller function
}
}
This is referenced in the ngModelAttrsTemplateManipulator documentation. I love formly but the documentation is hard to navigate
use ngModelElAttrs attribute:
{
type: "checkbox",
key: "is_active",
templateOptions: {
label: "Is Active"
},
ngModelElAttrs: {
'ng-change': "model.text = 'asdf'"
}
}
in this fiddle (http://jsbin.com/xavitufudu/1/edit?js,output) I added your checkbox as a first field, which on change changes next field's content.
this approach does not have direct access to outer scope, but rather to formly's scope. In order to reach an outer scope we can add field's controller, which may access outer scope (variable functions):
{
type: "checkbox",
key: "is_active",
templateOptions: {
label: "Is Active"
},
ngModelElAttrs: {
'ng-change': "myFunc()"
},
controller: function ($scope) {
$scope.myFunc = function () {
vm.model.text = 'Another text';
};
}
}
here, we access vm.model.text via outer scope (although we could also access it via inner field's scope as $scope.model.text)

mapping similar urls to same states based on an optional parameter (angular-ui-router)

I have the below states, which I would like to map to the same routes, which would then have a parameter.
/en/live/
/en/
/en/live/football
/en/football
Basically, the 'live' part needs to be stored in some kind of variable. I've tried something like:
$stateProvider.state('language.live-lobby', {
url: "/en/{liveStatus:live|}/football"
}
However, it does not let you specify an empty parameter. Basically, with the above state, /en/live/football matches while /en/football doesn't. If that worked, I could then read the liveStatus parameter.
Is it possible, without having to define multiple states? I would like to avoid having to create multiple states, as they all share the same information like views, data & resolve?
There is a working plunker
Based on these:
Angular js - route-ui add default parmeter
Prepend optional attribute in angular ui-router URL
we can use this state def:
.state('language.live-lobby', {
templateUrl: 'tpl.html',
//url: "/en/{liveStatus:live|}/football",
url: "/{lang:(?:en|cz|de)}" +
"/{liveStatus:(?:live|podcast)}" +
"/{sport:(?:football|golf)}",
params: {
lang: { squash: true, value: 'en' },
liveStatus: { squash: true, value: 'live' },
sport: { squash: true, value: 'football' },
}
})
All these links would work as expected:
// these follow defaults
<a href="#/en/live/">
<a href="#/en/">
<a href="#/en/live/football">
<a href="#/en/football">
//here we pass some non default
<a href="#/cz/podcast/">
<a href="#/cz/">
<a href="#/cz/podcast/golf">
<a href="#/cz/golf">
Check it here

angular js ng-class shows expression as class instead of processing it

I'm trying to make highlighted menu items by using angular js. I've read this question and tried implementing the anwser, but instead of angular evaluating the expression, it just shows it as the class name. I don't know what's going on.
I have the menu items listed as JSON, and the iterate trough it with ng-repeat. Once the list items are created, I want the angular to add a class of 'active', if the location url is the same as the link.href attribute of a menu item (it's a json attribute, not the html one).
Here's the relevant html:
<div class="header" ng-controller="NavbarController">
<ul>
<li ng-repeat="link in menu" ng-class="{ active: isActive({{ link.href }}) }"><a ng-href="{{ link.href }}">{{ link.item }}</a>
</li>
</ul>
</div>
and my controller:
.controller('NavbarController', function ($scope, $location) {
// navbar links
$scope.menu = [
{
item: 'PTC-Testers',
href: '#/PTC-Testers'
},
{
item: 'articles',
href: '#/articles'
},
{
item: 'PTC sites',
href: '#/sites'
},
{
item: 'account reviews',
href: '#/account_reviews'
},
{
item: 'forum',
href: '#/forum'
},
{
item: 'contact us',
href: '#/contact'
},
{
item: 'login',
href: '#/login'
}
]; // end $scope.menu
$scope.isActive = function (viewLocation) {
return viewLocation === $location.path();
};
});
This is the navbar part of a bigger project, and I tried only inserting the relevant code. If you need further info to understand the question properly, please let me know.
It should be ng-class="{'active' : isActive(link.href)}"
You didn't end the curly brace in ng-class and its better to put class name inside quotes

Using Factory to Display Navigation for Application?

I am still learning so any advice or improvements would be appreciated! I am looking to build a Navigation for my Angular App using the Service i built if its usable.
Quick Summary of Functionality:
User MouseOver Parent Item
Submenu Items Display under Active Parent Item
Here is what i have so far for my Service Structure:
app.factory("NavigationService", function () {
var e = [{
title: "Parent1",
type: "parentitem",
children: [{
title: "SubofParent",
type: "subitem",
href: "/location"
}]
}, {
title: "Parent2",
type: "parentitem",
children: [{
title: "SubofParent",
type: "subitem",
href: "/location"
}]
}, {
title: "Parent3",
type: "parentitem",
children: [{
title: "SubofParent",
type: "subitem",
href: "/location"
}]
}
}];
});
As you can see its a very simple structure but what i would like to know is how i can use this structure to ng-repeat the Parent Items and then display the SubItems on Hover over specific parent item, if that makes sense?
I am unsure how to build the controller to handle this method.
Any Advice on how to achieve this would be great.
<li ng-repeat="item in data" >
<ul ng-mouseover="isChildrenVisible=true" ng-mouseleave= "isChildrenVisible=false"> {{item.title}}</ul> //parent element
<ul ng-show="isChildrenVisible">
<li ng-repeat="child in item.children "> //child element
<p>{{child.title}}</p>
</li>
</ul>
</li>
Here I make child element hidden by default (initialize isChildrenVisible varible to false). when user mouse over the parent element then I will make isChildrenVisible=true and on mouse leave i make this varible false.

Resources