I have angular working in one of my ASP.NET MVC applications. I am using two html templates with Angular Routing. One is a list of current Favorites that comes from the database and is serialized into json from my Web API and used by angular to list those items from the database.
The second html template is a form that will be used to add new favorites. When the overall page that includes my angular code loads, it has a cookie named currentSearch which is holding the value of whatever the last search parameters executed by the user.
I would like to inject this value into my angular html template (newFavoriteView.html) for the value of a hidden input named and id'd searchString.
I have tried using jQuery, but had problems, plus I would much rather do this inside of angular and somehow pass the value along to my template or do the work inside the view(template). However, I know the latter would be bad form. Below is the code I think is important for one to see in order to understand what I am doing.
Index.cshtml (My ASP.NET VIEW)
#{
ViewBag.Title = "Render Search";
ViewBag.InitModule = "renderIndex";
}
<div class="medium-12 column">
<div data-ng-view=""></div>
</div>
#section ngScripts {
<script src="~/ng-modules/render-index.js"></script>
}
Setting the cookie in the MVC Controller
private void LastSearch()
{
string lastSearch = null;
if (Request.Url != null)
{
var currentSearch = Request.Url.LocalPath + "?" +
Request.QueryString;
if (Request.Cookies["currentSearch"] != null)
{
lastSearch = Request.Cookies["currentSearch"].Value;
ViewBag.LastSearch = lastSearch;
}
if (lastSearch != currentSearch)
{
var current = new HttpCookie("currentSearch", currentSearch){
Expires = DateTime.Now.AddDays(1) };
Response.Cookies.Set(current);
var previous = new HttpCookie("lastSearch", lastSearch) {
Expires = DateTime.Now.AddDays(1) };
Response.Cookies.Set(previous);
}
}
}
render-index.js
angular
.module("renderIndex", ["ngRoute"])
.config(config)
.controller("favoritesController", favoritesController)
.controller("newFavoriteController", newFavoriteController);
function config($routeProvider) {
$routeProvider
.when("/", {
templateUrl: "/ng-templates/favoritesView.html",
controller: "favoritesController",
controllerAs: "vm"
})
.when("/newsearch", {
templateUrl: "/ng-templates/newFavoriteView.html",
controller: "newFavoriteController",
controllerAs: "vm"
})
.otherwise({ redirectTo: "/" });
};
function favoritesController($http) {
var vm = this;
vm.searches = [];
vm.isBusy = true;
$http.get("/api/favorites")
.success(function (result) {
vm.searches = result;
})
.error(function () {
alert('error/failed');
})
.then(function () {
vm.isBusy = false;
});
};
function newFavoriteController($http, $window) {
var vm = this;
vm.newFavorite = {};
vm.save = function () {
$http.post("/api/favorites", vm.newFavorite)
.success(function (result) {
var newFavorite = result.data;
//TODO: merge with existing topics
alert("Thanks for your post");
})
.error(function () {
alert("Your broken, go fix yourself!");
})
.then(function () {
$window.location = "#/";
});
};
};
favoritesView.html
<div class="container">
<h3>New Favorite</h3>
<form name="newFavoriteForm" ng-submit="vm.save()">
<fieldset>
<div class="row">
<div class="medium-12 column">
<input name="searchString" id="searchString" type="hidden"
ng-model="vm.newFavorite.searchString"/>
<label for="title">Name</label><br />
<input name="title" type="text"
ng-model="vm.newFavorite.name"/>
<label for="title">Description</label><br />
<textarea name="body" rows="5" cols="30"
ng-model="vm.newTopic.description"></textarea>
</div>
<div class="medium-12 column">
<input type="submit" class="tiny button radius" value="Save"/> |
Cancel
</div>
</div>
</fieldset>
</form>
</div>
My current attepts have been using jQuery at the end of the page after Angular has loaded and grab the cookie and stuff it in the hidden value. But I was not able to get that to work. I also thought about setting the value as a javascript variable (in my c# page) and then using that variable in angular some how. AM I going about this the right way?
Or should it be handled in the angular controller?...
I'm new to angular and the Angular Scope and a bit of ignorance are getting in the way. If any other info is needed I can make it available, thanks if you can help or guide me in the right direction.
You can do it by reading the cookie value using JavaScript, set it as a property of the $scope object and access it on the template.
//Inside your controllers
function favoritesController($http, $scope) {
//Get the cookie value using Js
var cookie = document.cookie; //the value is returned as a semi-colon separated key-value string, so split the string and get the important value
//Say the cookie string returned is 'currentSearch=AngularJS'
//Split the string and extract the cookie value
cookie = cookie.split("="); //I am assuming there's only one cookie set
//make the cookie available on $scope, can be accessed in templates now
$scope.searchString = cookie[1];
}
EXTRA NOTE
In AngularJS, the scope is the glue between your application's controllers and your view. The controller and the view share this scope object. The scope is like the model of your application. Since both the controller and the view share the same scope object, it can be used to communicate between the two. The scope can contain the data and the functions that will run in the view. Take note that every controller has its own scope. The $scope object must be injected into the controller if you want to access it.
For example:
//inject $http and $scope so you can use them in the controller
function favoritesController($http, $scope) {
Whatever is stored on the scope can be accessed on the view and the value of a scope property can also be set from the view. The scope object is important for Angular's two-way data binding.
Sorry if I'm misunderstanding or over-simplifying, but...assuming JavaScript can read this cookie-value, you could just have your controller read it and assign it to a $scope variable?
If JavaScript can't read the value, then you could have your ASP write the value to a JavaScript inline script tag. This feels yuckier though.
Update to show controller-as example.
Assuming your HTML looked something vaguely like this:
<div ng-controller="MyController as controller">
<!-- other HTML goes here -->
<input name="searchString" id="searchString" type="hidden" ng-model="controller.data.currentSearch"/>
Then your controller may look something like this:
app.controller('MyController', function ($scope, $cookies) {
$scope.data = {
currentSearch: $cookies.currentSearch
};
// Note that the model is nested in a 'data' object to ensure that
// any ngIf (or similar) directives in your HTML pass by reference
// instead of value (so 2-way binding works).
});
Related
I've been teaching myself how to use the as statement of Angularjs's controller, but struggling to make controllers communicate with others, using the as syntax.
<script type="text/javascript">
angular.module('angularApp', [])
.factory('MessageService', function(){
var message = {
addedItem: "initialMessge"
};
return {
returnMessage: message//This is supposed to be the "var message" defined above
};
})
.controller('DiaplayingProductController', function(MessageService){
var instance = this;
this.data = {
message: MessageService.returnMessage.addedItem
};
})
.controller('ProductController', function($scope, $http, MessageService) {
var instance = this;
this.data = {
message: MessageService.message,
//There are other stuff here
};
this.addItem = function(productName) {
$http({
//other tasks
}).then(function addSucces(response) {
instance.data.message.addedItem = productName;
});
};
});
<span ng-controller="DiaplayingProductController as dpc" ng-bind="dpc.data.message"></span>
<div ng-controller="ProductController as pc">
#foreach ($products as $index => $product)
<div class="product">
<button ng-click="pc.addItem({{$product->name}})>
Add it to Cart
</button>
</div>
#endforeach
</div>
I use Laravel, so {{$product->name}} and #foreach are Laravel's expression.
In a nutshell,
There are one <span> and multiple <button>s, based on the result of #foreach (Again, I use Laravel, so this is basically the same thing as php's foreach)
When one of the <button> is pressed, the content of <span> is supposed to be updated.
The event is triggered in ProductController, which is supposed to update message of DiaplayingProductController, via MessageService.
The message is not going to be sent to the span tag.
This question may be silly. However, there are not many information resources out there which deal with this as statements, so I'd like to ask some advice here. Thank you in advance!
What's this #foreach?
There's a coma in your attributes. Shouldn't be there.
The expression in your ng-click has a missing parenthesis. Also, it should be an expression, therefore the {{}} have nothing to do here.
The data object are not shared between the controllers. You should:
use directives and pass the data using attributes ('=').
set the data in the $scope, which is not as good a solution
use a service as an intermediary (each controller can set/get the value
from that service)
I'm trying to take the first example from the angular.js homepage and adding in cookie support.
This is what I have so far: https://jsfiddle.net/y7dxa6n8/8/
It is:
<div ng-app="myApp">
<div ng-controller="MyController as mc">
<label>Name:</label>
<input type="text" ng-model="mc.user" placeholder="Enter a name here">
<hr>
<h1>Hello {{mc.user}}!</h1>
</div>
</div>
var myApp = angular.module('myApp', ['ngCookies']);
myApp.controller('MyController', [function($cookies) {
this.getCookieValue = function () {
$cookies.put('user', this.user);
return $cookies.get('user');
}
this.user = this.getCookieValue();
}]);
But it's not working, ive been trying to learn angular.
Thanks
I'd suggest you create a service as such in the app module:
app.service('shareDataService', ['$cookieStore', function ($cookieStore) {
var _setAppData = function (key, data) { //userId, userName) {
$cookieStore.put(key, data);
};
var _getAppData = function (key) {
var appData = $cookieStore.get(key);
return appData;
};
return {
setAppData: _setAppData,
getAppData: _getAppData
};
}]);
Inject the shareDataService in the controller to set and get cookie value
as:
//set
var userData = { 'userId': $scope.userId, 'userName': $scope.userName };
shareDataService.setAppData('userData', userData);
//get
var sharedUserData = shareDataService.getAppData('userData');
$scope.userId = sharedUserData.userId;
$scope.userName = sharedUserData.userName;
Working Fiddle: https://jsfiddle.net/y7dxa6n8/10/
I have used the cookie service between two controllers. Fill out the text box to see how it gets utilized.
ok, examined your code once again, and here is your answer
https://jsfiddle.net/wz3kgak3/
problem - wrong syntax: notice definition of controller, not using [] as second parameter
If you are using [] in controller, you must use it this way:
myApp.controller('MyController', ['$cookies', function($cookies) {
....
}]);
this "long" format is javascript uglyfier safe, when param $cookies will become a or b or so, and will be inaccessible as $cookies, so you are telling that controller: "first parameter in my function is cookies
problem: you are using angular 1.3.x, there is no method PUT or GET in $cookies, that methods are avalaible only in angular 1.4+, so you need to use it old way: $cookies.user = 'something'; and getter: var something = $cookies.user;
problem - you are not storing that cookie value, model is updated, but cookie is not automatically binded, so use $watch for watching changes in user and store it:
$watch('user', function(newValue) {
$cookies.user = newValues;
});
or do it via some event (click, submit or i dont know where)
EDIT: full working example with $scope
https://jsfiddle.net/mwcxv820/
I am trying to populate the ng-model from a value, but don't want it to update until it is saved. To do this I have the following code;
Controller;
var SettingsController = function (roleService) {
var ctrl = this;
ctrl.rename = function(name) {
ctrl.account.name = name;
};
roleService.role.settings().then(function (result) {
ctrl.account = result.data.account;
}, function (result) {
console.log(result);
});
};
A simple controller, it gets the current settings from a service and sets it to the ctrl.
When the rename(name) is called I update the name of the account (for now it just updates the scope but soon it will update also the back-end)
To rename your account I have the following piece of code
<input type="text" data-ng-model="account.name" />
<button type="button" data-ng-click="ctrl.rename(account.name)">
Rename
</button>
I use controler as so ctrl == SettingsController
Now I use data-ng-model="account.name" to update the name. This is done so I only update the name when the button is called. The only issue is how do I get the ctrl.account.name value to the input, without bubbling it up.
I could add $scope to my controller with $scope.account but that seems overkill to me. Isn't there a way to copy/populate a ng-model from an other value or some kind?
To get to the controller I use the angular routerProvider;
$routeProvider
.when('/settings/', {
templateUrl: '/user/template/settings/',
controller: 'SettingsController',
controllerAs: 'ctrl'
});
In my angular app I have a product view/controller that has the common questions for product (SKU, Name, Description etc). I also have a dropdown field named ProductType that I will use to load a dynamic view/js/controller for questions that vary based on that product type. When the user saves the product I'll have a property on the base product model named ProductTypeConfig (as well as ProductType) that contains a json representation of the product type configuration and I want to pass all that to the server controller for persistence.
Has anyone seen this done before in Angular? Comments or clues as to how to go about this? I don't want to load all of js for every product type controller etc. ahead of time as this will potentially be plugable by the client as new product types are rolled out.
EDIT:
Ok, so I created a plunk to demonstrate what I'm trying to accomplish. I have the dynamic piece working well I think. At this point I just need to figure out how to grab the dynamic data in the saveProduct() function in the ProductController. When save is clicked, I need to somehow call a method on either the TypeAController or the TypeBController depending on which one is loaded. I was thinking that I could probably create a service that all the controllers would depend on and have it do the work. Is this something that is possible?
The plunk is located here http://plnkr.co/edit/6kQYKU
This is the main controller:
(function() {
var app = angular.module('ProductApp', ['ngRoute']);
app.config(function($httpProvider, $routeProvider) {
$routeProvider.
when('/', {
templateUrl: 'product.html',
controller: 'ProductController'
}).
otherwise({
redirectTo: '/'
});
});
var ProductController = function($scope, $log, $routeParams, $location) {
var saveProduct = function() {
// how to get data from either TypeAController or TypeBController here when saved from ProductController
$log.log('Product saved')
$location.path('/');
};
$scope.saveProduct = saveProduct;
// values
$scope.ProductId = 1001;
$scope.Name = 'Product 1001';
$scope.Type = 'typea';
};
app.controller("ProductController", ProductController);
}());
And this is one of the dynamic views with it's controller:
<div ng-controller='TypeAController'>
<h1>Type A Settings</h1>
<fieldset>
<div class="dnnFormItem">
<label>Width:</label>
<br />
<input type="text" name="width" ng-model="Width" />
<br />
<label>Height:</label>
<br />
<input type="text" name="height" ng-model="Height" />
<br />
</div>
</fieldset>
</div>
<script>
console.log('TypeA is loaded');
var TypeAController = function($scope, $log) {
};
angular.module('ProductApp').controller("TypeAController", TypeAController);
</script>
I ended up doing a simple factory to share an object between controllers as demonstrated in this plunk, http://plnkr.co/edit/6kQYKU. This also demonstrates loading a dynamic view based on a field in the parent view.
var stateService = function() {
'use strict';
var state = {};
return {
state: state,
};
};
app.factory('StateService', stateService);
In my regular Javascript I append data to HTML ONLY if there's data from the server, otherwise I just show a simple div saying there's nothing. How can I implement this in AngularJS?
Example:
if (AJAXresult)
$element.append(JSONdata); //JSONdata will contain a list of customer data
else
$element.append('<div>No results</div>');
How can I achieve this in Angular?
The simplest way would be to control for the no data state in your returned view.
<div>
<div ng-if="!hasCustomers">
No Customers Available
</div>
<div ng-if="hasCustomers">
<!-- show some stuff -->
</div>
</div>
Then in your controller you can easily initialize this when you load your data:
angular.module('myApp').controller('MyController', function($scope, myDataService){
$scope.hasCustomers = false;
myDataService.getCustomers()
.then(function(value){
$scope.customers = value.data;
$scope.hasCustomers = customers && customers.length;
});
});
If you want to make sure the data is loaded before your view is ever instantiated, then you can also use the resolve property on your $route
$routeProvider.when('/someRoute/',{
templateUrl: '/sometemplate.html',
controller: 'MyController',
controllerAs: 'ctrl', // <-- Highly recommend you do this
resolve: {
customerData: function(myDataService){
return myDataService.getCustomers();
}
}
});
resolve is basically a hash of functions that return a promise, and can be dependency injected just like everything else. The controller and view will not be loaded until all the resolve promises have been fulfilled.
It will be available in your controller by the same property name you gave it:
angular.module('myApp')
.controller('MyController', function($scope, customerData){
$scope.customers = customerData;
$scope.hasCustomers = customerData && customerData.length;
});