Why is my ng-repeat directive creating an invalid HTTP request? - angularjs

Using Anguar UI-Router, I have the following state configured:
angular.module('foos').config(['$stateProvider',
function($stateProvider) {
$stateProvider.
state('seeFoos', {
url: '/foos',
templateUrl: 'modules/foos/client/views/list-foos.client.view.html',
controller: 'FoosController',
resolve: {
initialData : function(Foos) {
return Foos.query();
}
}
});
}
]);
In my view, I use ng-repeat to load some images by getting the image name from a service.
<section>
<div class="row">
<div class="col-xs-12 col-sm-6 top15"
ng-repeat-start="foo in bar">
<h4 ng-bind="foo.name"></h4>
<div class="row">
<a href="{{foo.link}}">
<img alt="{{foo.name}}" src="modules/foos/client/img/{{foo.imgName}}" class="col-xs-4" />
</a>
</div>
</div>
<div class="clearfix" ng-if="$index%2==1"></div>
<div ng-repeat-end=""></div>
</div>
</section>
Note that I ensure Foos.query() resolves before the template is loaded. The controller then loads the result to bar on the scope
angular.module('foos').controller('FoosController', ['$scope', '$stateParams', '$location', 'Foos', 'initialData',
function($scope, $stateParams, $location, Foos, initialData) {
$scope.bar = initialData;
}
]);
Everything works as expected, save for the following extra HTTP GET that returns a 404 error:
GET /modules/foos/client/img/%7B%7Bfoo.imgName%7D%7D
I don't understand why that request is being generated once I add the resolve option on the state configuration.

The main problem can be solved by using ng-src instead of src. From the documentation...
Using Angular markup like {{hash}} in a src attribute doesn't work right: The browser will fetch from the URL with the literal text {{hash}} until Angular replaces the expression inside {{hash}}. The ngSrc directive solves this problem.
Secondly, your resolve function does not wait for the query to complete before completing the state load. This is by design in $resource actions...
It is important to realize that invoking a $resource object method immediately returns an empty reference (object or array depending on isArray). Once the data is returned from the server the existing reference is populated with the actual data.
If you do want it to wait, change the resolve to
resolve: {
initialData : function(Foos) {
return Foos.query().$promise;
}
}
And finally, you can simply use ng-if="$odd" instead of ng-if="$index%2==1".

Related

data transfer between two angularjs controllers

I want to change the iframe source on runtime
<div class="pp lsv-video pp-player" id="rs" ng-controller="ctrl2">
<input type="text" style="width:0px;height:0px;display:none;" />
<iframe src="" class="lsv" marginheight="0" marginwidth="0" frameborder="0"></iframe>
</div>
when user clicks on any of the (mentioned below), teh data mentioned b.VideoSrc should be transferred to the different controller Ctrl2 and iframe source has to be changed.
<ul ng-controller="ctrl1">
<li ng-repeat="b in KeynoteSessions | filter:isBD">
<a href='#rs' class="fancybox" name='{{b.VideoSrc}}'>
<img src='{{b.ImageSrc}}' width='{{b.ImageWidth}}' height='{{b.ImageHeight}}' alt='{{b.ImageAlt}}' /><br />
{{b.Text}}
</a>
</li>
</ul>
please help me to achieve this, thanks!
there are many ways
1.You can create services and use common services to share data.
2.you can use rootscope variable.
3.angularjs $emit, $broadcast methods you can use
like
myApp.factory('Data', function () {
return { FirstName: '' };
});
myApp.controller('FirstCtrl', function ($scope, Data) {
$scope.Data = Data;
});
myApp.controller('SecondCtrl', function ($scope, Data) {
$scope.Data = Data;
});
http://jsfiddle.net/HEdJF/
check this one:Share data between AngularJS controllers
Usually I'm putting related content in the same controller (Youtube frame and "remote" together for exemple) but sometime I can't, so I pass the data through a Javascript Variable (Dont forget that your var need to be defined outside your controller )

How to prevent losing data when moving from one state to another in multi-step form?

I am new to web programming and especially to AngularJS.
So maybe my question will seem naive to some of you.
I'm developing a single page application using angular-ui-router.
I have created a multi-step form that contains 3 states:
(function () {
"use strict";
angular.module("sensorManagement", ["ui.router", "ngAnimate", "ngResource", "toaster"])
.config(["$stateProvider", "$urlRouterProvider", function ($stateProvider, $urlRouterPorvider) {
$urlRouterPorvider.otherwise("/Sensor/Home");
$stateProvider
.state("MultiStepForm", {
url: "/Sensor/MuiltyStepForm",
templateUrl: "/app/MultiStepForm/MuiltyStepForm.html",
})
.state('MultiStepForm.step1', {
url: '/step1',
templateUrl: '/app/MultiStepForm/FormStep1.html'
})
.state('MultiStepForm.step2', {
url: '/step2',
templateUrl: '/app/MultiStepForm/FormStep2.html'
})
.state('MultiStepForm.step3', {
url: '/step3',
templateUrl: '/app/MultiStepForm/FormStep3.html'
});
}]);
})();
Here is the HTML code:
<!-- form.html -->
<div class="row">
<div class="col-sm-8 col-sm-offset-2">
<div id="form-multiple-step">
<div class="page-header text-center">
<!-- the links to our nested states using relative paths -->
<!-- add the active class if the state matches our ui-sref -->
<div id="status-buttons" class="text-center">
<a ui-sref-active="active" ui-sref=".step1"><span>STEP1</span></a>
<a ui-sref-active="active" ui-sref=".step2"><span>STEP2</span></a>
<a ui-sref-active="active" ui-sref=".step3"><span>STEP3</span></a>
</div>
</div>
<form id="single-form">
<!-- our nested state views will be injected here -->
<div id="form-views" ui-view></div>
</form>
</div>
</div>
</div>
As you can see I have 3 states and each state has it's own view. The views have multiple elements (textboxes and checkboxes).
When the user enters some data for the STEP1 view and moves to the next step (STEP2 or STEP3) then at some point goes back to STEP1 all data is deleted. I checked with fiddler and can see that when I move from one state to another a call is made to the server and a new view generated.
My question is how can I prevent the lose of data when I move from one state to another? Do I have to use caching? Or maybe there is another way to prevent server calls and keep the data alive until the submit button clicked.
When you feel you have to save data across your controllers in your app. you should use a service.
app.factory('sharedDataService', function ($rootScope) {
console.log('sharedDataService service loaded.');
// do something here that can be shared in other controller
});
//pass this service in your controller to use it there
app.controller('Controller2', ['$scope', 'sharedDataService', function ($scope, sharedData) {
console.log('Controller2 controller loaded.');
//get data from shared service
}]);
find a useful Fiddle here
Cheers if it helps!
I think what you need to do is share you $scope between the parent and child stats. here is the stackoverflow post with good example. https://stackoverflow.com/a/27699798/284225

angularjs: how to invoke controller based on his parent results

I'm moving my very first steps with angularjs.
I have a controller which does a call to a service, which returns a list of urls (parent).
I would like to render a html ul for which each li is rendered by another controller (children) with its own template. I imagine something like this:
<ul ng-controller="ListCtrl">
<li ng-repeat="element in elements">
<div ng-controller="DetailCtrl">
{{oneField}} - {{anotherField}}
</div>
</li>
</ul>
The first controller is easy to implement:
myApp.controller('ListCtrl', function ($scope, $http) {
$http.get('services/elements')
.success(function(data) {
$scope.elements = data;
});
});
But for the second I have a problem, since I can't figure out how the controller could know which url use for the ajax call (DYNAMYC_URL):
myApp.controller('DetailCtrl', function ($scope, $http) {
$http.get(DYNAMYC_URL)
.success(function(data) {
$scope.element = data;
});
});
Could you please suggest me which is the best way in angular to approach this problem?
I've also considered to do many calls inside the first controller, but it doesn't seem a clean solution to me.
We will try to maintain the state of childRenderUrl function call in element while
initializing the element itself in ng-unit attribute.
Instead of DetailCtrl
We write a function renderChild defined as
$scope.renderChild =function(element)
{
$http.get(element.url)
.success(function(data) {
// add all required fields to element here
element.otherField=data.otherField;
element.sometherField=data.sometherField;
//a $scope.$digest may be required here
});
}
Your display logic would be
<ul ng-controller="ListCtrl">
<li ng-repeat="element in elements">
<div ng-init="renderChild(element)">
{{element.oneField}} - {{element.anotherField}}
</div>
</li>
</ul>
On initializing we would call the renderChild method which would
add two properties to the element object that would be aptly displayed by
expressions {{element.oneField}} - {{element.anotherField}}

AngularJS Trying to use ng-click with ng-switch but ng-switch is not switching my divs

AngNoob here. I have some global navigation that uses the routeProvider to swap out external html pages inside the view. Within the view i set up a list type sub navigation (created with ng-repeat) that switches out divs in the external html file. I can get it to load up the page if I set it manually in the appCtrl:
//Here I set the initial value
$scope.page = 'Comfort Homes of Athens';
But when I click on the span that has the ng-click. I get nothing. I started to think it was a scope issue but when i put just an ng-click='alert()' it does nothing either.
I have read around other posts but most seem to be putting a ng-click inside of an ng-switch rather than the reverse. and aren't using routing in their examples either. Still new to angular so maybe its something I haven't come across yet.
App HTML:
<body ng-app="app">
<header ng-include="header.url" ng-controller="nav"></header>
<article ng-view></article>
<footer ng-include="footer.url" ng-controller="nav"></footer>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.16/angular.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.16/angular-route.js"></script>
<script type="text/javascript" src="js/data.js"></script>
<script type="text/javascript" src="js/model.js"></script>
</body>
External HTML File:
<div id="web" class="wrapper">
<aside class="boxModel">
<div id="controller" class="container">
<div class="topBox bluebg subNavBar"><h1 class="white">Projects</h1></div>
<div ng-controller="nav" id="controls" class="botBox whitebg">
<span ng-repeat='item in webProjects' ng-click="page='{{item.name}}'">{{item.name}}</span>
</div>
</div>
</aside><section ng-switch on="page" class="boxModel">
<div ng-switch-when="Comfort Homes of Athens" id="sandbox" class="container round box whitebg">
<h1>Here is link 1</h1>
</div>
<div ng-switch-when="Sealpak Incorporated" id="sandbox" class="container round box whitebg">
<h1>here is Link 2</h1>
</div>
</section>
</div>
JS:
var app = angular.module("app", ["ngRoute"]);
function nav($scope) {
$scope.templates = templates;
$scope.header = $scope.templates[0];
$scope.footer = $scope.templates[1];
$scope.mainNav = mainNav;
$scope.footNav = footNav;
}
app.config(function($routeProvider) {
$routeProvider.when('/',{
templateUrl: "templates/home.html",
controller: "AppCtrl"
}).when('/templates/web.html',{
templateUrl: "templates/web.html",
controller: "AppCtrl"
}).when('/templates/seo.html',{
templateUrl: "templates/seo.html",
controller: "AppCtrl"
}).otherwise({
template: "This doesn't exist!"
});
});
app.controller("AppCtrl", function($scope) {
$scope.webProjects = webProjects;
$scope.seoProjects = seoProjects;
//Here I set the initial value
$scope.page = 'Comfort Homes of Athens';
});
Unfortunately for you, ng-repeat creates child scopes which are siblings with each other and children of your parent controller (ng-controller="nav") while your <section> where ng-switch is on is not child scope of your ng-controller="nav", but AppCtrl.
You could try ng-click="$parent.$parent.page=item.name" just to understand scopes in angular.
<div id="web" class="wrapper">
<aside class="boxModel">
<div id="controller" class="container">
<div class="topBox bluebg subNavBar"><h1 class="white">Projects</h1></div>
<div ng-controller="nav" id="controls" class="botBox whitebg">
<span ng-repeat='item in webProjects' ng-click="$parent.$parent.page=item.name">{{item.name}}</span>
</div>
</div>
</aside><section ng-switch on="page" class="boxModel">
<div ng-switch-when="Comfort Homes of Athens" id="sandbox" class="container round box whitebg">
<h1>Here is link 1</h1>
</div>
<div ng-switch-when="Sealpak Incorporated" id="sandbox" class="container round box whitebg">
<h1>here is Link 2</h1>
</div>
</section>
I don't recommend using this solution as it's quite ugly. The solution of #link64 is better, but I think the inheritance of model is so implicit and creates a tightly-coupled code. Here I propose another solution which I hope is better by emitting an event:
<span ng-repeat='item in webProjects' ng-click="$emit('pageChange',item.name)">{{item.name}}</span>
I'm not sure if angular is able to resolve $emit('pageChange',item.name) expression in the template. If you run into any problems, you could write inside your controller:
<span ng-repeat='item in webProjects' ng-click="setPageChange(item.name)">{{item.name}}</span>
In your nav controller:
$scope.setPageChange = function (pageName) {
$scope.$emit("pageChange",pageName);
}
In your AppCtrl, listen to the event and update the page.
app.controller("AppCtrl", function($scope) {
$scope.webProjects = webProjects;
$scope.seoProjects = seoProjects;
//Here I set the initial value
$scope.page = 'Comfort Homes of Athens';
$scope.$on("pageChange", function (event, newPage){
$scope.page = newPage;
}
});
In addition to #KhanhTo's answer, I wanted to point you toward another tool to use instead of ngRoute; UI-Router. This is not the answer to your original question, but it is a better solution that avoids your issue entirely.
UI-Router enhances the page routing of ngRoute and is more centered around states. You transition to states that have templates and optional controllers. It emits its own events such as $stateChangeStart or $stateChangeSuccess. You can invoke these state transitions with the function command $state.go(stateName) or by a directive ui-sref="my.state({name: item.name})
UI-Router is a very powerful tool and I cannot go into all the details here but the documentation and community is great.
A simple rewrite of your code could look like the following.
Template for web.html
<div class="wrapper">
<aside class="boxModel">
<div id="controller" class="container">
<div class="topBox bluebg subNavBar"><h1 class="white">Projects</h1></div>
<div ng-controller="nav" id="controls" class="botBox whitebg">
<span ng-repeat='item in webProjects' ui-sref="app.web.page({name: {{item.name}})">
{{item.name}}
</span>
</div>
</div>
</aside>
<section class="boxModel">
<div ui-view class="container round box whitebg">
<!-- Page content will go here -->
</div>
</section>
</div>
JavaScript
app.config(function($stateProvider) {
$stateProvider
.state('app', {
abstract: true,
template: '<div ui-view></div>', //Basic template
controller: "AppCtrl",
}).state('app.home', {
templateUrl: "templates/home.html",
url: '/home'
}).state('app.web',{
templateUrl: "templates/web.html",
url: '/web'
}).state('app.web.page',{
templateUrl: "templates/page.web.html",
url: '/web/page/:name' //Note here the ':' means name will be a parameter in the url
}).state('app.seo',{
templateUrl: "templates/seo.html",
url: '/seo'
});
});
app.controller('AppCtrl', function($scope){
$scope.webProjects = webProjects;
$scope.seoProjects = seoProjects;
$scope.$on("$stateChangeStart", function (event, toState, toParams, fromState, fromParams){
if(newState.name == 'app.web.page'){
var pageName = newStateParams.name; //Variable name matches
$scope.linkText = fetchPageContent(pageName);
}
});
});
Template for page.web.html
<h1>{{linkText}}</h1>
With these changes you will be able to reuse the same instance of your controller. In addition to allowing your paging content to be more scalable.
Notes on $scopes
Every $scope has a parent except for the $rootScope. When you ask for an object in the view, it will look at its $scope to find the reference. If it does not have the reference, it will traverse up to its parent scope and look again. This occurs until you get to the $rootScope.
If you assign something to the $scope in the view, it will assign it to the current $scope as opposed to searching up the $scope chain for an existing property. That is why ng-click="model.page = ..." works; it looks up the $scope chaing for model and then assigns to the page property whereas ng-click="page = ..." assigns directly to the current $scope.
Notes on Controller re-use
To my knowledge, ngRoute does not support nested views. When you go to a new route, it will destroy the current view and controller as specified in the $routeProvider and then instantiate a new controller for the new view. UI-Router supports nested states (i.e. child states with child $scopes). This allows us to create a parent controller that can be re-used amongst all the child states.
I think this may be related to some misunderstanding of how scope works.
ng-repeat creates its own scope. When attempting to set page, angular creates it on the scope of the ng-repeat.
In your AppCtrl, create an object on the scope as follows:
$scope.model = {};
$scope.model.page = 'Comfort Homes of Athens';//Default value
On your ng-click, refer to model.page instead of just page. Angular will then traverse up the scope to find model.page instead of just create a property on the local scope of the ng-repeat.
<span ng-repeat='item in webProjects' ng-click="model.page='{{item.name}}'">{{item.name}}</span>
Also, your AppCtrl is going to be recreated every time you change pages. You should probably use a service to persist the state between page changes

Why this doesnt fire ng-repeat?

My HTML
ng-app and ng-controller are specified in markup earlier
<div class="statusEntry" ng-repeat="statusInput in statusInputs">
<span class="userName"> a </span>
<span class="statusMsg"> b </span>
</div>
Controller
app.controller('globalCtrl', ['$scope', function($scope) {
//someWork
pubnub.subscribe({
channel: "statuses",
callback:
function (data) {
splitData = data.split(';');
prepData = '{'+splitData[0]+','+splitData[1]+'}';
statusInputs.push(prepData);
}
});
When I push the data no new object appears.
Your Controller has no name.
You haven't declare an ng-app or ng-controller in your markup anywhere.
data should be named $scope so Angular can appropriately inject the dependency.
It doesn't look like either statusInputs or your function are part of the $scope therefore there's no way for your view to access them.
Replace
statusInputs.push(prepData);
with
$scope.statusInputs.push(prepData);
This is how you enable your views to access them.

Resources